[PATCH] test: test folder renames

2014-02-23 Thread Jani Nikula
On Sun, 23 Feb 2014, Mark Walters  wrote:
> I was experimenting with letting notmuch new take an argument to tell it
> to scan only a particular directory (and sub-directories) for new
> messages. I came across the following strange behaviour which is also
> present in master (with a fresh database)
>
> I have a bunch of maildirs in /home/mail: so folders .mail.foo/
> .mail.bar/ each of which has cur/new/tmp and all the messages are in
> cur.
>
> If I do mv .mail.foo .mail.bar/ and run notmuch new I get the expected
> lots of renames (900 or so in the case I was trying). But if I then do
> mv .mail.bar/.mail.foo . and run notmuch new almost all the messages get
> removed (but 30 renames do get detected). If I then do touch .mail.foo/*
> the messages get found again
>
> I am guessing the 30 renames might be because those 30 have duplicates
> somewhere else.
>
> But the other behaviour has me puzzled.

This test reproduces the problem for me, but it's not
deterministic. With the loop, I get roughly one fail per test run:

 FAIL   Rename folder back
 --- T051-new-renames.27.expected   2014-02-23 21:37:10.121774241 +
 +++ T051-new-renames.27.output 2014-02-23 21:37:10.121774241 +
 @@ -1 +1 @@
 -No new mail. Detected 10 file renames.
 +No new mail. Removed 10 messages.
 FAIL   Files remain the same
 --- T051-new-renames.28.expected   2014-02-23 21:37:10.133774652 +
 +++ T051-new-renames.28.output 2014-02-23 21:37:10.133774652 +
 @@ -1,13 +1,3 @@
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-121
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-122
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-123
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-124
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-125
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-126
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-127
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-128
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-129
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-130
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-131
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-132
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-133

I'm as puzzled as you are.

BR,
Jani.
---
 test/T051-new-renames.sh | 40 
 1 file changed, 40 insertions(+)
 create mode 100755 test/T051-new-renames.sh

diff --git a/test/T051-new-renames.sh b/test/T051-new-renames.sh
new file mode 100755
index ..febe006f5888
--- /dev/null
+++ b/test/T051-new-renames.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" with directory renames'
+. ./test-lib.sh
+
+for loop in `seq 10`; do
+
+rm -rf ${MAIL_DIR}
+
+for i in `seq 10`; do
+generate_message '[dir]=foo' '[subject]="Message foo $i"'
+done
+
+for i in `seq 10`; do
+generate_message '[dir]=bar' '[subject]="Message bar $i"'
+done
+
+test_begin_subtest "Index the messages, round $loop"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 20 new messages to the database."
+
+all_files=$(notmuch search --output=files \*)
+count_foo=$(notmuch count folder:foo)
+
+test_begin_subtest "Rename folder"
+mv ${MAIL_DIR}/foo ${MAIL_DIR}/baz
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Rename folder back"
+mv ${MAIL_DIR}/baz ${MAIL_DIR}/foo
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Files remain the same"
+output=$(notmuch search --output=files \*)
+test_expect_equal "$output" "$all_files"
+
+done
+
+test_done
-- 
1.8.5.3



[PATCH v3 3/3] emacs: defun notmuch-hello-versions and bind 'v' in hello mode to it

2014-02-23 Thread Tomi Ollila
If notmuch cli & notmuch emacs MUA versions differ, print also the
emacs MUA version string (along with the cli version) to the
minibuffer.
---
 emacs/notmuch-hello.el | 15 +--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 7b3d76b..f5e7268 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -513,6 +513,18 @@ Such a list can be computed with 
`notmuch-hello-query-counts'."
   (remove-hook 'window-configuration-change-hook
   #'notmuch-hello-window-configuration-change

+;; the following variable is defined as being defconst in notmuch.el
+(defvar notmuch-emacs-version)
+
+(defun notmuch-hello-versions ()
+  "Display the notmuch version(s)"
+  (interactive)
+  (let ((notmuch-cli-version (notmuch-version)))
+(message "notmuch version %s"
+(if (string= notmuch-emacs-version notmuch-cli-version)
+notmuch-cli-version
+  (concat notmuch-cli-version
+  " (emacs mua version " notmuch-emacs-version ")")

 (defvar notmuch-hello-mode-map
   (let ((map (if (fboundp 'make-composed-keymap)
@@ -523,8 +535,7 @@ Such a list can be computed with 
`notmuch-hello-query-counts'."
   ;; it's unlikely to change.
   (copy-keymap widget-keymap
 (set-keymap-parent map notmuch-common-keymap)
-(define-key map "v" (lambda () "Display the notmuch version" (interactive)
- (message "notmuch version %s" (notmuch-version
+(define-key map "v" 'notmuch-hello-versions)
 (define-key map (kbd "") 'widget-backward)
 map)
   "Keymap for \"notmuch hello\" buffers.")
-- 
1.8.0



[PATCH v3 2/3] emacs: defconst notmuch-emacs-version to a value during byte compilation

2014-02-23 Thread Tomi Ollila
The notmuch cli program and emacs lisp versions may differ. For now
we can help users with their emacs client problems better if we can
ask what version of emacs MUA they are running. In the future we can
put the emacs MUA version to User-Agent: string in outgoing mail.
---
 emacs/Makefile.local | 6 +-
 emacs/notmuch.el | 7 +++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/emacs/Makefile.local b/emacs/Makefile.local
index 6a39b32..9e3fb7a 100644
--- a/emacs/Makefile.local
+++ b/emacs/Makefile.local
@@ -24,6 +24,8 @@ emacs_images := \

 emacs_bytecode = $(emacs_sources:.el=.elc)

+$(dir)/notmuch.elc: version.stamp
+
 # Because of defmacro's and defsubst's, we have to account for load
 # dependencies between Elisp files when byte compiling.  Otherwise,
 # the byte compiler may load an old .elc file when processing a
@@ -49,7 +51,9 @@ CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x

 ifeq ($(HAVE_EMACS),1)
 %.elc: %.el $(global_deps)
-   $(call quiet,EMACS) --directory emacs -batch -f batch-byte-compile $<
+   $(call quiet,EMACS) --directory emacs \
+   --eval "(setq notmuch--version \"$(VERSION)\")" \
+   -batch -f batch-byte-compile $<
 endif

 ifeq ($(WITH_EMACS),1)
diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 0471750..1b15054 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -81,6 +81,13 @@ To enter a line break in customize, press \\[quoted-insert] 
C-j."
   :type '(alist :key-type (string) :value-type (string))
   :group 'notmuch-search)

+;; defconst notmuch-version to a value during build-time byte compilation...
+(defconst notmuch-emacs-version
+  (eval-when-compile (if (boundp 'notmuch--version)
+notmuch--version
+  "unknown"))
+  "Version string of this version of Notmuch Emacs MUA.")
+
 (defvar notmuch-query-history nil
   "Variable to store minibuffer history for notmuch queries")

-- 
1.8.0



[PATCH v3 1/3] build: write version.stamp file containing $(VERSION) string

2014-02-23 Thread Tomi Ollila
This version file will be as prerequisite to the target files
that use the version info for some purpose, like printing
it for the user to examine. The contents of the version.stamp
file is seldom read by the build system itself as the $(VERSION)
variable has the same information.

Thanks to Trevor, David and Mark for their contributions.
---
 .gitignore |  1 +
 Makefile.local | 14 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index ef4f074..1fb3a71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 .first-build-message
 Makefile.config
+version.stamp
 TAGS
 tags
 *cscope*
diff --git a/Makefile.local b/Makefile.local
index 174506c..3a56c06 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -22,6 +22,11 @@ VERSION:=$(shell cat ${srcdir}/version)
 ifeq ($(filter release release-message pre-release 
update-versions,$(MAKECMDGOALS)),)
 ifeq ($(IS_GIT),yes)
 VERSION:=$(shell git describe --match '[0-9.]*'|sed -e s/_/~/ -e s/-/+/ -e 
s/-/~/)
+# Write the file 'version.stamp' in case its contents differ from $(VERSION)
+FILE_VERSION:=$(shell test -f version.stamp && read vs < version.stamp || vs=; 
echo $$vs)
+ifneq ($(FILE_VERSION),$(VERSION))
+   $(shell echo "$(VERSION)" > version.stamp)
+endif
 endif
 endif

@@ -69,6 +74,11 @@ ifeq ($(shell cat .first-build-message 2>/dev/null),)
 endif
 endif

+# Depend (also) on the file 'version'. In case of ifeq ($(IS_GIT),yes)
+# this file may already have been updated.
+version.stamp: version
+   echo $(VERSION) > $@
+
 $(TAR_FILE):
if git tag -v $(VERSION) >/dev/null 2>&1; then \
ref=$(VERSION); \
@@ -280,6 +290,8 @@ notmuch_client_srcs =   \

 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)

+notmuch.o: version.stamp
+
 notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a 
parse-time-string/libparse-time-string.a
$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@

@@ -318,7 +330,7 @@ install-desktop:
desktop-file-install --mode 0644 --dir "$(DESTDIR)$(desktop_dir)" 
notmuch.desktop

 SRCS  := $(SRCS) $(notmuch_client_srcs)
-CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) notmuch.elc
+CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) 
version.stamp

 DISTCLEAN := $(DISTCLEAN) .first-build-message Makefile.config

-- 
1.8.0



v3 of version change detection and notmuch emacs/hello version(s)

2014-02-23 Thread Tomi Ollila
This is v3 of

id:1393024837-30394-1-git-send-email-tomi.ollila at iki.fi

The only change is that version.stamp was added to root .gitignore file.



[DRAFT PATCH] notmuch new: do not ignore '.notmuch' in non-toplevel directories

2014-02-23 Thread Tomi Ollila
So that users may have email in subdir/.notmuch directories.
---

Compiles, current tests pass. might ignore database_path/.notmuch and
might descent into database_path/.../.notmuch :D

Tomi


 notmuch-new.c | 18 ++
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/notmuch-new.c b/notmuch-new.c
index 8529fdd..b17bd75 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -344,7 +344,8 @@ add_file (notmuch_database_t *notmuch, const char *filename,
 static notmuch_status_t
 add_files (notmuch_database_t *notmuch,
   const char *path,
-  add_files_state_t *state)
+  add_files_state_t *state,
+  int dirlevel)
 {
 DIR *dir = NULL;
 struct dirent *entry = NULL;
@@ -469,11 +470,11 @@ add_files (notmuch_database_t *notmuch,
if (strcmp (entry->d_name, ".") == 0 ||
strcmp (entry->d_name, "..") == 0 ||
(is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
-   strcmp (entry->d_name, ".notmuch") == 0)
+   (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0))
continue;

next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
-   status = add_files (notmuch, next, state);
+   status = add_files (notmuch, next, state, dirlevel + 1);
if (status) {
ret = status;
goto DONE;
@@ -702,7 +703,8 @@ stop_progress_printing_timer (void)
  * initialized to zero by the top-level caller before calling
  * count_files). */
 static void
-count_files (const char *path, int *count, add_files_state_t *state)
+count_files (const char *path, int *count, add_files_state_t *state,
+int dirlevel)
 {
 struct dirent *entry = NULL;
 char *next;
@@ -725,7 +727,7 @@ count_files (const char *path, int *count, 
add_files_state_t *state)
 */
if (strcmp (entry->d_name, ".") == 0 ||
strcmp (entry->d_name, "..") == 0 ||
-   strcmp (entry->d_name, ".notmuch") == 0 ||
+   (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0) ||
_entry_in_ignore_list (entry->d_name, state))
{
if (state->debug && _entry_in_ignore_list (entry->d_name, state))
@@ -750,7 +752,7 @@ count_files (const char *path, int *count, 
add_files_state_t *state)
fflush (stdout);
}
} else if (entry_type == S_IFDIR) {
-   count_files (next, count, state);
+   count_files (next, count, state, dirlevel + 1);
}

free (next);
@@ -962,7 +964,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
int count;

count = 0;
-   count_files (db_path, &count, &add_files_state);
+   count_files (db_path, &count, &add_files_state, 0);
if (interrupted)
return EXIT_FAILURE;

@@ -1021,7 +1023,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
timer_is_active = TRUE;
 }

-ret = add_files (notmuch, db_path, &add_files_state);
+ret = add_files (notmuch, db_path, &add_files_state, 0);
 if (ret)
goto DONE;

-- 
1.8.4.2



[PATCH 0/3] check new.tags for invalid tags

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, Jani Nikula  wrote:

> On Sun, 23 Feb 2014, Rob Browning  wrote:
>> In the [new] section, "tags=;" will cause notmuch to create empty tags
>> that are fairly hard to remove from the command line.
>
> Clearly broken. This series fixes the issue at the cli
> level. (Forbidding empty tags at the lib level is slightly more
> complicated, as we would still have to ensure old dump files can be
> restored.)
>
>> After some help on #bup, here's what I came up with to remove them,
>> though it assumes that the empty tag "+ " will always be first in dump's
>> output:
>>
>>   notmuch dump --format=batch-tag 'tag:""' | perl -pe 's/^\+ //' \
>> | notmuch restore --format=batch-tag
>>
>> And note that you have to use restore, "notmuch tag --batch" doesn't
>> appear to accept "- " as a tag, even though dump will produce "+ ".
>
> I didn't check this further, but the regular, non-batch notmuch tag
> should still work for removal of empty tags.

LGTM.

$ notmuch tag + -- 
id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.jani at nikula.org
Error: empty tag forbiddenzsh: exit 1 notmuch tag + --

$ notmuch restore --accumulate
+ id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.jani at nikula.org
 -- notmuch search shows space after ( to inform
there is empty tag.

$ notmuch tag - -- 
id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.jani at nikula.org


notmuch search no longer shows space after (

Also, SomeBody(tm) should add '\n' to the fprintf() in tag-util.c:175 (line
number after applying these patches).

Tomi

>
> BR,
> Jani.
>
> Jani Nikula (3):
>   cli: export function for illegal tag checking
>   cli: make sure notmuch new and insert don't add invalid tags
>   test: add tests for invalid new.tags
>
>  notmuch-insert.c|  9 +
>  notmuch-new.c   | 14 +-
>  tag-util.c  |  9 +
>  tag-util.h  | 12 
>  test/T050-new.sh| 17 +
>  test/T070-insert.sh | 19 +++
>  6 files changed, 71 insertions(+), 9 deletions(-)
>
> -- 
> 1.8.5.3
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


[RFC Patch v3 2/3] doc: add target rst2man to build man pages using rst2man

2014-02-23 Thread David Bremner
Tomi Ollila  writes:

> Anyway, I tried to build manual pages using 'make man' and got this:
>
>   $ make man
>   sphinx-build -b man -d doc/_build/doctrees -q -c doc doc doc/_build/man
>   Sphinx error:
>   Builder name man not registered

It seems you need sphinx version 1.0, from July 2010 for the man
builder.

> No fallback to use rst2man...

I don't really plan on an automatic fallback. There is currently a
separate target rst2man.  I'm open to better names for the targets.

> Comments regarding prerst2man.py inline below:

Yeah, I'm sure there's lots that could be improved with that script. But
I'll wait until I see if the general approach is acceptable.

d


[RFC Patch v3 2/3] doc: add target rst2man to build man pages using rst2man

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, David Bremner  wrote:

> Many people have docutils installed, but not sphinx. Allow these
> people to build the man pages.

+1 from me to start having manuals in reStructuredText format and
then converting these to the target formats. Some comments on
the patch series below:

Building any docs using this is not yet activated ?

Anyway, I tried to build manual pages using 'make man' and got this:

  $ make man
  sphinx-build -b man -d doc/_build/doctrees -q -c doc doc doc/_build/man
  Making output directory...

  Sphinx error:
  Builder name man not registered

No fallback to use rst2man...


Comments regarding prerst2man.py inline below:

Tomi

> ---

// stuff deleted //

> diff --git a/doc/rst2man/prerst2man.py b/doc/rst2man/prerst2man.py
> new file mode 100644
> index 000..797dd20
> --- /dev/null
> +++ b/doc/rst2man/prerst2man.py
> @@ -0,0 +1,53 @@
> +from sys import argv
> +from datetime import date
> +import re
> +
> +sourcedir=argv[1]
> +outdir=argv[2]

Style! run pep8 prerst2html.py and fix the issues it prints
to the screen, like ' = ' above and remove trailing semicolon
below... (and also pep8(1) doc/conf.py.

> +
> +execfile(sourcedir+"/conf.py");
> +
> +
> +
> +
> +def header(file,startdocname, command, description, authors, section):
> +file.write("""
> +{:s}
> +{:s}
> +{:s}
> +
> +:Date:   {:s}
> +:Version: {:s}
> +:Manual section: {:d}
> +:Manual group: {:s}

For python < 2.7 these needs to be {0:s}, {1:s}, {2:s}.. {5:d}...

> +
> +""".format(
> +'-' * len(description),
> +description,
> +'-' * len(description),
> +date.today().isoformat(),release,section,project))

Replace date.today.isoformat() with date determined from other
sources (NEWS file?)

> +
> +blankre = re.compile("^\s*$")
> +for page in man_pages:
> +outfile = open(outdir+"/"+page[0]+'.rst','w')
> +infile = open(sourcedir+"/"+page[0]+".rst",'r')

In addition to formatting above, use either ".rst" or '.rst'
(and perhaps other quotations in these 2 lines) for consistency.

> +
> +
> +# this is a crude hack. We look for the first blank line, and
> +# insert the rst2man header there.
> +#
> +# XXX consider really parsing input
> +
> +count=0
> +lines = infile.readlines()
> +for line in lines:
> +outfile.write(line);
> +if (blankre.match(line)):
> +break
> +count = count + 1
> +
> +del lines[0:count+1]

pep8 will in the lime above (as it is not lines[0:count + 1])
I might not have complained but... :D

> +
> +header(outfile,*page)
> +
> +outfile.write("".join(lines))
> -- 
> 1.8.5.3


[PATCH 3/3] test: add tests for invalid new.tags

2014-02-23 Thread Jani Nikula
Similar tests for both notmuch new and insert.
---
 test/T050-new.sh| 17 +
 test/T070-insert.sh | 19 +++
 2 files changed, 36 insertions(+)

diff --git a/test/T050-new.sh b/test/T050-new.sh
index b7668ff0c4bc..ad46ee6d51b6 100755
--- a/test/T050-new.sh
+++ b/test/T050-new.sh
@@ -263,4 +263,21 @@ notmuch search --format=text0 --output=files --offset=1 
--limit=1 '*' | xargs -0
 output=$(NOTMUCH_NEW --quiet)
 test_expect_equal "$output" ""

+OLDCONFIG=$(notmuch config get new.tags)
+
+test_begin_subtest "Empty tags in new.tags are forbidden"
+notmuch config set new.tags "foo;;bar"
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden"
+
+test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
+notmuch config set new.tags "-foo;bar"
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "Error: tag '-foo' in new.tags: tag starting with 
'-' forbidden"
+
+test_expect_code 1 "Invalid tags set exit code" \
+"NOTMUCH_NEW 2>&1"
+
+notmuch config set new.tags $OLDCONFIG
+
 test_done
diff --git a/test/T070-insert.sh b/test/T070-insert.sh
index e8dc4c099ed1..b77c5e13c87f 100755
--- a/test/T070-insert.sh
+++ b/test/T070-insert.sh
@@ -164,4 +164,23 @@ gen_insert_msg
 test_expect_code 1 "Insert message, create invalid subfolder" \
 "notmuch insert --folder=../G --create-folder $gen_msg_filename"

+OLDCONFIG=$(notmuch config get new.tags)
+
+test_begin_subtest "Empty tags in new.tags are forbidden"
+notmuch config set new.tags "foo;;bar"
+gen_insert_msg
+output=$(notmuch insert $gen_msg_filename 2>&1)
+test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden"
+
+test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
+notmuch config set new.tags "-foo;bar"
+gen_insert_msg
+output=$(notmuch insert $gen_msg_filename 2>&1)
+test_expect_equal "$output" "Error: tag '-foo' in new.tags: tag starting with 
'-' forbidden"
+
+test_expect_code 1 "Invalid tags set exit code" \
+"notmuch insert $gen_msg_filename 2>&1"
+
+notmuch config set new.tags $OLDCONFIG
+
 test_done
-- 
1.8.5.3



[PATCH 2/3] cli: make sure notmuch new and insert don't add invalid tags

2014-02-23 Thread Jani Nikula
Check new.tags configuration values before doing anything, and bail
out on invalid values.
---
 notmuch-insert.c |  9 +
 notmuch-new.c| 14 +-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/notmuch-insert.c b/notmuch-insert.c
index cd6de88f6891..6752fc8de255 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -431,6 +431,15 @@ notmuch_insert_command (notmuch_config_t *config, int 
argc, char *argv[])
return EXIT_FAILURE;
 }
 for (i = 0; i < new_tags_length; i++) {
+   const char *error_msg;
+
+   error_msg = illegal_tag (new_tags[i], FALSE);
+   if (error_msg) {
+   fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
+new_tags[i],  error_msg);
+   return EXIT_FAILURE;
+   }
+
if (tag_op_list_append (tag_ops, new_tags[i], FALSE))
return EXIT_FAILURE;
 }
diff --git a/notmuch-new.c b/notmuch-new.c
index 8529fdd3eac7..82acf695353e 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -19,6 +19,7 @@
  */

 #include "notmuch-client.h"
+#include "tag-util.h"

 #include 

@@ -918,7 +919,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 struct sigaction action;
 _filename_node_t *f;
 int opt_index;
-int i;
+unsigned int i;
 notmuch_bool_t timer_is_active = FALSE;
 notmuch_bool_t no_hooks = FALSE;
 notmuch_bool_t quiet = FALSE, verbose = FALSE;
@@ -950,6 +951,17 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 add_files_state.synchronize_flags = 
notmuch_config_get_maildir_synchronize_flags (config);
 db_path = notmuch_config_get_database_path (config);

+for (i = 0; i < add_files_state.new_tags_length; i++) {
+   const char *error_msg;
+
+   error_msg = illegal_tag (add_files_state.new_tags[i], FALSE);
+   if (error_msg) {
+   fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
+add_files_state.new_tags[i], error_msg);
+   return EXIT_FAILURE;
+   }
+}
+
 if (!no_hooks) {
ret = notmuch_run_hook (db_path, "pre-new");
if (ret)
-- 
1.8.5.3



[PATCH 1/3] cli: export function for illegal tag checking

2014-02-23 Thread Jani Nikula
This lets us check for forbidden tags consistently across the cli. No
functional changes.
---
 tag-util.c |  9 +
 tag-util.h | 12 
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/tag-util.c b/tag-util.c
index 3bde4097372a..e2d5b795acc3 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -31,14 +31,7 @@ line_error (tag_parse_status_t status,
 return status;
 }

-/*
- * Test tags for some forbidden cases.
- *
- * return: NULL if OK,
- *explanatory message otherwise.
- */
-
-static const char *
+const char *
 illegal_tag (const char *tag, notmuch_bool_t remove)
 {

diff --git a/tag-util.h b/tag-util.h
index 4628f1630ad6..8a4074ce168f 100644
--- a/tag-util.h
+++ b/tag-util.h
@@ -90,6 +90,18 @@ parse_tag_command_line (void *ctx, int argc, char **argv,
char **query_str, tag_op_list_t *ops);

 /*
+ * Test tags for some forbidden cases.
+ *
+ * Relax the checks if 'remove' is true to allow removal of previously
+ * added forbidden tags.
+ *
+ * return: NULL if OK,
+ *explanatory message otherwise.
+ */
+const char *
+illegal_tag (const char *tag, notmuch_bool_t remove);
+
+/*
  * Create an empty list of tag operations
  *
  * ctx is passed to talloc
-- 
1.8.5.3



[PATCH 0/3] check new.tags for invalid tags

2014-02-23 Thread Jani Nikula
On Sun, 23 Feb 2014, Rob Browning  wrote:
> In the [new] section, "tags=;" will cause notmuch to create empty tags
> that are fairly hard to remove from the command line.

Clearly broken. This series fixes the issue at the cli
level. (Forbidding empty tags at the lib level is slightly more
complicated, as we would still have to ensure old dump files can be
restored.)

> After some help on #bup, here's what I came up with to remove them,
> though it assumes that the empty tag "+ " will always be first in dump's
> output:
>
>   notmuch dump --format=batch-tag 'tag:""' | perl -pe 's/^\+ //' \
> | notmuch restore --format=batch-tag
>
> And note that you have to use restore, "notmuch tag --batch" doesn't
> appear to accept "- " as a tag, even though dump will produce "+ ".

I didn't check this further, but the regular, non-batch notmuch tag
should still work for removal of empty tags.

BR,
Jani.

Jani Nikula (3):
  cli: export function for illegal tag checking
  cli: make sure notmuch new and insert don't add invalid tags
  test: add tests for invalid new.tags

 notmuch-insert.c|  9 +
 notmuch-new.c   | 14 +-
 tag-util.c  |  9 +
 tag-util.h  | 12 
 test/T050-new.sh| 17 +
 test/T070-insert.sh | 19 +++
 6 files changed, 71 insertions(+), 9 deletions(-)

-- 
1.8.5.3



Re: v3 of sphinx docs

2014-02-23 Thread Mark Walters

This looks good to me. It will obviously need a little tweaking but I
like it.

There is some error in the conversion for SYNOPSIS for dump, restore
and tag (which may also have caused the problem for compact?)

Otherwise there were a small number of oddities in indentation, and a
general reduction in boldness (using underlining instead). (This is on a
standard xterm). One case we might care a little about is the SEE ALSO
section at the end where the underlining rather than bold seems
inconsistent with the rest of my debian manpages.

Best wishes

Mark




On Sun, 23 Feb 2014, David Bremner  wrote:
> This version includes a complete conversion of the existing manpages.
> The conversion uses doclifter + custom python code + pandoc. It is
> pretty much fully automated, so I can rebase against changes to the
> nroff source if needed.  
>
> On the other hand, it is fully automated, so there are bound to be a
> few rough spots. If there are systematic things, I can fix the
> conversion scripts; one offs can just be added to the series.
>
> It does do a much better job of option indentation than the previous
> version.
>
> I think this is reaching the point where if anybody has any strong
> objections to sphinx (with a fallback to rst2man) for the docs, I'd
> like to hear them.
>
> ___
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [RFC Patch v3 2/3] doc: add target rst2man to build man pages using rst2man

2014-02-23 Thread David Bremner
Tomi Ollila  writes:

> Anyway, I tried to build manual pages using 'make man' and got this:
>
>   $ make man
>   sphinx-build -b man -d doc/_build/doctrees -q -c doc doc doc/_build/man
>   Sphinx error:
>   Builder name man not registered

It seems you need sphinx version 1.0, from July 2010 for the man
builder.

> No fallback to use rst2man...

I don't really plan on an automatic fallback. There is currently a
separate target rst2man.  I'm open to better names for the targets.

> Comments regarding prerst2man.py inline below:

Yeah, I'm sure there's lots that could be improved with that script. But
I'll wait until I see if the general approach is acceptable.

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] test: test folder renames

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, Jani Nikula  wrote:

> On Sun, 23 Feb 2014, Mark Walters  wrote:
>> I was experimenting with letting notmuch new take an argument to tell it
>> to scan only a particular directory (and sub-directories) for new
>> messages. I came across the following strange behaviour which is also
>> present in master (with a fresh database)
>>
>> I have a bunch of maildirs in /home/mail: so folders .mail.foo/
>> .mail.bar/ each of which has cur/new/tmp and all the messages are in
>> cur.
>>
>> If I do mv .mail.foo .mail.bar/ and run notmuch new I get the expected
>> lots of renames (900 or so in the case I was trying). But if I then do
>> mv .mail.bar/.mail.foo . and run notmuch new almost all the messages get
>> removed (but 30 renames do get detected). If I then do touch .mail.foo/*
>> the messages get found again
>>
>> I am guessing the 30 renames might be because those 30 have duplicates
>> somewhere else.
>>
>> But the other behaviour has me puzzled.
>
> This test reproduces the problem for me, but it's not
> deterministic. With the loop, I get roughly one fail per test run:
>
>  FAIL   Rename folder back
>  --- T051-new-renames.27.expected 2014-02-23 21:37:10.121774241 +
>  +++ T051-new-renames.27.output   2014-02-23 21:37:10.121774241 
> +
>  @@ -1 +1 @@
>  -No new mail. Detected 10 file renames.
>  +No new mail. Removed 10 messages.
>  FAIL   Files remain the same
>  --- T051-new-renames.28.expected 2014-02-23 21:37:10.133774652 +
>  +++ T051-new-renames.28.output   2014-02-23 21:37:10.133774652 
> +
>  @@ -1,13 +1,3 @@
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-121
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-122
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-123
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-124
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-125
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-126
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-127
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-128
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-129
>  -/path/to/test/tmp.T051-new-renames/mail/foo/msg-130
>   /path/to/test/tmp.T051-new-renames/mail/bar/msg-131
>   /path/to/test/tmp.T051-new-renames/mail/bar/msg-132
>   /path/to/test/tmp.T051-new-renames/mail/bar/msg-133
>
> I'm as puzzled as you are.

find /path/to/test/tmp.T051-new-renames/ might show what is the order
or 'foo' and 'bar' directories there. the order might be arbitrary
-- it surely is not alphabetical and it might not be the order
created...

The order should not matter -- and maybe it didn't and some change
made that matter... 

I'd test now but I should be ZZZ :D

>
> BR,
> Jani.

Tomi

> ---
>  test/T051-new-renames.sh | 40 
>  1 file changed, 40 insertions(+)
>  create mode 100755 test/T051-new-renames.sh
>
> diff --git a/test/T051-new-renames.sh b/test/T051-new-renames.sh
> new file mode 100755
> index ..febe006f5888
> --- /dev/null
> +++ b/test/T051-new-renames.sh
> @@ -0,0 +1,40 @@
> +#!/usr/bin/env bash
> +test_description='"notmuch new" with directory renames'
> +. ./test-lib.sh
> +
> +for loop in `seq 10`; do
> +
> +rm -rf ${MAIL_DIR}
> +
> +for i in `seq 10`; do
> +generate_message '[dir]=foo' '[subject]="Message foo $i"'
> +done
> +
> +for i in `seq 10`; do
> +generate_message '[dir]=bar' '[subject]="Message bar $i"'
> +done
> +
> +test_begin_subtest "Index the messages, round $loop"
> +output=$(NOTMUCH_NEW)
> +test_expect_equal "$output" "Added 20 new messages to the database."
> +
> +all_files=$(notmuch search --output=files \*)
> +count_foo=$(notmuch count folder:foo)
> +
> +test_begin_subtest "Rename folder"
> +mv ${MAIL_DIR}/foo ${MAIL_DIR}/baz
> +output=$(NOTMUCH_NEW)
> +test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
> +
> +test_begin_subtest "Rename folder back"
> +mv ${MAIL_DIR}/baz ${MAIL_DIR}/foo
> +output=$(NOTMUCH_NEW)
> +test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
> +
> +test_begin_subtest "Files remain the same"
> +output=$(notmuch search --output=files \*)
> +test_expect_equal "$output" "$all_files"
> +
> +done
> +
> +test_done
> -- 
> 1.8.5.3
>
> ___
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [DRAFT PATCH] notmuch new: do not ignore '.notmuch' in non-toplevel directories

2014-02-23 Thread Jani Nikula
On Sun, 23 Feb 2014, Tomi Ollila  wrote:
> So that users may have email in subdir/.notmuch directories.
> ---
>
> Compiles, current tests pass. might ignore database_path/.notmuch and
> might descent into database_path/.../.notmuch :D
>
> Tomi
>
>
>  notmuch-new.c | 18 ++
>  1 file changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-new.c b/notmuch-new.c
> index 8529fdd..b17bd75 100644
> --- a/notmuch-new.c
> +++ b/notmuch-new.c
> @@ -344,7 +344,8 @@ add_file (notmuch_database_t *notmuch, const char 
> *filename,
>  static notmuch_status_t
>  add_files (notmuch_database_t *notmuch,
>  const char *path,
> -add_files_state_t *state)
> +add_files_state_t *state,
> +int dirlevel)

I think this is ugly and makes the interface harder to use for indexing
arbitrary paths.

Instead, I suggest

diff --git a/notmuch-new.c b/notmuch-new.c
index 8529fdd3eac7..20bc33fca4bd 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -469,7 +469,8 @@ add_files (notmuch_database_t *notmuch,
if (strcmp (entry->d_name, ".") == 0 ||
strcmp (entry->d_name, "..") == 0 ||
(is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
-   strcmp (entry->d_name, ".notmuch") == 0)
+   (strcmp (entry->d_name, ".notmuch") == 0 &&
+strcmp (path, notmuch_database_get_path (notmuch)) == 0))
continue;
 
next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);

And similarly in count_files(), with the root db path passed as first
argument (as the db is not open yet).


BR,
Jani.



>  {
>  DIR *dir = NULL;
>  struct dirent *entry = NULL;
> @@ -469,11 +470,11 @@ add_files (notmuch_database_t *notmuch,
>   if (strcmp (entry->d_name, ".") == 0 ||
>   strcmp (entry->d_name, "..") == 0 ||
>   (is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
> - strcmp (entry->d_name, ".notmuch") == 0)
> + (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0))
>   continue;
>  
>   next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
> - status = add_files (notmuch, next, state);
> + status = add_files (notmuch, next, state, dirlevel + 1);
>   if (status) {
>   ret = status;
>   goto DONE;
> @@ -702,7 +703,8 @@ stop_progress_printing_timer (void)
>   * initialized to zero by the top-level caller before calling
>   * count_files). */
>  static void
> -count_files (const char *path, int *count, add_files_state_t *state)
> +count_files (const char *path, int *count, add_files_state_t *state,
> +  int dirlevel)
>  {
>  struct dirent *entry = NULL;
>  char *next;
> @@ -725,7 +727,7 @@ count_files (const char *path, int *count, 
> add_files_state_t *state)
>*/
>   if (strcmp (entry->d_name, ".") == 0 ||
>   strcmp (entry->d_name, "..") == 0 ||
> - strcmp (entry->d_name, ".notmuch") == 0 ||
> + (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0) ||
>   _entry_in_ignore_list (entry->d_name, state))
>   {
>   if (state->debug && _entry_in_ignore_list (entry->d_name, state))
> @@ -750,7 +752,7 @@ count_files (const char *path, int *count, 
> add_files_state_t *state)
>   fflush (stdout);
>   }
>   } else if (entry_type == S_IFDIR) {
> - count_files (next, count, state);
> + count_files (next, count, state, dirlevel + 1);
>   }
>  
>   free (next);
> @@ -962,7 +964,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
> char *argv[])
>   int count;
>  
>   count = 0;
> - count_files (db_path, &count, &add_files_state);
> + count_files (db_path, &count, &add_files_state, 0);
>   if (interrupted)
>   return EXIT_FAILURE;
>  
> @@ -1021,7 +1023,7 @@ notmuch_new_command (notmuch_config_t *config, int 
> argc, char *argv[])
>   timer_is_active = TRUE;
>  }
>  
> -ret = add_files (notmuch, db_path, &add_files_state);
> +ret = add_files (notmuch, db_path, &add_files_state, 0);
>  if (ret)
>   goto DONE;
>  
> -- 
> 1.8.4.2
>
> ___
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: test folder renames

2014-02-23 Thread Jani Nikula
On Sun, 23 Feb 2014, Mark Walters  wrote:
> I was experimenting with letting notmuch new take an argument to tell it
> to scan only a particular directory (and sub-directories) for new
> messages. I came across the following strange behaviour which is also
> present in master (with a fresh database)
>
> I have a bunch of maildirs in /home/mail: so folders .mail.foo/
> .mail.bar/ each of which has cur/new/tmp and all the messages are in
> cur.
>
> If I do mv .mail.foo .mail.bar/ and run notmuch new I get the expected
> lots of renames (900 or so in the case I was trying). But if I then do
> mv .mail.bar/.mail.foo . and run notmuch new almost all the messages get
> removed (but 30 renames do get detected). If I then do touch .mail.foo/*
> the messages get found again
>
> I am guessing the 30 renames might be because those 30 have duplicates
> somewhere else.
>
> But the other behaviour has me puzzled.

This test reproduces the problem for me, but it's not
deterministic. With the loop, I get roughly one fail per test run:

 FAIL   Rename folder back
 --- T051-new-renames.27.expected   2014-02-23 21:37:10.121774241 +
 +++ T051-new-renames.27.output 2014-02-23 21:37:10.121774241 +
 @@ -1 +1 @@
 -No new mail. Detected 10 file renames.
 +No new mail. Removed 10 messages.
 FAIL   Files remain the same
 --- T051-new-renames.28.expected   2014-02-23 21:37:10.133774652 +
 +++ T051-new-renames.28.output 2014-02-23 21:37:10.133774652 +
 @@ -1,13 +1,3 @@
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-121
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-122
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-123
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-124
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-125
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-126
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-127
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-128
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-129
 -/path/to/test/tmp.T051-new-renames/mail/foo/msg-130
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-131
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-132
  /path/to/test/tmp.T051-new-renames/mail/bar/msg-133

I'm as puzzled as you are.

BR,
Jani.
---
 test/T051-new-renames.sh | 40 
 1 file changed, 40 insertions(+)
 create mode 100755 test/T051-new-renames.sh

diff --git a/test/T051-new-renames.sh b/test/T051-new-renames.sh
new file mode 100755
index ..febe006f5888
--- /dev/null
+++ b/test/T051-new-renames.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" with directory renames'
+. ./test-lib.sh
+
+for loop in `seq 10`; do
+
+rm -rf ${MAIL_DIR}
+
+for i in `seq 10`; do
+generate_message '[dir]=foo' '[subject]="Message foo $i"'
+done
+
+for i in `seq 10`; do
+generate_message '[dir]=bar' '[subject]="Message bar $i"'
+done
+
+test_begin_subtest "Index the messages, round $loop"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 20 new messages to the database."
+
+all_files=$(notmuch search --output=files \*)
+count_foo=$(notmuch count folder:foo)
+
+test_begin_subtest "Rename folder"
+mv ${MAIL_DIR}/foo ${MAIL_DIR}/baz
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Rename folder back"
+mv ${MAIL_DIR}/baz ${MAIL_DIR}/foo
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Files remain the same"
+output=$(notmuch search --output=files \*)
+test_expect_equal "$output" "$all_files"
+
+done
+
+test_done
-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 00/13] literal folder: prefix, new path: prefix

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, Mark Walters  wrote:

> I have read most of this series, tested it and run the tests and LGTM +1.
>
> I read the C code fairly carefully, the tests rather less so but they
> looked sane, and I didn't really look at patch 9 for building old
> databases.

Well, I can add (at this point) that patch 9 is tolerable...

>
> Best wishes
>
> Mark

Tomi


>
>
>
>
> On Sat, 22 Feb 2014, Jani Nikula  wrote:
>> Hi all, this is v2 of id:cover.1389304779.git.jani at nikula.org.
>>
>> The new path: prefix is a literal boolean prefix matching the paths,
>> relative from the maildir root, of the message files. There's no
>> interpretation of the maildir special cur/new folders, but a recursive
>> match is provided with "/**" suffix. See the patch for details.
>>
>> The folder: prefix becomes a literal boolean prefix, similar to path:,
>> except it matches the maildir cur/new folders in addition to the
>> specified path. There's no recursive version.
>>
>> Patches 1-5 add the above.
>>
>> Patches 6-8 change the test infrastructure to make it easier to add
>> multiple corpuses, and adds a new test for path: and folder:.
>>
>> Patches 9-11 add support for testing the database upgrade.
>>
>> Patches 12-13 update man pages.
>>
>>
>> I've dropped most of the content in patches 7 and 10 due to their
>> size. The patches (and the whole series) are available in the
>> boolean-folder-and-path-v2 branch at
>> git://gitorious.org/jani/notmuch.git. Web interface at
>> https://gitorious.org/jani/notmuch/commits/0b3dd2d1cc6c413ea07ea326883ac448499c0e79.
>>
>>
>> WARNING! The change requires a database format version bump, and a
>> database upgrade, which is automatically done on 'notmuch new'. The
>> upgrade is irreversible if you want to try this on your database! A
>> complete database rebuild is required for reverting the database format
>> version. Make sure your backups are in order!
>>
>>
>> BR,
>> Jani.
>>
>>
>> Jani Nikula (13):
>>   lib: refactor folder term update after filename removal
>>   lib: add support for path: prefix searches
>>   test: make insert test use the path: prefix
>>   lib: make folder: prefix literal
>>   test: fix test for literal folder: search
>>   test: make it possible to have several corpora
>>   test: add new corpus with folders
>>   test: add tests for the new boolean folder: and path: prefixes
>>   devel: add script to generate test databases
>>   test: add test database in format version 1
>>   test: add database upgrade test from format version 1 to 2
>>   man: update man pages for folder: and path: search terms
>>   man: try to clarify the folder: and path: vs. --output=files confusion
>>
>>  devel/gen-testdb.sh| 124 
>>  lib/database.cc|  45 -
>>  lib/message.cc | 249 ---
>>  lib/notmuch-private.h  |   3 +
>>  man/man1/notmuch-search.1  |  10 +-
>>  man/man7/notmuch-search-terms.7|  28 ++-
>>  test/.gitignore|   2 +-
>>  test/Makefile.local|   2 +-
>>  test/T070-insert.sh|  10 +-
>>  test/T100-search-by-folder.sh  |  24 ++-
>>  test/T101-search-by-folder-and-path.sh |  83 
>>  test/T480-hex-escaping.sh  |   4 +-
>>  test/T530-upgrade.sh   | 103 ++
>>  test/corpus/{ => default}/cur/01:2,|   0
>>  test/corpus/{ => default}/cur/02:2,|   0
>>  test/corpus/{ => default}/cur/03:2,|   0
>>  test/corpus/{ => default}/cur/04:2,|   0
>>  test/corpus/{ => default}/cur/05:2,|   0
>>  test/corpus/{ => default}/cur/06:2,|   0
>>  test/corpus/{ => default}/cur/07:2,|   0
>>  test/corpus/{ => default}/cur/08:2,|   0
>>  test/corpus/{ => default}/cur/09:2,|   0
>>  test/corpus/{ => default}/cur/10:2,|   0
>>  test/corpus/{ => default}/cur/11:2,|   0
>>  test/corpus/{ => default}/cur/12:2,|   0
>>  test/corpus/{ => default}/cur/13:2,|   0
>>  test/corpus/{ => default}/cur/14:2,|   0
>>  test/corpus/{ => default}/cur/15:2,|   0
>>  test/corpus/{ => default}/cur/16:2,|   0
>>  test/corpus/{ => default}/cur/17:2,|   0
>>  test/corpus/{ => default}/cur/18:2,|   0
>>  test/corpus/{ => default}/cur/19:2,|   0
>>  test/corpus/{ => default}/cur/20:2,|   0
>>  test/corpus/{ => default}/cur/21:2,|   0
>>  test/corpus/{ => default}/cur/22:2,|   0
>>  test/corpus/{ => default}/cur/23:2,|   0
>>  test/corpus/{ => default}/cur/24:2,|   0
>>  test/corpus/{ => default}/cur/25:2,|   0
>>  test/corpus/{ => default}/cur/26:2,|   0
>>  test/corpus/{ => default}/cur/27:2,|   0
>>  test/corpus/{ => default}/cur/28:2,|   0
>>  test/corpus/{ => default}/cur/29:2,|   0
>>  test/corpus/{ => default}/cur/30:2,|   0
>>  test/corpus/{ => default}/cur/31:2,|   0
>>  test/corpus/{ => default}/cur/32:2,|   0
>>  test/corpus/{ => default}/cur/33:2, 

[PATCH v3 1/3] build: write version.stamp file containing $(VERSION) string

2014-02-23 Thread Tomi Ollila
This version file will be as prerequisite to the target files
that use the version info for some purpose, like printing
it for the user to examine. The contents of the version.stamp
file is seldom read by the build system itself as the $(VERSION)
variable has the same information.

Thanks to Trevor, David and Mark for their contributions.
---
 .gitignore |  1 +
 Makefile.local | 14 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index ef4f074..1fb3a71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 .first-build-message
 Makefile.config
+version.stamp
 TAGS
 tags
 *cscope*
diff --git a/Makefile.local b/Makefile.local
index 174506c..3a56c06 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -22,6 +22,11 @@ VERSION:=$(shell cat ${srcdir}/version)
 ifeq ($(filter release release-message pre-release 
update-versions,$(MAKECMDGOALS)),)
 ifeq ($(IS_GIT),yes)
 VERSION:=$(shell git describe --match '[0-9.]*'|sed -e s/_/~/ -e s/-/+/ -e 
s/-/~/)
+# Write the file 'version.stamp' in case its contents differ from $(VERSION)
+FILE_VERSION:=$(shell test -f version.stamp && read vs < version.stamp || vs=; 
echo $$vs)
+ifneq ($(FILE_VERSION),$(VERSION))
+   $(shell echo "$(VERSION)" > version.stamp)
+endif
 endif
 endif
 
@@ -69,6 +74,11 @@ ifeq ($(shell cat .first-build-message 2>/dev/null),)
 endif
 endif
 
+# Depend (also) on the file 'version'. In case of ifeq ($(IS_GIT),yes)
+# this file may already have been updated.
+version.stamp: version
+   echo $(VERSION) > $@
+
 $(TAR_FILE):
if git tag -v $(VERSION) >/dev/null 2>&1; then \
ref=$(VERSION); \
@@ -280,6 +290,8 @@ notmuch_client_srcs =   \
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
 
+notmuch.o: version.stamp
+
 notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a 
parse-time-string/libparse-time-string.a
$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
 
@@ -318,7 +330,7 @@ install-desktop:
desktop-file-install --mode 0644 --dir "$(DESTDIR)$(desktop_dir)" 
notmuch.desktop
 
 SRCS  := $(SRCS) $(notmuch_client_srcs)
-CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) notmuch.elc
+CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) 
version.stamp
 
 DISTCLEAN := $(DISTCLEAN) .first-build-message Makefile.config
 
-- 
1.8.0

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3 3/3] emacs: defun notmuch-hello-versions and bind 'v' in hello mode to it

2014-02-23 Thread Tomi Ollila
If notmuch cli & notmuch emacs MUA versions differ, print also the
emacs MUA version string (along with the cli version) to the
minibuffer.
---
 emacs/notmuch-hello.el | 15 +--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 7b3d76b..f5e7268 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -513,6 +513,18 @@ Such a list can be computed with 
`notmuch-hello-query-counts'."
   (remove-hook 'window-configuration-change-hook
   #'notmuch-hello-window-configuration-change
 
+;; the following variable is defined as being defconst in notmuch.el
+(defvar notmuch-emacs-version)
+
+(defun notmuch-hello-versions ()
+  "Display the notmuch version(s)"
+  (interactive)
+  (let ((notmuch-cli-version (notmuch-version)))
+(message "notmuch version %s"
+(if (string= notmuch-emacs-version notmuch-cli-version)
+notmuch-cli-version
+  (concat notmuch-cli-version
+  " (emacs mua version " notmuch-emacs-version ")")
 
 (defvar notmuch-hello-mode-map
   (let ((map (if (fboundp 'make-composed-keymap)
@@ -523,8 +535,7 @@ Such a list can be computed with 
`notmuch-hello-query-counts'."
   ;; it's unlikely to change.
   (copy-keymap widget-keymap
 (set-keymap-parent map notmuch-common-keymap)
-(define-key map "v" (lambda () "Display the notmuch version" (interactive)
- (message "notmuch version %s" (notmuch-version
+(define-key map "v" 'notmuch-hello-versions)
 (define-key map (kbd "") 'widget-backward)
 map)
   "Keymap for \"notmuch hello\" buffers.")
-- 
1.8.0

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


v3 of version change detection and notmuch emacs/hello version(s)

2014-02-23 Thread Tomi Ollila
This is v3 of

id:1393024837-30394-1-git-send-email-tomi.oll...@iki.fi

The only change is that version.stamp was added to root .gitignore file.

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3 2/3] emacs: defconst notmuch-emacs-version to a value during byte compilation

2014-02-23 Thread Tomi Ollila
The notmuch cli program and emacs lisp versions may differ. For now
we can help users with their emacs client problems better if we can
ask what version of emacs MUA they are running. In the future we can
put the emacs MUA version to User-Agent: string in outgoing mail.
---
 emacs/Makefile.local | 6 +-
 emacs/notmuch.el | 7 +++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/emacs/Makefile.local b/emacs/Makefile.local
index 6a39b32..9e3fb7a 100644
--- a/emacs/Makefile.local
+++ b/emacs/Makefile.local
@@ -24,6 +24,8 @@ emacs_images := \
 
 emacs_bytecode = $(emacs_sources:.el=.elc)
 
+$(dir)/notmuch.elc: version.stamp
+
 # Because of defmacro's and defsubst's, we have to account for load
 # dependencies between Elisp files when byte compiling.  Otherwise,
 # the byte compiler may load an old .elc file when processing a
@@ -49,7 +51,9 @@ CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x
 
 ifeq ($(HAVE_EMACS),1)
 %.elc: %.el $(global_deps)
-   $(call quiet,EMACS) --directory emacs -batch -f batch-byte-compile $<
+   $(call quiet,EMACS) --directory emacs \
+   --eval "(setq notmuch--version \"$(VERSION)\")" \
+   -batch -f batch-byte-compile $<
 endif
 
 ifeq ($(WITH_EMACS),1)
diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index 0471750..1b15054 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -81,6 +81,13 @@ To enter a line break in customize, press \\[quoted-insert] 
C-j."
   :type '(alist :key-type (string) :value-type (string))
   :group 'notmuch-search)
 
+;; defconst notmuch-version to a value during build-time byte compilation...
+(defconst notmuch-emacs-version
+  (eval-when-compile (if (boundp 'notmuch--version)
+notmuch--version
+  "unknown"))
+  "Version string of this version of Notmuch Emacs MUA.")
+
 (defvar notmuch-query-history nil
   "Variable to store minibuffer history for notmuch queries")
 
-- 
1.8.0

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[DRAFT PATCH] notmuch new: do not ignore '.notmuch' in non-toplevel directories

2014-02-23 Thread Tomi Ollila
So that users may have email in subdir/.notmuch directories.
---

Compiles, current tests pass. might ignore database_path/.notmuch and
might descent into database_path/.../.notmuch :D

Tomi


 notmuch-new.c | 18 ++
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/notmuch-new.c b/notmuch-new.c
index 8529fdd..b17bd75 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -344,7 +344,8 @@ add_file (notmuch_database_t *notmuch, const char *filename,
 static notmuch_status_t
 add_files (notmuch_database_t *notmuch,
   const char *path,
-  add_files_state_t *state)
+  add_files_state_t *state,
+  int dirlevel)
 {
 DIR *dir = NULL;
 struct dirent *entry = NULL;
@@ -469,11 +470,11 @@ add_files (notmuch_database_t *notmuch,
if (strcmp (entry->d_name, ".") == 0 ||
strcmp (entry->d_name, "..") == 0 ||
(is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
-   strcmp (entry->d_name, ".notmuch") == 0)
+   (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0))
continue;
 
next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
-   status = add_files (notmuch, next, state);
+   status = add_files (notmuch, next, state, dirlevel + 1);
if (status) {
ret = status;
goto DONE;
@@ -702,7 +703,8 @@ stop_progress_printing_timer (void)
  * initialized to zero by the top-level caller before calling
  * count_files). */
 static void
-count_files (const char *path, int *count, add_files_state_t *state)
+count_files (const char *path, int *count, add_files_state_t *state,
+int dirlevel)
 {
 struct dirent *entry = NULL;
 char *next;
@@ -725,7 +727,7 @@ count_files (const char *path, int *count, 
add_files_state_t *state)
 */
if (strcmp (entry->d_name, ".") == 0 ||
strcmp (entry->d_name, "..") == 0 ||
-   strcmp (entry->d_name, ".notmuch") == 0 ||
+   (dirlevel == 0 && strcmp (entry->d_name, ".notmuch") == 0) ||
_entry_in_ignore_list (entry->d_name, state))
{
if (state->debug && _entry_in_ignore_list (entry->d_name, state))
@@ -750,7 +752,7 @@ count_files (const char *path, int *count, 
add_files_state_t *state)
fflush (stdout);
}
} else if (entry_type == S_IFDIR) {
-   count_files (next, count, state);
+   count_files (next, count, state, dirlevel + 1);
}
 
free (next);
@@ -962,7 +964,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
int count;
 
count = 0;
-   count_files (db_path, &count, &add_files_state);
+   count_files (db_path, &count, &add_files_state, 0);
if (interrupted)
return EXIT_FAILURE;
 
@@ -1021,7 +1023,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
timer_is_active = TRUE;
 }
 
-ret = add_files (notmuch, db_path, &add_files_state);
+ret = add_files (notmuch, db_path, &add_files_state, 0);
 if (ret)
goto DONE;
 
-- 
1.8.4.2

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 0/3] check new.tags for invalid tags

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, Jani Nikula  wrote:

> On Sun, 23 Feb 2014, Rob Browning  wrote:
>> In the [new] section, "tags=;" will cause notmuch to create empty tags
>> that are fairly hard to remove from the command line.
>
> Clearly broken. This series fixes the issue at the cli
> level. (Forbidding empty tags at the lib level is slightly more
> complicated, as we would still have to ensure old dump files can be
> restored.)
>
>> After some help on #bup, here's what I came up with to remove them,
>> though it assumes that the empty tag "+ " will always be first in dump's
>> output:
>>
>>   notmuch dump --format=batch-tag 'tag:""' | perl -pe 's/^\+ //' \
>> | notmuch restore --format=batch-tag
>>
>> And note that you have to use restore, "notmuch tag --batch" doesn't
>> appear to accept "- " as a tag, even though dump will produce "+ ".
>
> I didn't check this further, but the regular, non-batch notmuch tag
> should still work for removal of empty tags.

LGTM.

$ notmuch tag + -- 
id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.j...@nikula.org
Error: empty tag forbiddenzsh: exit 1 notmuch tag + --

$ notmuch restore --accumulate
+ id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.j...@nikula.org
 -- notmuch search shows space after ( to inform
there is empty tag.

$ notmuch tag - -- 
id:edc2bc900f75bb2e72be2037e2df9105be7f0273.1393174108.git.j...@nikula.org


notmuch search no longer shows space after (

Also, SomeBody(tm) should add '\n' to the fprintf() in tag-util.c:175 (line
number after applying these patches).

Tomi

>
> BR,
> Jani.
>
> Jani Nikula (3):
>   cli: export function for illegal tag checking
>   cli: make sure notmuch new and insert don't add invalid tags
>   test: add tests for invalid new.tags
>
>  notmuch-insert.c|  9 +
>  notmuch-new.c   | 14 +-
>  tag-util.c  |  9 +
>  tag-util.h  | 12 
>  test/T050-new.sh| 17 +
>  test/T070-insert.sh | 19 +++
>  6 files changed, 71 insertions(+), 9 deletions(-)
>
> -- 
> 1.8.5.3
>
> ___
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 05/13] test: fix test for literal folder: search

2014-02-23 Thread David Bremner
Jani Nikula  writes:

> Some of the folder: matching capabilities are lost in the
> probabilistic to boolean prefix change. Fix them.

the first 5 look copacetic.

d


Re: [RFC Patch v3 2/3] doc: add target rst2man to build man pages using rst2man

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, David Bremner  wrote:

> Many people have docutils installed, but not sphinx. Allow these
> people to build the man pages.

+1 from me to start having manuals in reStructuredText format and
then converting these to the target formats. Some comments on
the patch series below:

Building any docs using this is not yet activated ?

Anyway, I tried to build manual pages using 'make man' and got this:

  $ make man
  sphinx-build -b man -d doc/_build/doctrees -q -c doc doc doc/_build/man
  Making output directory...

  Sphinx error:
  Builder name man not registered

No fallback to use rst2man...


Comments regarding prerst2man.py inline below:

Tomi

> ---

// stuff deleted //

> diff --git a/doc/rst2man/prerst2man.py b/doc/rst2man/prerst2man.py
> new file mode 100644
> index 000..797dd20
> --- /dev/null
> +++ b/doc/rst2man/prerst2man.py
> @@ -0,0 +1,53 @@
> +from sys import argv
> +from datetime import date
> +import re
> +
> +sourcedir=argv[1]
> +outdir=argv[2]

Style! run pep8 prerst2html.py and fix the issues it prints
to the screen, like ' = ' above and remove trailing semicolon
below... (and also pep8(1) doc/conf.py.
 
> +
> +execfile(sourcedir+"/conf.py");
> +
> +
> +
> +
> +def header(file,startdocname, command, description, authors, section):
> +file.write("""
> +{:s}
> +{:s}
> +{:s}
> +
> +:Date:   {:s}
> +:Version: {:s}
> +:Manual section: {:d}
> +:Manual group: {:s}

For python < 2.7 these needs to be {0:s}, {1:s}, {2:s}.. {5:d}...

> +
> +""".format(
> +'-' * len(description),
> +description,
> +'-' * len(description),
> +date.today().isoformat(),release,section,project))

Replace date.today.isoformat() with date determined from other
sources (NEWS file?)

> +
> +blankre = re.compile("^\s*$")
> +for page in man_pages:
> +outfile = open(outdir+"/"+page[0]+'.rst','w')
> +infile = open(sourcedir+"/"+page[0]+".rst",'r')

In addition to formatting above, use either ".rst" or '.rst'
(and perhaps other quotations in these 2 lines) for consistency.

> +
> +
> +# this is a crude hack. We look for the first blank line, and
> +# insert the rst2man header there.
> +#
> +# XXX consider really parsing input
> +
> +count=0
> +lines = infile.readlines()
> +for line in lines:
> +outfile.write(line);
> +if (blankre.match(line)):
> +break
> +count = count + 1
> +
> +del lines[0:count+1]

pep8 will in the lime above (as it is not lines[0:count + 1])
I might not have complained but... :D

> +
> +header(outfile,*page)
> +
> +outfile.write("".join(lines))
> -- 
> 1.8.5.3
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] man: escape backslash in notmuch-tag example

2014-02-23 Thread David Bremner
The example was originally intended to have a literal backslash in it, but
'\ ' is interpreted by nroff as a non-breaking space.

It doesn't make much difference to the example, but the non-breaking
space triggers a bug in doclifter.
---

Tomi sortof convinced me on IRC that \e was a better choice that \\. I
also looked at the groff manual, but that mostly encouraged me to work
more on converting to something other than *roff.

 man/man1/notmuch-tag.1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 710fae6..f4fb1f3 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -126,7 +126,7 @@ of the tag

 +space%20in%20tags -- Two
 # add tag '(tags)', among other stunts.
-+crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
++crazy{ +(tags) +&are +#possible\e -- tag:"space in tags"
 +match*crazy -- tag:crazy{
 +some_tag -- id:"this is ""nauty)"""
 .fi
-- 
1.8.5.3



[PATCH 0/3] check new.tags for invalid tags

2014-02-23 Thread Jani Nikula
On Sun, 23 Feb 2014, Rob Browning  wrote:
> In the [new] section, "tags=;" will cause notmuch to create empty tags
> that are fairly hard to remove from the command line.

Clearly broken. This series fixes the issue at the cli
level. (Forbidding empty tags at the lib level is slightly more
complicated, as we would still have to ensure old dump files can be
restored.)

> After some help on #bup, here's what I came up with to remove them,
> though it assumes that the empty tag "+ " will always be first in dump's
> output:
>
>   notmuch dump --format=batch-tag 'tag:""' | perl -pe 's/^\+ //' \
> | notmuch restore --format=batch-tag
>
> And note that you have to use restore, "notmuch tag --batch" doesn't
> appear to accept "- " as a tag, even though dump will produce "+ ".

I didn't check this further, but the regular, non-batch notmuch tag
should still work for removal of empty tags.

BR,
Jani.

Jani Nikula (3):
  cli: export function for illegal tag checking
  cli: make sure notmuch new and insert don't add invalid tags
  test: add tests for invalid new.tags

 notmuch-insert.c|  9 +
 notmuch-new.c   | 14 +-
 tag-util.c  |  9 +
 tag-util.h  | 12 
 test/T050-new.sh| 17 +
 test/T070-insert.sh | 19 +++
 6 files changed, 71 insertions(+), 9 deletions(-)

-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/3] cli: export function for illegal tag checking

2014-02-23 Thread Jani Nikula
This lets us check for forbidden tags consistently across the cli. No
functional changes.
---
 tag-util.c |  9 +
 tag-util.h | 12 
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/tag-util.c b/tag-util.c
index 3bde4097372a..e2d5b795acc3 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -31,14 +31,7 @@ line_error (tag_parse_status_t status,
 return status;
 }
 
-/*
- * Test tags for some forbidden cases.
- *
- * return: NULL if OK,
- *explanatory message otherwise.
- */
-
-static const char *
+const char *
 illegal_tag (const char *tag, notmuch_bool_t remove)
 {
 
diff --git a/tag-util.h b/tag-util.h
index 4628f1630ad6..8a4074ce168f 100644
--- a/tag-util.h
+++ b/tag-util.h
@@ -90,6 +90,18 @@ parse_tag_command_line (void *ctx, int argc, char **argv,
char **query_str, tag_op_list_t *ops);
 
 /*
+ * Test tags for some forbidden cases.
+ *
+ * Relax the checks if 'remove' is true to allow removal of previously
+ * added forbidden tags.
+ *
+ * return: NULL if OK,
+ *explanatory message otherwise.
+ */
+const char *
+illegal_tag (const char *tag, notmuch_bool_t remove);
+
+/*
  * Create an empty list of tag operations
  *
  * ctx is passed to talloc
-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 2/3] cli: make sure notmuch new and insert don't add invalid tags

2014-02-23 Thread Jani Nikula
Check new.tags configuration values before doing anything, and bail
out on invalid values.
---
 notmuch-insert.c |  9 +
 notmuch-new.c| 14 +-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/notmuch-insert.c b/notmuch-insert.c
index cd6de88f6891..6752fc8de255 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -431,6 +431,15 @@ notmuch_insert_command (notmuch_config_t *config, int 
argc, char *argv[])
return EXIT_FAILURE;
 }
 for (i = 0; i < new_tags_length; i++) {
+   const char *error_msg;
+
+   error_msg = illegal_tag (new_tags[i], FALSE);
+   if (error_msg) {
+   fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
+new_tags[i],  error_msg);
+   return EXIT_FAILURE;
+   }
+
if (tag_op_list_append (tag_ops, new_tags[i], FALSE))
return EXIT_FAILURE;
 }
diff --git a/notmuch-new.c b/notmuch-new.c
index 8529fdd3eac7..82acf695353e 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -19,6 +19,7 @@
  */
 
 #include "notmuch-client.h"
+#include "tag-util.h"
 
 #include 
 
@@ -918,7 +919,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 struct sigaction action;
 _filename_node_t *f;
 int opt_index;
-int i;
+unsigned int i;
 notmuch_bool_t timer_is_active = FALSE;
 notmuch_bool_t no_hooks = FALSE;
 notmuch_bool_t quiet = FALSE, verbose = FALSE;
@@ -950,6 +951,17 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 add_files_state.synchronize_flags = 
notmuch_config_get_maildir_synchronize_flags (config);
 db_path = notmuch_config_get_database_path (config);
 
+for (i = 0; i < add_files_state.new_tags_length; i++) {
+   const char *error_msg;
+
+   error_msg = illegal_tag (add_files_state.new_tags[i], FALSE);
+   if (error_msg) {
+   fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
+add_files_state.new_tags[i], error_msg);
+   return EXIT_FAILURE;
+   }
+}
+
 if (!no_hooks) {
ret = notmuch_run_hook (db_path, "pre-new");
if (ret)
-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 3/3] test: add tests for invalid new.tags

2014-02-23 Thread Jani Nikula
Similar tests for both notmuch new and insert.
---
 test/T050-new.sh| 17 +
 test/T070-insert.sh | 19 +++
 2 files changed, 36 insertions(+)

diff --git a/test/T050-new.sh b/test/T050-new.sh
index b7668ff0c4bc..ad46ee6d51b6 100755
--- a/test/T050-new.sh
+++ b/test/T050-new.sh
@@ -263,4 +263,21 @@ notmuch search --format=text0 --output=files --offset=1 
--limit=1 '*' | xargs -0
 output=$(NOTMUCH_NEW --quiet)
 test_expect_equal "$output" ""
 
+OLDCONFIG=$(notmuch config get new.tags)
+
+test_begin_subtest "Empty tags in new.tags are forbidden"
+notmuch config set new.tags "foo;;bar"
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden"
+
+test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
+notmuch config set new.tags "-foo;bar"
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "Error: tag '-foo' in new.tags: tag starting with 
'-' forbidden"
+
+test_expect_code 1 "Invalid tags set exit code" \
+"NOTMUCH_NEW 2>&1"
+
+notmuch config set new.tags $OLDCONFIG
+
 test_done
diff --git a/test/T070-insert.sh b/test/T070-insert.sh
index e8dc4c099ed1..b77c5e13c87f 100755
--- a/test/T070-insert.sh
+++ b/test/T070-insert.sh
@@ -164,4 +164,23 @@ gen_insert_msg
 test_expect_code 1 "Insert message, create invalid subfolder" \
 "notmuch insert --folder=../G --create-folder $gen_msg_filename"
 
+OLDCONFIG=$(notmuch config get new.tags)
+
+test_begin_subtest "Empty tags in new.tags are forbidden"
+notmuch config set new.tags "foo;;bar"
+gen_insert_msg
+output=$(notmuch insert $gen_msg_filename 2>&1)
+test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden"
+
+test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
+notmuch config set new.tags "-foo;bar"
+gen_insert_msg
+output=$(notmuch insert $gen_msg_filename 2>&1)
+test_expect_equal "$output" "Error: tag '-foo' in new.tags: tag starting with 
'-' forbidden"
+
+test_expect_code 1 "Invalid tags set exit code" \
+"notmuch insert $gen_msg_filename 2>&1"
+
+notmuch config set new.tags $OLDCONFIG
+
 test_done
-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2 05/13] test: fix test for literal folder: search

2014-02-23 Thread David Bremner
Jani Nikula  writes:

> Some of the folder: matching capabilities are lost in the
> probabilistic to boolean prefix change. Fix them.

the first 5 look copacetic.

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] man: escape backslash in notmuch-tag example

2014-02-23 Thread David Bremner
The example was originally intended to have a literal backslash in it, but
'\ ' is interpreted by nroff as a non-breaking space.

It doesn't make much difference to the example, but the non-breaking
space triggers a bug in doclifter.
---

Tomi sortof convinced me on IRC that \e was a better choice that \\. I
also looked at the groff manual, but that mostly encouraged me to work
more on converting to something other than *roff.

 man/man1/notmuch-tag.1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 710fae6..f4fb1f3 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -126,7 +126,7 @@ of the tag
 
 +space%20in%20tags -- Two
 # add tag '(tags)', among other stunts.
-+crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
++crazy{ +(tags) +&are +#possible\e -- tag:"space in tags"
 +match*crazy -- tag:crazy{
 +some_tag -- id:"this is ""nauty)"""
 .fi
-- 
1.8.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2 00/13] literal folder: prefix, new path: prefix

2014-02-23 Thread Tomi Ollila
On Sun, Feb 23 2014, Mark Walters  wrote:

> I have read most of this series, tested it and run the tests and LGTM +1.
>
> I read the C code fairly carefully, the tests rather less so but they
> looked sane, and I didn't really look at patch 9 for building old
> databases.

Well, I can add (at this point) that patch 9 is tolerable...

>
> Best wishes
>
> Mark

Tomi


>
>
>
>
> On Sat, 22 Feb 2014, Jani Nikula  wrote:
>> Hi all, this is v2 of id:cover.1389304779.git.j...@nikula.org.
>>
>> The new path: prefix is a literal boolean prefix matching the paths,
>> relative from the maildir root, of the message files. There's no
>> interpretation of the maildir special cur/new folders, but a recursive
>> match is provided with "/**" suffix. See the patch for details.
>>
>> The folder: prefix becomes a literal boolean prefix, similar to path:,
>> except it matches the maildir cur/new folders in addition to the
>> specified path. There's no recursive version.
>>
>> Patches 1-5 add the above.
>>
>> Patches 6-8 change the test infrastructure to make it easier to add
>> multiple corpuses, and adds a new test for path: and folder:.
>>
>> Patches 9-11 add support for testing the database upgrade.
>>
>> Patches 12-13 update man pages.
>>
>>
>> I've dropped most of the content in patches 7 and 10 due to their
>> size. The patches (and the whole series) are available in the
>> boolean-folder-and-path-v2 branch at
>> git://gitorious.org/jani/notmuch.git. Web interface at
>> https://gitorious.org/jani/notmuch/commits/0b3dd2d1cc6c413ea07ea326883ac448499c0e79.
>>
>>
>> WARNING! The change requires a database format version bump, and a
>> database upgrade, which is automatically done on 'notmuch new'. The
>> upgrade is irreversible if you want to try this on your database! A
>> complete database rebuild is required for reverting the database format
>> version. Make sure your backups are in order!
>>
>>
>> BR,
>> Jani.
>>
>>
>> Jani Nikula (13):
>>   lib: refactor folder term update after filename removal
>>   lib: add support for path: prefix searches
>>   test: make insert test use the path: prefix
>>   lib: make folder: prefix literal
>>   test: fix test for literal folder: search
>>   test: make it possible to have several corpora
>>   test: add new corpus with folders
>>   test: add tests for the new boolean folder: and path: prefixes
>>   devel: add script to generate test databases
>>   test: add test database in format version 1
>>   test: add database upgrade test from format version 1 to 2
>>   man: update man pages for folder: and path: search terms
>>   man: try to clarify the folder: and path: vs. --output=files confusion
>>
>>  devel/gen-testdb.sh| 124 
>>  lib/database.cc|  45 -
>>  lib/message.cc | 249 ---
>>  lib/notmuch-private.h  |   3 +
>>  man/man1/notmuch-search.1  |  10 +-
>>  man/man7/notmuch-search-terms.7|  28 ++-
>>  test/.gitignore|   2 +-
>>  test/Makefile.local|   2 +-
>>  test/T070-insert.sh|  10 +-
>>  test/T100-search-by-folder.sh  |  24 ++-
>>  test/T101-search-by-folder-and-path.sh |  83 
>>  test/T480-hex-escaping.sh  |   4 +-
>>  test/T530-upgrade.sh   | 103 ++
>>  test/corpus/{ => default}/cur/01:2,|   0
>>  test/corpus/{ => default}/cur/02:2,|   0
>>  test/corpus/{ => default}/cur/03:2,|   0
>>  test/corpus/{ => default}/cur/04:2,|   0
>>  test/corpus/{ => default}/cur/05:2,|   0
>>  test/corpus/{ => default}/cur/06:2,|   0
>>  test/corpus/{ => default}/cur/07:2,|   0
>>  test/corpus/{ => default}/cur/08:2,|   0
>>  test/corpus/{ => default}/cur/09:2,|   0
>>  test/corpus/{ => default}/cur/10:2,|   0
>>  test/corpus/{ => default}/cur/11:2,|   0
>>  test/corpus/{ => default}/cur/12:2,|   0
>>  test/corpus/{ => default}/cur/13:2,|   0
>>  test/corpus/{ => default}/cur/14:2,|   0
>>  test/corpus/{ => default}/cur/15:2,|   0
>>  test/corpus/{ => default}/cur/16:2,|   0
>>  test/corpus/{ => default}/cur/17:2,|   0
>>  test/corpus/{ => default}/cur/18:2,|   0
>>  test/corpus/{ => default}/cur/19:2,|   0
>>  test/corpus/{ => default}/cur/20:2,|   0
>>  test/corpus/{ => default}/cur/21:2,|   0
>>  test/corpus/{ => default}/cur/22:2,|   0
>>  test/corpus/{ => default}/cur/23:2,|   0
>>  test/corpus/{ => default}/cur/24:2,|   0
>>  test/corpus/{ => default}/cur/25:2,|   0
>>  test/corpus/{ => default}/cur/26:2,|   0
>>  test/corpus/{ => default}/cur/27:2,|   0
>>  test/corpus/{ => default}/cur/28:2,|   0
>>  test/corpus/{ => default}/cur/29:2,|   0
>>  test/corpus/{ => default}/cur/30:2,|   0
>>  test/corpus/{ => default}/cur/31:2,|   0
>>  test/corpus/{ => default}/cur/32:2,|   0
>>  test/corpus/{ => default}/cur/33:2,

Weird behaviour in notmuch new

2014-02-23 Thread Mark Walters

Hi

I was experimenting with letting notmuch new take an argument to tell it
to scan only a particular directory (and sub-directories) for new
messages. I came across the following strange behaviour which is also
present in master (with a fresh database)

I have a bunch of maildirs in /home/mail: so folders .mail.foo/
.mail.bar/ each of which has cur/new/tmp and all the messages are in
cur.

If I do mv .mail.foo .mail.bar/ and run notmuch new I get the expected
lots of renames (900 or so in the case I was trying). But if I then do
mv .mail.bar/.mail.foo . and run notmuch new almost all the messages get
removed (but 30 renames do get detected). If I then do touch .mail.foo/*
the messages get found again

I am guessing the 30 renames might be because those 30 have duplicates
somewhere else. 

But the other behaviour has me puzzled.

Best wishes

Mark





[PATCH v2 13/13] man: try to clarify the folder: and path: vs. --output=files confusion

2014-02-23 Thread Jani Nikula
---
 man/man1/notmuch-search.1 | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/man/man1/notmuch-search.1 b/man/man1/notmuch-search.1
index 55a81e79fce4..a2b1ae43f411 100644
--- a/man/man1/notmuch-search.1
+++ b/man/man1/notmuch-search.1
@@ -82,8 +82,14 @@ one per line (\-\-format=text), separated by null characters
 S-Expression list (\-\-format=sexp).

 Note that each message may have multiple filenames associated with it.
-All of them are included in the output, unless limited with the
-\-\-duplicate=N option.
+All of them are included in the output (unless limited with the
+\-\-duplicate=N option). This may be particularly confusing for
+.B folder:
+or
+.B path:
+searches in a specified directory, as the messages may have duplicates
+in other directories that are included in the output, although these
+files alone would not match the search.
 .RE
 .RS 4
 .TP 4
-- 
1.8.5.3



[PATCH v2 12/13] man: update man pages for folder: and path: search terms

2014-02-23 Thread Jani Nikula
---
 man/man7/notmuch-search-terms.7 | 28 ++--
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/man/man7/notmuch-search-terms.7 b/man/man7/notmuch-search-terms.7
index a768b630a4d1..907403dd6f2e 100644
--- a/man/man7/notmuch-search-terms.7
+++ b/man/man7/notmuch-search-terms.7
@@ -54,6 +54,8 @@ terms to match against specific portions of an email, (where

folder:

+   path: or path:/**
+
date:..

 The
@@ -101,12 +103,26 @@ thread ID values can be seen in the first column of 
output from

 The
 .B folder:
-prefix can be used to search for email message files that are
-contained within particular directories within the mail store. If the
-same email message has multiple message files associated with it, it's
-sufficient for a match that at least one of the files is contained
-within a matching directory. Only the directory components below the
-top-level mail database path are available to be searched.
+and
+.B path:
+prefixes can be used to search for email message files that are
+contained within particular directories within the mail store. The
+directories are specified relative from the top-level mail database
+path, and thus only the directory components below that are available
+to be searched.
+
+The
+.B folder:
+prefix matches messages in the specified maildir folder, i.e. in the
+specified directory and its "new" and "cur" subdirectories. The
+.B path:
+prefix matches messages in the specified directory only, unless the
+"/**" suffix is used to denote the specified directory and all its
+subdirectories recursively. For both, the empty string "" matches the
+top level maildir folder or directory. If the same email message has
+multiple message files associated with it, it's sufficient for a match
+that at least one of the files is contained within a matching
+directory.

 The
 .B date:
-- 
1.8.5.3



[PATCH v2 11/13] test: add database upgrade test from format version 1 to 2

2014-02-23 Thread Jani Nikula
Test the upgrade from probabilistic to boolean folder: terms, and
addition of path: terms.
---
 test/T530-upgrade.sh | 103 +++
 1 file changed, 103 insertions(+)
 create mode 100755 test/T530-upgrade.sh

diff --git a/test/T530-upgrade.sh b/test/T530-upgrade.sh
new file mode 100755
index ..cf9914e380be
--- /dev/null
+++ b/test/T530-upgrade.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+test_description="database upgrade"
+
+. ./test-lib.sh
+
+tar zxf $TEST_DIRECTORY/test-databases/database-v1.tar.gz -C ${MAIL_DIR} 
--strip-components=1
+
+test_begin_subtest "folder: search does not work with old database version"
+output=$(notmuch search folder:foo)
+test_expect_equal "$output" ""
+
+test_begin_subtest "path: search does not work with old database version"
+output=$(notmuch search path:foo)
+test_expect_equal "$output" ""
+
+test_begin_subtest "database upgrade from format version 1"
+output=$(notmuch new)
+test_expect_equal "$output" "\
+Welcome to a new version of notmuch! Your database will now be upgraded.
+Your notmuch database has now been upgraded to database format version 2.
+No new mail."
+
+test_begin_subtest "folder: no longer matches in the middle of path"
+output=$(notmuch search folder:baz)
+test_expect_equal "$output" ""
+
+test_begin_subtest "folder: search"
+output=$(notmuch search --output=files folder:foo | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# bar/baz/05:2, and new/03:2, are duplicates of foo/05:2, and
+# foo/new/03:2, respectively
+test_expect_equal "$output" "MAIL_DIR/bar/baz/05:2,
+MAIL_DIR/foo/05:2,
+MAIL_DIR/foo/06:2,
+MAIL_DIR/foo/cur/07:2,
+MAIL_DIR/foo/cur/08:2,
+MAIL_DIR/foo/new/03:2,
+MAIL_DIR/foo/new/09:2,
+MAIL_DIR/foo/new/10:2,
+MAIL_DIR/new/03:2,"
+
+test_begin_subtest "top level folder: search"
+output=$(notmuch search --output=files folder:'""' | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/new/03:2, is a duplicate of new/03:2,
+test_expect_equal "$output" "MAIL_DIR/01:2,
+MAIL_DIR/02:2,
+MAIL_DIR/cur/29:2,
+MAIL_DIR/cur/30:2,
+MAIL_DIR/cur/31:2,
+MAIL_DIR/cur/32:2,
+MAIL_DIR/cur/33:2,
+MAIL_DIR/cur/34:2,
+MAIL_DIR/cur/35:2,
+MAIL_DIR/cur/36:2,
+MAIL_DIR/cur/37:2,
+MAIL_DIR/cur/38:2,
+MAIL_DIR/cur/39:2,
+MAIL_DIR/cur/40:2,
+MAIL_DIR/cur/41:2,
+MAIL_DIR/cur/42:2,
+MAIL_DIR/cur/43:2,
+MAIL_DIR/cur/44:2,
+MAIL_DIR/cur/45:2,
+MAIL_DIR/cur/46:2,
+MAIL_DIR/cur/47:2,
+MAIL_DIR/cur/48:2,
+MAIL_DIR/cur/49:2,
+MAIL_DIR/cur/50:2,
+MAIL_DIR/cur/52:2,
+MAIL_DIR/cur/53:2,
+MAIL_DIR/foo/new/03:2,
+MAIL_DIR/new/03:2,
+MAIL_DIR/new/04:2,"
+
+test_begin_subtest "path: search"
+output=$(notmuch search --output=files path:"bar" | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/05:2, is a duplicate of bar/baz/05:2,
+test_expect_equal "$output" "MAIL_DIR/bar/17:2,
+MAIL_DIR/bar/18:2,"
+
+test_begin_subtest "top level path: search"
+output=$(notmuch search --output=files path:'""' | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+test_expect_equal "$output" "MAIL_DIR/01:2,
+MAIL_DIR/02:2,"
+
+test_begin_subtest "recursive path: search"
+output=$(notmuch search --output=files path:"bar/**" | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/05:2, is a duplicate of bar/baz/05:2,
+test_expect_equal "$output" "MAIL_DIR/bar/17:2,
+MAIL_DIR/bar/18:2,
+MAIL_DIR/bar/baz/05:2,
+MAIL_DIR/bar/baz/23:2,
+MAIL_DIR/bar/baz/24:2,
+MAIL_DIR/bar/baz/cur/25:2,
+MAIL_DIR/bar/baz/cur/26:2,
+MAIL_DIR/bar/baz/new/27:2,
+MAIL_DIR/bar/baz/new/28:2,
+MAIL_DIR/bar/cur/19:2,
+MAIL_DIR/bar/cur/20:2,
+MAIL_DIR/bar/new/21:2,
+MAIL_DIR/bar/new/22:2,
+MAIL_DIR/foo/05:2,"
+
+test_done
-- 
1.8.5.3



[PATCH v2 10/13] test: add test database in format version 1

2014-02-23 Thread Jani Nikula
Generated using:

$ cd test
$ ../devel/gen-testdb.sh -v 0.17 -c $PWD/corpus/folders -s v1

This database contains old style probabilistic folder: terms and no
path: terms.
---

corpus/database content dropped to save bandwidth, see

https://gitorious.org/jani/notmuch/commit/88bcffe31441e62d19769a2a941a2965f8c91486

 test/test-databases/README |   5 +
 test/test-databases/database-v1.tar.gz | Bin 0 -> 262063 bytes
 2 files changed, 5 insertions(+)
 create mode 100644 test/test-databases/README
 create mode 100644 test/test-databases/database-v1.tar.gz

diff --git a/test/test-databases/README b/test/test-databases/README
new file mode 100644
index ..af5defe80728
--- /dev/null
+++ b/test/test-databases/README
@@ -0,1 +1,5 @@
+Notmuch test databases
+==
+
+This directory contains pre-generated databases with their source
+corpus, chiefly for the purpose of testing database upgrade.
-- 
1.8.5.3



[PATCH v2 09/13] devel: add script to generate test databases

2014-02-23 Thread Jani Nikula
Add script to generate notmuch test databases using specified versions
of notmuch. This is useful for generating material for database
upgrade tests.

This reuses the test infrastructure to have a sandbox environment for
notmuch new etc.
---
 devel/gen-testdb.sh | 124 
 1 file changed, 124 insertions(+)
 create mode 100755 devel/gen-testdb.sh

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



[PATCH v2 08/13] test: add tests for the new boolean folder: and path: prefixes

2014-02-23 Thread Jani Nikula
Additional tests for the boolean folder: and path: prefixes.
---
 test/T101-search-by-folder-and-path.sh | 83 ++
 1 file changed, 83 insertions(+)
 create mode 100755 test/T101-search-by-folder-and-path.sh

diff --git a/test/T101-search-by-folder-and-path.sh 
b/test/T101-search-by-folder-and-path.sh
new file mode 100755
index ..9f809e46e110
--- /dev/null
+++ b/test/T101-search-by-folder-and-path.sh
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+test_description='"notmuch search" by folder: and path:'
+. ./test-lib.sh
+
+add_email_corpus folders
+
+test_begin_subtest "folder: search"
+output=$(notmuch search --output=files folder:foo | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# bar/baz/05:2, and new/03:2, are duplicates of foo/05:2, and
+# foo/new/03:2, respectively
+test_expect_equal "$output" "MAIL_DIR/bar/baz/05:2,
+MAIL_DIR/foo/05:2,
+MAIL_DIR/foo/06:2,
+MAIL_DIR/foo/cur/07:2,
+MAIL_DIR/foo/cur/08:2,
+MAIL_DIR/foo/new/03:2,
+MAIL_DIR/foo/new/09:2,
+MAIL_DIR/foo/new/10:2,
+MAIL_DIR/new/03:2,"
+
+test_begin_subtest "top level folder: search"
+output=$(notmuch search --output=files folder:'""' | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/new/03:2, is a duplicate of new/03:2,
+test_expect_equal "$output" "MAIL_DIR/01:2,
+MAIL_DIR/02:2,
+MAIL_DIR/cur/29:2,
+MAIL_DIR/cur/30:2,
+MAIL_DIR/cur/31:2,
+MAIL_DIR/cur/32:2,
+MAIL_DIR/cur/33:2,
+MAIL_DIR/cur/34:2,
+MAIL_DIR/cur/35:2,
+MAIL_DIR/cur/36:2,
+MAIL_DIR/cur/37:2,
+MAIL_DIR/cur/38:2,
+MAIL_DIR/cur/39:2,
+MAIL_DIR/cur/40:2,
+MAIL_DIR/cur/41:2,
+MAIL_DIR/cur/42:2,
+MAIL_DIR/cur/43:2,
+MAIL_DIR/cur/44:2,
+MAIL_DIR/cur/45:2,
+MAIL_DIR/cur/46:2,
+MAIL_DIR/cur/47:2,
+MAIL_DIR/cur/48:2,
+MAIL_DIR/cur/49:2,
+MAIL_DIR/cur/50:2,
+MAIL_DIR/cur/52:2,
+MAIL_DIR/cur/53:2,
+MAIL_DIR/foo/new/03:2,
+MAIL_DIR/new/03:2,
+MAIL_DIR/new/04:2,"
+
+test_begin_subtest "path: search"
+output=$(notmuch search --output=files path:"bar" | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/05:2, is a duplicate of bar/baz/05:2,
+test_expect_equal "$output" "MAIL_DIR/bar/17:2,
+MAIL_DIR/bar/18:2,"
+
+test_begin_subtest "top level path: search"
+output=$(notmuch search --output=files path:'""' | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+test_expect_equal "$output" "MAIL_DIR/01:2,
+MAIL_DIR/02:2,"
+
+test_begin_subtest "recursive path: search"
+output=$(notmuch search --output=files path:"bar/**" | sed -e 
"s,$MAIL_DIR,MAIL_DIR," | sort)
+# foo/05:2, is a duplicate of bar/baz/05:2,
+test_expect_equal "$output" "MAIL_DIR/bar/17:2,
+MAIL_DIR/bar/18:2,
+MAIL_DIR/bar/baz/05:2,
+MAIL_DIR/bar/baz/23:2,
+MAIL_DIR/bar/baz/24:2,
+MAIL_DIR/bar/baz/cur/25:2,
+MAIL_DIR/bar/baz/cur/26:2,
+MAIL_DIR/bar/baz/new/27:2,
+MAIL_DIR/bar/baz/new/28:2,
+MAIL_DIR/bar/cur/19:2,
+MAIL_DIR/bar/cur/20:2,
+MAIL_DIR/bar/new/21:2,
+MAIL_DIR/bar/new/22:2,
+MAIL_DIR/foo/05:2,"
+
+test_done
-- 
1.8.5.3



[PATCH v2 07/13] test: add new corpus with folders

2014-02-23 Thread Jani Nikula
---

corpus content dropped to save bandwidth, see

https://gitorious.org/jani/notmuch/commit/3aff18b55de46e3b27748376a59cda41b6cd7f6d

 test/corpus/folders/01:2, |  34 
 test/corpus/folders/02:2, |  32 +++
 test/corpus/folders/bar/17:2, |  23 +++
 test/corpus/folders/bar/18:2, |  12 ++
 test/corpus/folders/bar/baz/05:2, | 104 ++
 test/corpus/folders/bar/baz/23:2, | 145 ++
 test/corpus/folders/bar/baz/24:2, | 204 +++
 test/corpus/folders/bar/baz/cur/25:2, |  32 +++
 test/corpus/folders/bar/baz/cur/26:2, | 121 
 test/corpus/folders/bar/baz/new/27:2, |  21 ++
 test/corpus/folders/bar/baz/new/28:2, |  38 
 test/corpus/folders/bar/cur/19:2, | 360 ++
 test/corpus/folders/bar/cur/20:2, | 101 ++
 test/corpus/folders/bar/new/21:2, | 102 ++
 test/corpus/folders/bar/new/22:2, |  84 
 test/corpus/folders/cur/29:2, |  21 ++
 test/corpus/folders/cur/30:2, |  75 +++
 test/corpus/folders/cur/31:2, |  31 +++
 test/corpus/folders/cur/32:2, | 165 
 test/corpus/folders/cur/33:2, |  13 ++
 test/corpus/folders/cur/34:2, |  46 +
 test/corpus/folders/cur/35:2, |  24 +++
 test/corpus/folders/cur/36:2, |  25 +++
 test/corpus/folders/cur/37:2, |  22 +++
 test/corpus/folders/cur/38:2, |  40 
 test/corpus/folders/cur/39:2, |  32 +++
 test/corpus/folders/cur/40:2, |  31 +++
 test/corpus/folders/cur/41:2, |  37 
 test/corpus/folders/cur/42:2, |  30 +++
 test/corpus/folders/cur/43:2, |  26 +++
 test/corpus/folders/cur/44:2, |  29 +++
 test/corpus/folders/cur/45:2, |  41 
 test/corpus/folders/cur/46:2, |  57 ++
 test/corpus/folders/cur/47:2, |  84 
 test/corpus/folders/cur/48:2, |  17 ++
 test/corpus/folders/cur/49:2, |  33 
 test/corpus/folders/cur/50:2, |  39 
 test/corpus/folders/cur/52:2, |  39 
 test/corpus/folders/cur/53:2, |  20 ++
 test/corpus/folders/foo/05:2, | 104 ++
 test/corpus/folders/foo/06:2, |  36 
 test/corpus/folders/foo/baz/11:2, |  27 +++
 test/corpus/folders/foo/baz/12:2, |  27 +++
 test/corpus/folders/foo/baz/cur/13:2, | 178 +
 test/corpus/folders/foo/baz/cur/14:2, |  39 
 test/corpus/folders/foo/baz/new/15:2, |  22 +++
 test/corpus/folders/foo/baz/new/16:2, |  27 +++
 test/corpus/folders/foo/cur/07:2, |  57 ++
 test/corpus/folders/foo/cur/08:2, |  87 
 test/corpus/folders/foo/new/03:2, |  93 +
 test/corpus/folders/foo/new/09:2, |  33 
 test/corpus/folders/foo/new/10:2, |  54 +
 test/corpus/folders/new/03:2, |  93 +
 test/corpus/folders/new/04:2, |  84 
 54 files changed, 3351 insertions(+)
 create mode 100644 test/corpus/folders/01:2,
 create mode 100644 test/corpus/folders/02:2,
 create mode 100644 test/corpus/folders/bar/17:2,
 create mode 100644 test/corpus/folders/bar/18:2,
 create mode 100644 test/corpus/folders/bar/baz/05:2,
 create mode 100644 test/corpus/folders/bar/baz/23:2,
 create mode 100644 test/corpus/folders/bar/baz/24:2,
 create mode 100644 test/corpus/folders/bar/baz/cur/25:2,
 create mode 100644 test/corpus/folders/bar/baz/cur/26:2,
 create mode 100644 test/corpus/folders/bar/baz/new/27:2,
 create mode 100644 test/corpus/folders/bar/baz/new/28:2,
 create mode 100644 test/corpus/folders/bar/cur/19:2,
 create mode 100644 test/corpus/folders/bar/cur/20:2,
 create mode 100644 test/corpus/folders/bar/new/21:2,
 create mode 100644 test/corpus/folders/bar/new/22:2,
 create mode 100644 test/corpus/folders/cur/29:2,
 create mode 100644 test/corpus/folders/cur/30:2,
 create mode 100644 test/corpus/folders/cur/31:2,
 create mode 100644 test/corpus/folders/cur/32:2,
 create mode 100644 test/corpus/folders/cur/33:2,
 create mode 100644 test/corpus/folders/cur/34:2,
 create mode 100644 test/corpus/folders/cur/35:2,
 create mode 100644 test/corpus/folders/cur/36:2,
 create mode 100644 test/corpus/folders/cur/37:2,
 create mode 100644 test/corpus/folders/cur/38:2,
 create mode 100644 test/corpus/folders/cur/39:2,
 create mode 100644 test/corpus/folders/cur/40:2,
 create mode 100644 test/corpus/folders/cur/41:2,
 create mode 100644 test/corpus/folders/cur/42:2,
 create mode 100644 test/corpus/folders/cur/43:2,
 create mode 100644 test/corpus/folders/cur/44:2,
 create mode 100644 test/corpus/folders/cur/45:2,
 create mode 100644 test/corpus/folders/cur/46:2,
 create mode 100644 test/corpus/folders/cur/47:2,
 create mode 100644 test/corpus/folders/cur/48:2,
 create mode 100644 test/corpus/folders/cur/49:2,
 create mode 100644 test/corpus/folders/cur/50:2,
 create mode 100644 test/corpus/folders/cur/52:2,
 create mode 100644 test/corpus/folders/cur/53:2,
 cre

[PATCH v2 06/13] test: make it possible to have several corpora

2014-02-23 Thread Jani Nikula
Move the existing corpus under corpus/default, and make it possible to
have multiple corpora under the directory.
---
 test/.gitignore |  2 +-
 test/Makefile.local |  2 +-
 test/T480-hex-escaping.sh   |  4 ++--
 test/corpus/{ => default}/cur/01:2, |  0
 test/corpus/{ => default}/cur/02:2, |  0
 test/corpus/{ => default}/cur/03:2, |  0
 test/corpus/{ => default}/cur/04:2, |  0
 test/corpus/{ => default}/cur/05:2, |  0
 test/corpus/{ => default}/cur/06:2, |  0
 test/corpus/{ => default}/cur/07:2, |  0
 test/corpus/{ => default}/cur/08:2, |  0
 test/corpus/{ => default}/cur/09:2, |  0
 test/corpus/{ => default}/cur/10:2, |  0
 test/corpus/{ => default}/cur/11:2, |  0
 test/corpus/{ => default}/cur/12:2, |  0
 test/corpus/{ => default}/cur/13:2, |  0
 test/corpus/{ => default}/cur/14:2, |  0
 test/corpus/{ => default}/cur/15:2, |  0
 test/corpus/{ => default}/cur/16:2, |  0
 test/corpus/{ => default}/cur/17:2, |  0
 test/corpus/{ => default}/cur/18:2, |  0
 test/corpus/{ => default}/cur/19:2, |  0
 test/corpus/{ => default}/cur/20:2, |  0
 test/corpus/{ => default}/cur/21:2, |  0
 test/corpus/{ => default}/cur/22:2, |  0
 test/corpus/{ => default}/cur/23:2, |  0
 test/corpus/{ => default}/cur/24:2, |  0
 test/corpus/{ => default}/cur/25:2, |  0
 test/corpus/{ => default}/cur/26:2, |  0
 test/corpus/{ => default}/cur/27:2, |  0
 test/corpus/{ => default}/cur/28:2, |  0
 test/corpus/{ => default}/cur/29:2, |  0
 test/corpus/{ => default}/cur/30:2, |  0
 test/corpus/{ => default}/cur/31:2, |  0
 test/corpus/{ => default}/cur/32:2, |  0
 test/corpus/{ => default}/cur/33:2, |  0
 test/corpus/{ => default}/cur/34:2, |  0
 test/corpus/{ => default}/cur/35:2, |  0
 test/corpus/{ => default}/cur/36:2, |  0
 test/corpus/{ => default}/cur/37:2, |  0
 test/corpus/{ => default}/cur/38:2, |  0
 test/corpus/{ => default}/cur/39:2, |  0
 test/corpus/{ => default}/cur/40:2, |  0
 test/corpus/{ => default}/cur/41:2, |  0
 test/corpus/{ => default}/cur/42:2, |  0
 test/corpus/{ => default}/cur/43:2, |  0
 test/corpus/{ => default}/cur/44:2, |  0
 test/corpus/{ => default}/cur/45:2, |  0
 test/corpus/{ => default}/cur/46:2, |  0
 test/corpus/{ => default}/cur/47:2, |  0
 test/corpus/{ => default}/cur/48:2, |  0
 test/corpus/{ => default}/cur/49:2, |  0
 test/corpus/{ => default}/cur/50:2, |  0
 test/corpus/{ => default}/cur/51:2, |  0
 test/corpus/{ => default}/cur/52:2, |  0
 test/corpus/{ => default}/cur/53:2, |  0
 test/notmuch-test   |  2 +-
 test/test-lib.sh| 21 +
 58 files changed, 18 insertions(+), 13 deletions(-)
 rename test/corpus/{ => default}/cur/01:2, (100%)
 rename test/corpus/{ => default}/cur/02:2, (100%)
 rename test/corpus/{ => default}/cur/03:2, (100%)
 rename test/corpus/{ => default}/cur/04:2, (100%)
 rename test/corpus/{ => default}/cur/05:2, (100%)
 rename test/corpus/{ => default}/cur/06:2, (100%)
 rename test/corpus/{ => default}/cur/07:2, (100%)
 rename test/corpus/{ => default}/cur/08:2, (100%)
 rename test/corpus/{ => default}/cur/09:2, (100%)
 rename test/corpus/{ => default}/cur/10:2, (100%)
 rename test/corpus/{ => default}/cur/11:2, (100%)
 rename test/corpus/{ => default}/cur/12:2, (100%)
 rename test/corpus/{ => default}/cur/13:2, (100%)
 rename test/corpus/{ => default}/cur/14:2, (100%)
 rename test/corpus/{ => default}/cur/15:2, (100%)
 rename test/corpus/{ => default}/cur/16:2, (100%)
 rename test/corpus/{ => default}/cur/17:2, (100%)
 rename test/corpus/{ => default}/cur/18:2, (100%)
 rename test/corpus/{ => default}/cur/19:2, (100%)
 rename test/corpus/{ => default}/cur/20:2, (100%)
 rename test/corpus/{ => default}/cur/21:2, (100%)
 rename test/corpus/{ => default}/cur/22:2, (100%)
 rename test/corpus/{ => default}/cur/23:2, (100%)
 rename test/corpus/{ => default}/cur/24:2, (100%)
 rename test/corpus/{ => default}/cur/25:2, (100%)
 rename test/corpus/{ => default}/cur/26:2, (100%)
 rename test/corpus/{ => default}/cur/27:2, (100%)
 rename test/corpus/{ => default}/cur/28:2, (100%)
 rename test/corpus/{ => default}/cur/29:2, (100%)
 rename test/corpus/{ => default}/cur/30:2, (100%)
 rename test/corpus/{ => default}/cur/31:2, (100%)
 rename test/corpus/{ => default}/cur/32:2, (100%)
 rename test/corpus/{ => default}/cur/33:2, (100%)
 rename test/corpus/{ => default}/cur/34:2, (100%)
 rename test/corpus/{ => default}/cur/35:2, (100%)
 rename test/corpus/{ => default}/cur/36:2, (100%)
 rename test/corpus/{ => default}/cur/37:2, (100%)
 rename test/corpus/{ => default}/cur/38:2, (100%)
 rename test/corpus/{ => default}/cur/39:2, (100%)
 rename test/corpus/{ => default}/cur/40:2, (100%)
 rename test/corpus/{ => default}/cur/41:2, (100%)
 rename test/corpus/{ => default}/cur/42:2, (100%)
 rename test/corpus/{ => default}/cur/43:2, (100%)
 rename test/corpus/{ => default}/cur/44:2, (100%)
 rename test/corpus/{ => default}/cur/45:2, (100%)
 rename test/corpus/{ => default}/cur/46:2, (100%)
 rename te

[PATCH v2 05/13] test: fix test for literal folder: search

2014-02-23 Thread Jani Nikula
Some of the folder: matching capabilities are lost in the
probabilistic to boolean prefix change. Fix them.
---
 test/T100-search-by-folder.sh | 24 +---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/test/T100-search-by-folder.sh b/test/T100-search-by-folder.sh
index 5cc2ca8d388a..84ca43820031 100755
--- a/test/T100-search-by-folder.sh
+++ b/test/T100-search-by-folder.sh
@@ -3,6 +3,7 @@ test_description='"notmuch search" by folder: (with variations)'
 . ./test-lib.sh

 add_message '[dir]=bad' '[subject]="To the bone"'
+add_message '[dir]=.' '[subject]="Top level"'
 add_message '[dir]=bad/news' '[subject]="Bears"'
 mkdir -p "${MAIL_DIR}/duplicate/bad/news"
 cp "$gen_msg_filename" "${MAIL_DIR}/duplicate/bad/news"
@@ -12,29 +13,46 @@ add_message '[dir]=things/favorite' '[subject]="Raindrops, 
whiskers, kettles"'
 add_message '[dir]=things/bad' '[subject]="Bites, stings, sad feelings"'

 test_begin_subtest "Single-world folder: specification (multiple results)"
-output=$(notmuch search folder:bad | notmuch_search_sanitize)
+output=$(notmuch search folder:bad folder:bad/news folder:things/bad | 
notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; 
To the bone (inbox unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings 
(inbox unread)"

+test_begin_subtest "Top level folder"
+output=$(notmuch search folder:'""' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; 
Top level (inbox unread)"
+
 test_begin_subtest "Two-word path to narrow results to one"
 output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; 
Bears (inbox unread)"

+test_begin_subtest "Folder search with --output=files"
+output=$(notmuch search --output=files folder:bad/news | sed -e 
"s,$MAIL_DIR,MAIL_DIR,")
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-003
+MAIL_DIR/duplicate/bad/news/msg-003"
+
 test_begin_subtest "After removing duplicate instance of matching path"
 rm -r "${MAIL_DIR}/bad/news"
 notmuch new
 output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
+test_expect_equal "$output" ""
+
+test_begin_subtest "Folder search with --output=files part #2"
+output=$(notmuch search --output=files folder:duplicate/bad/news | sed -e 
"s,$MAIL_DIR,MAIL_DIR,")
+test_expect_equal "$output" "MAIL_DIR/duplicate/bad/news/msg-003"
+
+test_begin_subtest "After removing duplicate instance of matching path part #2"
+output=$(notmuch search folder:duplicate/bad/news | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; 
Bears (inbox unread)"

 test_begin_subtest "After rename, old path returns nothing"
 mv "${MAIL_DIR}/duplicate/bad/news" "${MAIL_DIR}/duplicate/bad/olds"
 notmuch new
-output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
+output=$(notmuch search folder:duplicate/bad/news | notmuch_search_sanitize)
 test_expect_equal "$output" ""

 test_begin_subtest "After rename, new path returns result"
-output=$(notmuch search folder:bad/olds | notmuch_search_sanitize)
+output=$(notmuch search folder:duplicate/bad/olds | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; 
Bears (inbox unread)"

 test_done
-- 
1.8.5.3



[PATCH v2 04/13] lib: make folder: prefix literal

2014-02-23 Thread Jani Nikula
In xapian terms, convert folder: prefix from probabilistic to boolean
prefix, matching the paths, relative form the maildir root, of the
message files, ignoring the maildir new and cur leaf directories.

folder:foo matches all message files in foo, foo/new, and foo/cur.

folder:foo/new does *not* match message files in foo/new.

folder:"" matches all message files in the top level maildir and its
new and cur subdirectories.

This change constitutes a database change: bump the database version
and add database upgrade support for folder: terms. The upgrade also
adds path: terms.
---
 lib/database.cc   | 38 ++--
 lib/message.cc| 80 ---
 lib/notmuch-private.h |  3 ++
 3 files changed, 108 insertions(+), 13 deletions(-)

diff --git a/lib/database.cc b/lib/database.cc
index 93cc7f57e9db..186e3a7976fe 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -42,7 +42,7 @@ typedef struct {
 const char *prefix;
 } prefix_t;

-#define NOTMUCH_DATABASE_VERSION 1
+#define NOTMUCH_DATABASE_VERSION 2

 #define STRINGIFY(s) _SUB_STRINGIFY(s)
 #define _SUB_STRINGIFY(s) #s
@@ -210,6 +210,7 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
 { "is","K" },
 { "id","Q" },
 { "path",  "P" },
+{ "folder","XFOLDER:" },
 };

 static prefix_t PROBABILISTIC_PREFIX[]= {
@@ -217,7 +218,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
 { "to","XTO" },
 { "attachment","XATTACHMENT" },
 { "subject",   "XSUBJECT"},
-{ "folder","XFOLDER"}
 };

 const char *
@@ -1168,6 +1168,40 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
}
 }

+/*
+ * Prior to version 2, the "folder:" prefix was probabilistic and
+ * stemmed. Change it to the current boolean prefix. Add "path:"
+ * prefixes while at it.
+ */
+if (version < 2) {
+   notmuch_query_t *query = notmuch_query_create (notmuch, "");
+   notmuch_messages_t *messages;
+   notmuch_message_t *message;
+
+   count = 0;
+   total = notmuch_query_count_messages (query);
+
+   for (messages = notmuch_query_search_messages (query);
+notmuch_messages_valid (messages);
+notmuch_messages_move_to_next (messages)) {
+   if (do_progress_notify) {
+   progress_notify (closure, (double) count / total);
+   do_progress_notify = 0;
+   }
+
+   message = notmuch_messages_get (messages);
+
+   _notmuch_message_upgrade_folder (message);
+   _notmuch_message_sync (message);
+
+   notmuch_message_destroy (message);
+
+   count++;
+   }
+
+   notmuch_query_destroy (query);
+}
+
 db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));
 db->flush ();

diff --git a/lib/message.cc b/lib/message.cc
index 21abe8e12b9d..31cb9f107dd7 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -504,6 +504,56 @@ _notmuch_message_remove_terms (notmuch_message_t *message, 
const char *prefix)
 }
 }

+/* Return true if p points at "new" or "cur". */
+static bool is_maildir (const char *p)
+{
+return strcmp (p, "cur") == 0 || strcmp (p, "new") == 0;
+}
+
+/* Add "folder:" term for directory. */
+static notmuch_status_t
+_notmuch_message_add_folder_terms (notmuch_message_t *message,
+  const char *directory)
+{
+char *folder, *last;
+
+folder = talloc_strdup (NULL, directory);
+if (! folder)
+   return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+/*
+ * If the message file is in a leaf directory named "new" or
+ * "cur", presume maildir and index the parent directory. Thus a
+ * "folder:" prefix search matches messages in the specified
+ * maildir folder, i.e. in the specified directory and its "new"
+ * and "cur" subdirectories.
+ *
+ * Note that this means the "folder:" prefix can't be used for
+ * distinguishing between message files in "new" or "cur". The
+ * "path:" prefix needs to be used for that.
+ *
+ * Note the deliberate difference to _filename_is_in_maildir(). We
+ * don't want to index different things depending on the existence
+ * or non-existence of all maildir sibling directories "new",
+ * "cur", and "tmp". Doing so would be surprising, and difficult
+ * for the user to fix in case all subdirectories were not in
+ * place during indexing.
+ */
+last = strrchr (folder, '/');
+if (last) {
+   if (is_maildir (last + 1))
+   *last = '\0';
+} else if (is_maildir (folder)) {
+   *folder = '\0';
+}
+
+_notmuch_message_add_term (message, "folder", folder);
+
+talloc_free (folder);
+
+return NOTMUCH_STATUS_SUCCESS;
+}
+
 #define RECURSIVE_SUFFIX "/**"

 /* Add "path:" terms for directory. */
@@ -570,9 +620,8 @@ _notmuch

[PATCH v2 03/13] test: make insert test use the path: prefix

2014-02-23 Thread Jani Nikula
This is a more strict test for the insert test.
---
 test/T070-insert.sh | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/test/T070-insert.sh b/test/T070-insert.sh
index e8dc4c099ed1..5de5da43cebb 100755
--- a/test/T070-insert.sh
+++ b/test/T070-insert.sh
@@ -126,14 +126,14 @@ test_expect_equal "$dirname" "$MAIL_DIR/new"
 test_begin_subtest "Insert message into folder"
 gen_insert_msg
 notmuch insert --folder=Drafts < "$gen_msg_filename"
-output=$(notmuch search --output=files folder:Drafts)
+output=$(notmuch search --output=files path:Drafts/new)
 dirname=$(dirname "$output")
 test_expect_equal "$dirname" "$MAIL_DIR/Drafts/new"

 test_begin_subtest "Insert message into folder, add/remove tags"
 gen_insert_msg
 notmuch insert --folder=Drafts +draft -unread < "$gen_msg_filename"
-output=$(notmuch search --output=messages folder:Drafts tag:draft NOT 
tag:unread)
+output=$(notmuch search --output=messages path:Drafts/cur tag:draft NOT 
tag:unread)
 test_expect_equal "$output" "id:$gen_msg_id"

 gen_insert_msg
@@ -143,21 +143,21 @@ test_expect_code 1 "Insert message into non-existent 
folder" \
 test_begin_subtest "Insert message, create folder"
 gen_insert_msg
 notmuch insert --folder=F --create-folder +folder < "$gen_msg_filename"
-output=$(notmuch search --output=files folder:F tag:folder)
+output=$(notmuch search --output=files path:F/new tag:folder)
 basename=$(basename "$output")
 test_expect_equal_file "$gen_msg_filename" "$MAIL_DIR/F/new/${basename}"

 test_begin_subtest "Insert message, create subfolder"
 gen_insert_msg
 notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
-output=$(notmuch search --output=files folder:F/G/H/I/J tag:folder)
+output=$(notmuch search --output=files path:F/G/H/I/J/new tag:folder)
 basename=$(basename "$output")
 test_expect_equal_file "$gen_msg_filename" 
"${MAIL_DIR}/F/G/H/I/J/new/${basename}"

 test_begin_subtest "Insert message, create existing subfolder"
 gen_insert_msg
 notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
-output=$(notmuch count folder:F/G/H/I/J tag:folder)
+output=$(notmuch count path:F/G/H/I/J/new tag:folder)
 test_expect_equal "$output" "2"

 gen_insert_msg
-- 
1.8.5.3



[PATCH v2 02/13] lib: add support for path: prefix searches

2014-02-23 Thread Jani Nikula
The path: prefix is a literal boolean prefix matching the paths,
relative from the maildir root, of the message files.

path:foo matches all message files in foo (but not in foo/new or
foo/cur).

path:foo/new matches all message files in foo/new.

path:"" matches all message files in the top level maildir.

path:foo/** matches all message files in foo and recursively in all
subdirectories of foo.

path:** matches all message files recursively, i.e. all messages.
---
 lib/database.cc |  7 ---
 lib/message.cc  | 52 +---
 2 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/lib/database.cc b/lib/database.cc
index f395061e3a73..93cc7f57e9db 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -100,8 +100,8 @@ typedef struct {
  * In addition, terms from the content of the message are added with
  * "from", "to", "attachment", and "subject" prefixes for use by the
  * user in searching. Similarly, terms from the path of the mail
- * message are added with a "folder" prefix. But the database doesn't
- * really care itself about any of these.
+ * message are added with "folder" and "path" prefixes. But the
+ * database doesn't really care itself about any of these.
  *
  * The data portion of a mail document is empty.
  *
@@ -208,7 +208,8 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
 { "thread","G" },
 { "tag",   "K" },
 { "is","K" },
-{ "id","Q" }
+{ "id","Q" },
+{ "path",  "P" },
 };

 static prefix_t PROBABILISTIC_PREFIX[]= {
diff --git a/lib/message.cc b/lib/message.cc
index 7aff4ae5111a..21abe8e12b9d 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -504,6 +504,40 @@ _notmuch_message_remove_terms (notmuch_message_t *message, 
const char *prefix)
 }
 }

+#define RECURSIVE_SUFFIX "/**"
+
+/* Add "path:" terms for directory. */
+static notmuch_status_t
+_notmuch_message_add_path_terms (notmuch_message_t *message,
+const char *directory)
+{
+/* Add exact "path:" term. */
+_notmuch_message_add_term (message, "path", directory);
+
+if (strlen (directory)) {
+   char *path, *p;
+
+   path = talloc_asprintf (NULL, "%s%s", directory, RECURSIVE_SUFFIX);
+   if (! path)
+   return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+   /* Add recursive "path:" terms for directory and all parents. */
+   for (p = path + strlen (path) - 1; p > path; p--) {
+   if (*p == '/') {
+   strcpy (p, RECURSIVE_SUFFIX);
+   _notmuch_message_add_term (message, "path", path);
+   }
+   }
+
+   talloc_free (path);
+}
+
+/* Recursive all-matching path:** for consistency. */
+_notmuch_message_add_term (message, "path", "**");
+
+return NOTMUCH_STATUS_SUCCESS;
+}
+
 /* Add directory based terms for all filenames of the message. */
 static notmuch_status_t
 _notmuch_message_add_directory_terms (void *ctx, notmuch_message_t *message)
@@ -538,6 +572,8 @@ _notmuch_message_add_directory_terms (void *ctx, 
notmuch_message_t *message)
  directory_id);
if (strlen (directory))
_notmuch_message_gen_terms (message, "folder", directory);
+
+   _notmuch_message_add_path_terms (message, directory);
 }

 return status;
@@ -577,6 +613,8 @@ _notmuch_message_add_filename (notmuch_message_t *message,
 /* New terms allow user to search with folder: specification. */
 _notmuch_message_gen_terms (message, "folder", directory);

+_notmuch_message_add_path_terms (message, directory);
+
 talloc_free (local);

 return NOTMUCH_STATUS_SUCCESS;
@@ -618,18 +656,18 @@ _notmuch_message_remove_filename (notmuch_message_t 
*message,
 if (status)
return status;

-/* Re-synchronize "folder:" terms for this message. This requires:
- *  1. removing all "folder:" terms
- *  2. removing all "folder:" stemmed terms
- *  3. adding back terms for all remaining filenames of the message. */
+/* Re-synchronize "folder:" and "path:" terms for this message. */

-/* 1. removing all "folder:" terms */
+/* Remove all "folder:" terms. */
 _notmuch_message_remove_terms (message, folder_prefix);

-/* 2. removing all "folder:" stemmed terms */
+/* Remove all "folder:" stemmed terms. */
 _notmuch_message_remove_terms (message, zfolder_prefix);

-/* 3. adding back terms for all remaining filenames of the message. */
+/* Remove all "path:" terms. */
+_notmuch_message_remove_terms (message, _find_prefix ("path"));
+
+/* Add back terms for all remaining filenames of the message. */
 status = _notmuch_message_add_directory_terms (local, message);

 talloc_free (local);
-- 
1.8.5.3



[PATCH v2 01/13] lib: refactor folder term update after filename removal

2014-02-23 Thread Jani Nikula
Abstract some blocks of code for reuse. No functional changes.
---
 lib/message.cc | 135 -
 1 file changed, 66 insertions(+), 69 deletions(-)

diff --git a/lib/message.cc b/lib/message.cc
index c91f3a59836f..7aff4ae5111a 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -481,6 +481,68 @@ notmuch_message_get_replies (notmuch_message_t *message)
 return _notmuch_messages_create (message->replies);
 }

+static void
+_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
+{
+Xapian::TermIterator i;
+size_t prefix_len = strlen (prefix);
+
+while (1) {
+   i = message->doc.termlist_begin ();
+   i.skip_to (prefix);
+
+   /* Terminate loop when no terms remain with desired prefix. */
+   if (i == message->doc.termlist_end () ||
+   strncmp ((*i).c_str (), prefix, prefix_len))
+   break;
+
+   try {
+   message->doc.remove_term ((*i));
+   } catch (const Xapian::InvalidArgumentError) {
+   /* Ignore failure to remove non-existent term. */
+   }
+}
+}
+
+/* Add directory based terms for all filenames of the message. */
+static notmuch_status_t
+_notmuch_message_add_directory_terms (void *ctx, notmuch_message_t *message)
+{
+const char *direntry_prefix = _find_prefix ("file-direntry");
+int direntry_prefix_len = strlen (direntry_prefix);
+Xapian::TermIterator i = message->doc.termlist_begin ();
+notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+for (i.skip_to (direntry_prefix); i != message->doc.termlist_end (); i++) {
+   unsigned int directory_id;
+   const char *direntry, *directory;
+   char *colon;
+
+   /* Terminate loop at first term without desired prefix. */
+   if (strncmp ((*i).c_str (), direntry_prefix, direntry_prefix_len))
+   break;
+
+   /* Indicate that there are filenames remaining. */
+   status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
+
+   direntry = (*i).c_str ();
+   direntry += direntry_prefix_len;
+
+   directory_id = strtol (direntry, &colon, 10);
+
+   if (colon == NULL || *colon != ':')
+   INTERNAL_ERROR ("malformed direntry");
+
+   directory = _notmuch_database_get_directory_path (ctx,
+ message->notmuch,
+ directory_id);
+   if (strlen (directory))
+   _notmuch_message_gen_terms (message, "folder", directory);
+}
+
+return status;
+}
+
 /* Add an additional 'filename' for 'message'.
  *
  * This change will not be reflected in the database until the next
@@ -536,17 +598,12 @@ notmuch_status_t
 _notmuch_message_remove_filename (notmuch_message_t *message,
  const char *filename)
 {
-const char *direntry_prefix = _find_prefix ("file-direntry");
-int direntry_prefix_len = strlen (direntry_prefix);
-const char *folder_prefix = _find_prefix ("folder");
-int folder_prefix_len = strlen (folder_prefix);
 void *local = talloc_new (message);
+const char *folder_prefix = _find_prefix ("folder");
 char *zfolder_prefix = talloc_asprintf(local, "Z%s", folder_prefix);
-int zfolder_prefix_len = strlen (zfolder_prefix);
 char *direntry;
 notmuch_private_status_t private_status;
 notmuch_status_t status;
-Xapian::TermIterator i, last;

 status = _notmuch_database_filename_to_direntry (
local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
@@ -567,73 +624,13 @@ _notmuch_message_remove_filename (notmuch_message_t 
*message,
  *  3. adding back terms for all remaining filenames of the message. */

 /* 1. removing all "folder:" terms */
-while (1) {
-   i = message->doc.termlist_begin ();
-   i.skip_to (folder_prefix);
-
-   /* Terminate loop when no terms remain with desired prefix. */
-   if (i == message->doc.termlist_end () ||
-   strncmp ((*i).c_str (), folder_prefix, folder_prefix_len))
-   {
-   break;
-   }
-
-   try {
-   message->doc.remove_term ((*i));
-   } catch (const Xapian::InvalidArgumentError) {
-   /* Ignore failure to remove non-existent term. */
-   }
-}
+_notmuch_message_remove_terms (message, folder_prefix);

 /* 2. removing all "folder:" stemmed terms */
-while (1) {
-   i = message->doc.termlist_begin ();
-   i.skip_to (zfolder_prefix);
-
-   /* Terminate loop when no terms remain with desired prefix. */
-   if (i == message->doc.termlist_end () ||
-   strncmp ((*i).c_str (), zfolder_prefix, zfolder_prefix_len))
-   {
-   break;
-   }
-
-   try {
-   message->doc.remove_term ((*i));
-   } catch (const Xapian::InvalidArgumentError) {
-   /* Ignore failure to remove non-existent term. */
-   }
-}
+_notmuch_message_remove_terms (message, zf

[PATCH v2 00/13] literal folder: prefix, new path: prefix

2014-02-23 Thread Jani Nikula
Hi all, this is v2 of id:cover.1389304779.git.jani at nikula.org.

The new path: prefix is a literal boolean prefix matching the paths,
relative from the maildir root, of the message files. There's no
interpretation of the maildir special cur/new folders, but a recursive
match is provided with "/**" suffix. See the patch for details.

The folder: prefix becomes a literal boolean prefix, similar to path:,
except it matches the maildir cur/new folders in addition to the
specified path. There's no recursive version.

Patches 1-5 add the above.

Patches 6-8 change the test infrastructure to make it easier to add
multiple corpuses, and adds a new test for path: and folder:.

Patches 9-11 add support for testing the database upgrade.

Patches 12-13 update man pages.


I've dropped most of the content in patches 7 and 10 due to their
size. The patches (and the whole series) are available in the
boolean-folder-and-path-v2 branch at
git://gitorious.org/jani/notmuch.git. Web interface at
https://gitorious.org/jani/notmuch/commits/0b3dd2d1cc6c413ea07ea326883ac448499c0e79.


WARNING! The change requires a database format version bump, and a
database upgrade, which is automatically done on 'notmuch new'. The
upgrade is irreversible if you want to try this on your database! A
complete database rebuild is required for reverting the database format
version. Make sure your backups are in order!


BR,
Jani.


Jani Nikula (13):
  lib: refactor folder term update after filename removal
  lib: add support for path: prefix searches
  test: make insert test use the path: prefix
  lib: make folder: prefix literal
  test: fix test for literal folder: search
  test: make it possible to have several corpora
  test: add new corpus with folders
  test: add tests for the new boolean folder: and path: prefixes
  devel: add script to generate test databases
  test: add test database in format version 1
  test: add database upgrade test from format version 1 to 2
  man: update man pages for folder: and path: search terms
  man: try to clarify the folder: and path: vs. --output=files confusion

 devel/gen-testdb.sh| 124 
 lib/database.cc|  45 -
 lib/message.cc | 249 ---
 lib/notmuch-private.h  |   3 +
 man/man1/notmuch-search.1  |  10 +-
 man/man7/notmuch-search-terms.7|  28 ++-
 test/.gitignore|   2 +-
 test/Makefile.local|   2 +-
 test/T070-insert.sh|  10 +-
 test/T100-search-by-folder.sh  |  24 ++-
 test/T101-search-by-folder-and-path.sh |  83 
 test/T480-hex-escaping.sh  |   4 +-
 test/T530-upgrade.sh   | 103 ++
 test/corpus/{ => default}/cur/01:2,|   0
 test/corpus/{ => default}/cur/02:2,|   0
 test/corpus/{ => default}/cur/03:2,|   0
 test/corpus/{ => default}/cur/04:2,|   0
 test/corpus/{ => default}/cur/05:2,|   0
 test/corpus/{ => default}/cur/06:2,|   0
 test/corpus/{ => default}/cur/07:2,|   0
 test/corpus/{ => default}/cur/08:2,|   0
 test/corpus/{ => default}/cur/09:2,|   0
 test/corpus/{ => default}/cur/10:2,|   0
 test/corpus/{ => default}/cur/11:2,|   0
 test/corpus/{ => default}/cur/12:2,|   0
 test/corpus/{ => default}/cur/13:2,|   0
 test/corpus/{ => default}/cur/14:2,|   0
 test/corpus/{ => default}/cur/15:2,|   0
 test/corpus/{ => default}/cur/16:2,|   0
 test/corpus/{ => default}/cur/17:2,|   0
 test/corpus/{ => default}/cur/18:2,|   0
 test/corpus/{ => default}/cur/19:2,|   0
 test/corpus/{ => default}/cur/20:2,|   0
 test/corpus/{ => default}/cur/21:2,|   0
 test/corpus/{ => default}/cur/22:2,|   0
 test/corpus/{ => default}/cur/23:2,|   0
 test/corpus/{ => default}/cur/24:2,|   0
 test/corpus/{ => default}/cur/25:2,|   0
 test/corpus/{ => default}/cur/26:2,|   0
 test/corpus/{ => default}/cur/27:2,|   0
 test/corpus/{ => default}/cur/28:2,|   0
 test/corpus/{ => default}/cur/29:2,|   0
 test/corpus/{ => default}/cur/30:2,|   0
 test/corpus/{ => default}/cur/31:2,|   0
 test/corpus/{ => default}/cur/32:2,|   0
 test/corpus/{ => default}/cur/33:2,|   0
 test/corpus/{ => default}/cur/34:2,|   0
 test/corpus/{ => default}/cur/35:2,|   0
 test/corpus/{ => default}/cur/36:2,|   0
 test/corpus/{ => default}/cur/37:2,|   0
 test/corpus/{ => default}/cur/38:2,|   0
 test/corpus/{ => default}/cur/39:2,|   0
 test/corpus/{ => default}/cur/40:2,|   0
 test/corpus/{ => default}/cur/41:2,|   0
 test/corpus/{ => default}/cur/42:2,|   0
 test/corpus/{ => default}/cur/43:2,|   0
 test/corpus/{ => default}/cur/44:2,|   0
 test/corpus/{ => default}/cur/45:2,|   0
 test/corpus/{ => default}/cur/46:2,|   0
 test/corpus/{ => default}/cur/47:2,|   0
 test/corpus/{ => default}/cur/48:2,|   0
 test/c