unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
@ 2011-10-23 15:18 David Bremner
  2011-10-23 21:51 ` [PATCH] xregcomp: don't consider every regex compilation failure an internal error David Bremner
                   ` (4 more replies)
  0 siblings, 5 replies; 35+ messages in thread
From: David Bremner @ 2011-10-23 15:18 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

We keep the lib/xutil.c version. As a consequence, also factor out
_internal_error and associated macros.  It might be overkill to make a
new file error_util.c for this, but _internal_error does not really
belong in database.cc.
---

This turned out to be more disruptive than I thought. On the other
hand, having two copies of xutil.c seems like a recipe for disaster.
I wanted to factor out the logic in xregcomp so I could use it in
situations where miscompilation is not an internal error, but more
likely a user error.

 Makefile              |    2 +-
 Makefile.local        |    7 +--
 lib/Makefile.local    |    5 +-
 lib/database.cc       |   15 -----
 lib/notmuch-private.h |   20 +-------
 lib/xutil.c           |  134 -----------------------------------------------
 lib/xutil.h           |   55 -------------------
 util/Makefile         |    5 ++
 util/Makefile.local   |   11 ++++
 util/error_util.c     |   41 +++++++++++++++
 util/error_util.h     |   45 ++++++++++++++++
 util/xutil.c          |  136 ++++++++++++++++++++++++++++++++++++++++++++++++
 util/xutil.h          |   55 +++++++++++++++++++
 xutil.c               |  138 -------------------------------------------------
 14 files changed, 300 insertions(+), 369 deletions(-)
 delete mode 100644 lib/xutil.c
 delete mode 100644 lib/xutil.h
 create mode 100644 util/Makefile
 create mode 100644 util/Makefile.local
 create mode 100644 util/error_util.c
 create mode 100644 util/error_util.h
 create mode 100644 util/xutil.c
 create mode 100644 util/xutil.h
 delete mode 100644 xutil.c

diff --git a/Makefile b/Makefile
index 11e3a3d..2fb2a61 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 all:
 
 # List all subdirectories here. Each contains its own Makefile.local
-subdirs = compat completion emacs lib test
+subdirs = compat completion emacs lib util test
 
 # We make all targets depend on the Makefiles themselves.
 global_deps = Makefile Makefile.config Makefile.local \
diff --git a/Makefile.local b/Makefile.local
index 38f6c17..e31defa 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -39,7 +39,7 @@ PV_FILE=bindings/python/notmuch/version.py
 # Smash together user's values with our extra values
 FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
 FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS) $(extra_cflags) $(extra_cxxflags)
-FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
+FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
 FINAL_NOTMUCH_LINKER = CC
 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
 FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
@@ -288,12 +288,11 @@ notmuch_client_srcs =		\
 	notmuch-time.c		\
 	query-string.c		\
 	show-message.c		\
-	json.c			\
-	xutil.c
+	json.c			
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
 
-notmuch: $(notmuch_client_modules) lib/libnotmuch.a
+notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a
 	$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
 
 notmuch-shared: $(notmuch_client_modules) lib/$(LINKER_NAME)
diff --git a/lib/Makefile.local b/lib/Makefile.local
index ea20b2b..f148661 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -49,8 +49,7 @@ libnotmuch_c_srcs =		\
 	$(dir)/message-file.c	\
 	$(dir)/messages.c	\
 	$(dir)/sha1.c		\
-	$(dir)/tags.c		\
-	$(dir)/xutil.c
+	$(dir)/tags.c		
 
 libnotmuch_cxx_srcs =		\
 	$(dir)/database.cc	\
@@ -66,7 +65,7 @@ $(dir)/libnotmuch.a: $(libnotmuch_modules)
 	$(call quiet,AR) rcs $@ $^
 
 $(dir)/$(LIBNAME): $(libnotmuch_modules) notmuch.sym
-	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@
+	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@ -L$(srcdir)/util -lutil
 
 notmuch.sym: lib/notmuch.h $(libnotmuch_modules)
 	sh lib/gen-version-script.sh $< $(libnotmuch_modules) > $@
diff --git a/lib/database.cc b/lib/database.cc
index e77fd53..88be939 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -209,21 +209,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
     { "folder",			"XFOLDER"}
 };
 
-int
-_internal_error (const char *format, ...)
-{
-    va_list va_args;
-
-    va_start (va_args, format);
-
-    fprintf (stderr, "Internal error: ");
-    vfprintf (stderr, format, va_args);
-
-    exit (1);
-
-    return 1;
-}
-
 const char *
 _find_prefix (const char *name)
 {
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index d319530..0d3cc27 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -47,6 +47,7 @@ NOTMUCH_BEGIN_DECLS
 #include <talloc.h>
 
 #include "xutil.h"
+#include "error_util.h"
 
 #pragma GCC visibility push(hidden)
 
@@ -60,25 +61,6 @@ NOTMUCH_BEGIN_DECLS
 #define STRNCMP_LITERAL(var, literal) \
     strncmp ((var), (literal), sizeof (literal) - 1)
 
-/* There's no point in continuing when we've detected that we've done
- * something wrong internally (as opposed to the user passing in a
- * bogus value).
- *
- * Note that PRINTF_ATTRIBUTE comes from talloc.h
- */
-int
-_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
-
-/* There's no point in continuing when we've detected that we've done
- * something wrong internally (as opposed to the user passing in a
- * bogus value).
- *
- * Note that __location__ comes from talloc.h.
- */
-#define INTERNAL_ERROR(format, ...)			\
-    _internal_error (format " (%s).\n",			\
-		     ##__VA_ARGS__, __location__)
-
 #define unused(x) x __attribute__ ((unused))
 
 #ifdef __cplusplus
diff --git a/lib/xutil.c b/lib/xutil.c
deleted file mode 100644
index 268225b..0000000
diff --git a/lib/xutil.h b/lib/xutil.h
deleted file mode 100644
index fd77f73..0000000
diff --git a/util/Makefile b/util/Makefile
new file mode 100644
index 0000000..fa25832
--- /dev/null
+++ b/util/Makefile
@@ -0,0 +1,5 @@
+all:
+	$(MAKE) -C .. all
+
+.DEFAULT:
+	$(MAKE) -C .. $@
diff --git a/util/Makefile.local b/util/Makefile.local
new file mode 100644
index 0000000..2ff42b3
--- /dev/null
+++ b/util/Makefile.local
@@ -0,0 +1,11 @@
+# -*- makefile -*-
+
+dir := util
+extra_cflags += -I$(srcdir)/$(dir)
+
+libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c
+
+libutil_modules := $(libutil_c_srcs:.c=.o)
+
+$(dir)/libutil.a: $(libutil_modules)
+	$(call quiet,AR) rcs $@ $^
diff --git a/util/error_util.c b/util/error_util.c
new file mode 100644
index 0000000..630d228
--- /dev/null
+++ b/util/error_util.c
@@ -0,0 +1,41 @@
+/* error_util.c - internal error utilities for notmuch.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "error_util.h"
+
+int
+_internal_error (const char *format, ...)
+{
+    va_list va_args;
+
+    va_start (va_args, format);
+
+    fprintf (stderr, "Internal error: ");
+    vfprintf (stderr, format, va_args);
+
+    exit (1);
+
+    return 1;
+}
+
diff --git a/util/error_util.h b/util/error_util.h
new file mode 100644
index 0000000..0f1e5ef
--- /dev/null
+++ b/util/error_util.h
@@ -0,0 +1,45 @@
+/* error_util.h - Internal interfaces for notmuch.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef ERROR_UTIL_H
+#define ERROR_UTIL_H
+
+#include <talloc.h>
+
+/* There's no point in continuing when we've detected that we've done
+ * something wrong internally (as opposed to the user passing in a
+ * bogus value).
+ *
+ * Note that PRINTF_ATTRIBUTE comes from talloc.h
+ */
+int
+_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
+
+/* There's no point in continuing when we've detected that we've done
+ * something wrong internally (as opposed to the user passing in a
+ * bogus value).
+ *
+ * Note that __location__ comes from talloc.h.
+ */
+#define INTERNAL_ERROR(format, ...)			\
+    _internal_error (format " (%s).\n",			\
+		     ##__VA_ARGS__, __location__)
+
+#endif
diff --git a/util/xutil.c b/util/xutil.c
new file mode 100644
index 0000000..15ff765
--- /dev/null
+++ b/util/xutil.c
@@ -0,0 +1,136 @@
+/* xutil.c - Various wrapper functions to abort on error.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "xutil.h"
+#include "error_util.h"
+
+void *
+xcalloc (size_t nmemb, size_t size)
+{
+    void *ret;
+
+    ret = calloc (nmemb, size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xmalloc (size_t size)
+{
+    void *ret;
+
+    ret = malloc (size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xrealloc (void *ptr, size_t size)
+{
+    void *ret;
+
+    ret = realloc (ptr, size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrdup (const char *s)
+{
+    char *ret;
+
+    ret = strdup (s);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrndup (const char *s, size_t n)
+{
+    char *ret;
+
+    if (strlen (s) <= n)
+	n = strlen (s);
+
+    ret = malloc (n + 1);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+    memcpy (ret, s, n);
+    ret[n] = '\0';
+
+    return ret;
+}
+
+void
+xregcomp (regex_t *preg, const char *regex, int cflags)
+{
+    int rerr;
+
+    rerr = regcomp (preg, regex, cflags);
+    if (rerr) {
+	size_t error_size = regerror (rerr, preg, NULL, 0);
+	char *error = xmalloc (error_size);
+
+	regerror (rerr, preg, error, error_size);
+	INTERNAL_ERROR ("compiling regex %s: %s\n",
+			regex, error);
+    }
+}
+
+int
+xregexec (const regex_t *preg, const char *string,
+	  size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+    unsigned int i;
+    int rerr;
+
+    rerr = regexec (preg, string, nmatch, pmatch, eflags);
+    if (rerr)
+	return rerr;
+
+    for (i = 0; i < nmatch; i++) {
+	if (pmatch[i].rm_so == -1)
+	    INTERNAL_ERROR ("matching regex against %s: Sub-match %d not found\n",
+			    string, i);
+    }
+
+    return 0;
+}
diff --git a/util/xutil.h b/util/xutil.h
new file mode 100644
index 0000000..fd77f73
--- /dev/null
+++ b/util/xutil.h
@@ -0,0 +1,55 @@
+/* xutil.h - Various wrapper functions to abort on error.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef NOTMUCH_XUTIL_H
+#define NOTMUCH_XUTIL_H
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#pragma GCC visibility push(hidden)
+
+/* xutil.c */
+void *
+xcalloc (size_t nmemb, size_t size);
+
+void *
+xmalloc (size_t size);
+
+void *
+xrealloc (void *ptrr, size_t size);
+
+char *
+xstrdup (const char *s);
+
+char *
+xstrndup (const char *s, size_t n);
+
+void
+xregcomp (regex_t *preg, const char *regex, int cflags);
+
+int
+xregexec (const regex_t *preg, const char *string,
+	  size_t nmatch, regmatch_t pmatch[], int eflags);
+
+#pragma GCC visibility pop
+
+#endif
diff --git a/xutil.c b/xutil.c
deleted file mode 100644
index 5f98f3f..0000000
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH] xregcomp: don't consider every regex compilation failure an internal error.
  2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
@ 2011-10-23 21:51 ` David Bremner
  2011-10-29  3:04   ` David Bremner
  2011-10-29 18:52 ` [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 35+ messages in thread
From: David Bremner @ 2011-10-23 21:51 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

This pushes the error handling up one step, but makes the function
more flexible. Running out of memory still triggers an internal error,
in the spirit of other xutils functions.
---

And here is the promised modification of xregcomp.  One issue I
thought about is that we now have a(nother) place where a library
routine is writing to stderr, not necessarily in the process of
shutting down.

 notmuch-restore.c |    7 ++++---
 util/xutil.c      |    7 +++++--
 util/xutil.h      |    3 ++-
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index ff1ebab..13b4325 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -88,9 +88,10 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
      * non-space characters for the message-id, then one or more
      * spaces, then a list of space-separated tags as a sequence of
      * characters within literal '(' and ')'. */
-    xregcomp (&regex,
-	      "^([^ ]+) \\(([^)]*)\\)$",
-	      REG_EXTENDED);
+    if ( xregcomp (&regex,
+		   "^([^ ]+) \\(([^)]*)\\)$",
+		   REG_EXTENDED) )
+	INTERNAL_ERROR("compile time constant regex failed.");
 
     while ((line_len = getline (&line, &line_size, input)) != -1) {
 	regmatch_t match[3];
diff --git a/util/xutil.c b/util/xutil.c
index 15ff765..ac496da 100644
--- a/util/xutil.c
+++ b/util/xutil.c
@@ -99,7 +99,7 @@ xstrndup (const char *s, size_t n)
     return ret;
 }
 
-void
+int
 xregcomp (regex_t *preg, const char *regex, int cflags)
 {
     int rerr;
@@ -110,9 +110,12 @@ xregcomp (regex_t *preg, const char *regex, int cflags)
 	char *error = xmalloc (error_size);
 
 	regerror (rerr, preg, error, error_size);
-	INTERNAL_ERROR ("compiling regex %s: %s\n",
+	fprintf (stderr, "compiling regex %s: %s\n",
 			regex, error);
+	free (error);
+	return 1;
     }
+    return 0;
 }
 
 int
diff --git a/util/xutil.h b/util/xutil.h
index fd77f73..9299256 100644
--- a/util/xutil.h
+++ b/util/xutil.h
@@ -43,7 +43,8 @@ xstrdup (const char *s);
 char *
 xstrndup (const char *s, size_t n);
 
-void
+/* Returns 0 for successful compilation, 1 otherwise */
+int
 xregcomp (regex_t *preg, const char *regex, int cflags);
 
 int
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* (no subject)
  2011-10-23 21:51 ` [PATCH] xregcomp: don't consider every regex compilation failure an internal error David Bremner
@ 2011-10-29  3:04   ` David Bremner
  2011-10-29  3:04     ` [PATCH 1/4] notmuch-restore: add tests for new --match command line argument David Bremner
                       ` (3 more replies)
  0 siblings, 4 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29  3:04 UTC (permalink / raw)
  To: notmuch

These still need doc updates, but you can probably figure how to use them by reviewing the patches ;).

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 1/4] notmuch-restore: add tests for new --match command line argument.
  2011-10-29  3:04   ` David Bremner
@ 2011-10-29  3:04     ` David Bremner
  2011-10-29  3:04     ` [PATCH 2/4] notmuch-restore: implement argument parsing for --match David Bremner
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29  3:04 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

The syntax is notmuch restore --match=regex

We check here that
- missing regex fails
- bad regex fails
- good regex succeeds (at least the command line argument parsing).
---
 test/dump-restore |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/test/dump-restore b/test/dump-restore
index 439e998..c176b52 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -50,9 +50,24 @@ test_expect_success 'Restore with nothing to do, III' \
   test_cmp dump.expected dump.actual'
 
 # notmuch restore currently only considers the first argument.
-test_expect_success 'Invalid restore invocation' \
+test_expect_success 'restore extra argument' \
   'test_must_fail notmuch restore dump.expected another_one'
 
+
+test_begin_subtest 'restore --match #missing arg'
+test_subtest_known_broken
+test_expect_equal "restore: option '--match' requires an argument"\
+  "$(notmuch restore --match 2>&1)"
+
+test_begin_subtest 'restore --match=<bad regex>'
+test_subtest_known_broken
+test_expect_equal 'compiling regex notmuch.*[: Invalid regular expression'\
+  "$(notmuch restore --match='notmuch.*[' 2>&1)"
+
+test_subtest_known_broken
+test_expect_success 'restore --match=<good regex>' \
+    'notmuch restore --match="notmuch.*" < /dev/null > /dev/null 2>&1'
+
 test_begin_subtest "dump outfile"
 notmuch dump dump-outfile.actual
 test_expect_equal_file dump.expected dump-outfile.actual
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 2/4] notmuch-restore: implement argument parsing for --match
  2011-10-29  3:04   ` David Bremner
  2011-10-29  3:04     ` [PATCH 1/4] notmuch-restore: add tests for new --match command line argument David Bremner
@ 2011-10-29  3:04     ` David Bremner
  2011-10-29  3:04     ` [PATCH 3/4] test/dump-restore: add tests for notmuch restore --match David Bremner
  2011-10-29  3:04     ` [PATCH 4/4] notmuch-restore: implement --match functionality David Bremner
  3 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29  3:04 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

- recognize the --match option
- require an argument
- check the argument is a correct regex.

Currently the arguments are ignored after parsing. Note that we have
to be a bit careful to avoid creating a resource leak here by error
returning before calling regfree. On the other hand, notmuch is
probably shutting down at that point, so it may not matter much.
---
 notmuch-restore.c |   18 ++++++++++++++----
 test/dump-restore |    3 ---
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index 13b4325..e5ac162 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -29,11 +29,12 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
     notmuch_database_t *notmuch;
     notmuch_bool_t synchronize_flags;
     notmuch_bool_t accumulate = FALSE;
+    notmuch_bool_t match_enabled = FALSE;
     FILE *input = stdin;
     char *line = NULL;
     size_t line_size;
     ssize_t line_len;
-    regex_t regex;
+    regex_t input_regex, match_regex;
     int rerr;
 
     config = notmuch_config_open (ctx, NULL, NULL);
@@ -49,6 +50,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 
     struct option options[] = {
 	{ "accumulate",   no_argument,       0, 'a' },
+	{ "match",	  required_argument, 0, 'm' },
 	{ 0, 0, 0, 0}
     };
 
@@ -60,6 +62,11 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 	case 'a':
 	    accumulate = 1;
 	    break;
+	case 'm':
+	    match_enabled = TRUE;
+	    if ( xregcomp (&match_regex, optarg, REG_EXTENDED) )
+		return 1;
+	    break;
 	case '?':
 	    return 1;
 	    break;
@@ -88,7 +95,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
      * non-space characters for the message-id, then one or more
      * spaces, then a list of space-separated tags as a sequence of
      * characters within literal '(' and ')'. */
-    if ( xregcomp (&regex,
+    if ( xregcomp (&input_regex,
 		   "^([^ ]+) \\(([^)]*)\\)$",
 		   REG_EXTENDED) )
 	INTERNAL_ERROR("compile time constant regex failed.");
@@ -103,7 +110,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 
 	chomp_newline (line);
 
-	rerr = xregexec (&regex, line, 3, match, 0);
+	rerr = xregexec (&input_regex, line, 3, match, 0);
 	if (rerr == REG_NOMATCH)
 	{
 	    fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",
@@ -186,7 +193,10 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 	free (file_tags);
     }
 
-    regfree (&regex);
+    regfree (&input_regex);
+
+    if (match_enabled)
+	regfree (&match_regex);
 
     if (line)
 	free (line);
diff --git a/test/dump-restore b/test/dump-restore
index c176b52..c6089f9 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -55,16 +55,13 @@ test_expect_success 'restore extra argument' \
 
 
 test_begin_subtest 'restore --match #missing arg'
-test_subtest_known_broken
 test_expect_equal "restore: option '--match' requires an argument"\
   "$(notmuch restore --match 2>&1)"
 
 test_begin_subtest 'restore --match=<bad regex>'
-test_subtest_known_broken
 test_expect_equal 'compiling regex notmuch.*[: Invalid regular expression'\
   "$(notmuch restore --match='notmuch.*[' 2>&1)"
 
-test_subtest_known_broken
 test_expect_success 'restore --match=<good regex>' \
     'notmuch restore --match="notmuch.*" < /dev/null > /dev/null 2>&1'
 
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 3/4] test/dump-restore: add tests for notmuch restore --match
  2011-10-29  3:04   ` David Bremner
  2011-10-29  3:04     ` [PATCH 1/4] notmuch-restore: add tests for new --match command line argument David Bremner
  2011-10-29  3:04     ` [PATCH 2/4] notmuch-restore: implement argument parsing for --match David Bremner
@ 2011-10-29  3:04     ` David Bremner
  2011-10-29  3:04     ` [PATCH 4/4] notmuch-restore: implement --match functionality David Bremner
  3 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29  3:04 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

The first test passes now because it only needs command line parsing.
The other three are marked broken, with functionality to follow.
---
 test/dump-restore |   43 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/test/dump-restore b/test/dump-restore
index c6089f9..18925a4 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -65,6 +65,49 @@ test_expect_equal 'compiling regex notmuch.*[: Invalid regular expression'\
 test_expect_success 'restore --match=<good regex>' \
     'notmuch restore --match="notmuch.*" < /dev/null > /dev/null 2>&1'
 
+test_expect_success 'Restoring with trivial match' \
+  'notmuch restore --match="." dump.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump.expected dump.actual'
+
+sed 's/inbox\|unread\|signed//g' < dump-ABC_DEF.expected > dump-ABC_DEF-only
+
+test_subtest_known_broken
+test_expect_success 'Simulate accumulate with match' \
+  'notmuch restore < dump.expected &&
+   notmuch restore --match="(ABC|DEF)" < dump-ABC_DEF-only &&
+   notmuch dump > dump-simulate.actual &&
+   notmuch restore < dump.expected &&
+   test_cmp dump-ABC_DEF.expected dump-simulate.actual'
+
+test_subtest_known_broken
+test_expect_success 'clear only matched tags' \
+  'notmuch restore < dump-ABC_DEF.expected &&
+   notmuch restore --match="(ABC|DEF)" < clear.expected &&
+   notmuch dump > dump-clear-match.actual &&
+   notmuch restore < dump.expected &&
+   test_cmp dump.expected dump-clear-match.actual'
+
+test_subtest_known_broken
+test_expect_success 'import only matched tags' \
+  'notmuch restore < dump.expected &&
+   notmuch restore --match="(inbox|unread)" < dump-ABC_DEF.expected &&
+   notmuch dump > dump-import-match.actual &&
+   notmuch restore < dump.expected &&
+   test_cmp dump.expected dump-import-match.actual'
+
+sed 's/inbox\|unread\|signed\|ABC//g' < dump-ABC_DEF.expected > dump-DEF-only
+sed 's/inbox\|unread\|signed\|DEF//g' < dump-ABC_DEF.expected > dump-ABC-only
+
+test_subtest_known_broken
+test_expect_success 'combine --match and --accumulate' \
+  'notmuch restore < dump.expected &&
+   notmuch restore --match="(ABC|DEF)" < dump-ABC-only &&
+   notmuch restore --accumulate --match="(ABC|DEF)" < dump-DEF-only &&
+   notmuch dump > dump-combine-match.actual &&
+   notmuch restore < dump.expected &&
+   test_cmp dump-ABC_DEF.expected dump-combine-match.actual'
+
 test_begin_subtest "dump outfile"
 notmuch dump dump-outfile.actual
 test_expect_equal_file dump.expected dump-outfile.actual
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 4/4] notmuch-restore: implement --match functionality
  2011-10-29  3:04   ` David Bremner
                       ` (2 preceding siblings ...)
  2011-10-29  3:04     ` [PATCH 3/4] test/dump-restore: add tests for notmuch restore --match David Bremner
@ 2011-10-29  3:04     ` David Bremner
  2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
  2011-10-29 20:55       ` [PATCH] docs: man, online help, and NEWS updated for notmuch restore --match David Bremner
  3 siblings, 2 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29  3:04 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

notmuch restore --match=<regex> will for each message in the dump
file, delete any tags from the database matching <regex> and add any
from the dump file matching <regex>.

I tried to keep this simple, so in particular turning on regex
matching disables one of the optimizations.
---
 notmuch-restore.c |   16 +++++++++++++---
 test/dump-restore |    4 ----
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index e5ac162..a1b6b81 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -64,7 +64,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 	    break;
 	case 'm':
 	    match_enabled = TRUE;
-	    if ( xregcomp (&match_regex, optarg, REG_EXTENDED) )
+	    if ( xregcomp (&match_regex, optarg, REG_EXTENDED|REG_NOSUB) )
 		return 1;
 	    break;
 	case '?':
@@ -147,6 +147,10 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 	{
 	    const char *tag = notmuch_tags_get (db_tags);
 
+	    if (match_enabled && !accumulate &&
+		!regexec (&match_regex, tag, 0, NULL, 0) )
+		notmuch_message_remove_tag (message, tag);
+
 	    if (db_tags_str)
 		db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag);
 	    else
@@ -155,14 +159,15 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 
 	if (((file_tags == NULL || *file_tags == '\0') &&
 	     (db_tags_str == NULL || *db_tags_str == '\0')) ||
-	    (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0))
+	    (!match_enabled && file_tags && db_tags_str && 
+	     strcmp (file_tags, db_tags_str) == 0))
 	{
 	    goto NEXT_LINE;
 	}
 
 	notmuch_message_freeze (message);
 
-	if (!accumulate)
+	if (!accumulate && !match_enabled)
 	    notmuch_message_remove_all_tags (message);
 
 	next = file_tags;
@@ -170,6 +175,11 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
 	    tag = strsep (&next, " ");
 	    if (*tag == '\0')
 		continue;
+
+	    if (match_enabled &&
+		regexec (&match_regex, tag, 0, NULL, 0))
+		continue;
+
 	    status = notmuch_message_add_tag (message, tag);
 	    if (status) {
 		fprintf (stderr,
diff --git a/test/dump-restore b/test/dump-restore
index 18925a4..a4517f6 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -72,7 +72,6 @@ test_expect_success 'Restoring with trivial match' \
 
 sed 's/inbox\|unread\|signed//g' < dump-ABC_DEF.expected > dump-ABC_DEF-only
 
-test_subtest_known_broken
 test_expect_success 'Simulate accumulate with match' \
   'notmuch restore < dump.expected &&
    notmuch restore --match="(ABC|DEF)" < dump-ABC_DEF-only &&
@@ -80,7 +79,6 @@ test_expect_success 'Simulate accumulate with match' \
    notmuch restore < dump.expected &&
    test_cmp dump-ABC_DEF.expected dump-simulate.actual'
 
-test_subtest_known_broken
 test_expect_success 'clear only matched tags' \
   'notmuch restore < dump-ABC_DEF.expected &&
    notmuch restore --match="(ABC|DEF)" < clear.expected &&
@@ -88,7 +86,6 @@ test_expect_success 'clear only matched tags' \
    notmuch restore < dump.expected &&
    test_cmp dump.expected dump-clear-match.actual'
 
-test_subtest_known_broken
 test_expect_success 'import only matched tags' \
   'notmuch restore < dump.expected &&
    notmuch restore --match="(inbox|unread)" < dump-ABC_DEF.expected &&
@@ -99,7 +96,6 @@ test_expect_success 'import only matched tags' \
 sed 's/inbox\|unread\|signed\|ABC//g' < dump-ABC_DEF.expected > dump-DEF-only
 sed 's/inbox\|unread\|signed\|DEF//g' < dump-ABC_DEF.expected > dump-ABC-only
 
-test_subtest_known_broken
 test_expect_success 'combine --match and --accumulate' \
   'notmuch restore < dump.expected &&
    notmuch restore --match="(ABC|DEF)" < dump-ABC-only &&
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Experimental Tag Sharing
  2011-10-29  3:04     ` [PATCH 4/4] notmuch-restore: implement --match functionality David Bremner
@ 2011-10-29 16:45       ` David Bremner
  2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
                           ` (2 more replies)
  2011-10-29 20:55       ` [PATCH] docs: man, online help, and NEWS updated for notmuch restore --match David Bremner
  1 sibling, 3 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29 16:45 UTC (permalink / raw)
  To: notmuch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, here is my rough and ready attempt at tag sharing.  I figure we
can smooth out the rough edges if/when we agree on a set of tags and
preferably on an on-disk format.

How to play?

- - Apply all the patches in this thread (starting
  id:"1319383133-11006-1-git-send-email-david@tethera.net")

- - to git master git clone git://pivot.cs.unb.ca/nmbug.git $HOME/.nmbug

- - put the script nmbug somewhere

# suck in the tags
$ nmbug restore

[ do some tagging ]

$ nmbug dump
$ cd $HOME/.nmbug && git add tags && git commit

now you have to get the changes into the "master" repo.  I think
hosting this on git.notmuchmail.org in a seperate repo will make
sense, but for now, if you want to participate in the experiment, send
me your public key (ideally in a gpg signed mail, but if not, oh well), 
and you can push to my repo at

    git@pivot.cs.unb.ca:pub/nmbug

Probably the most crucial thing is that we agree on some set of tags.
Here is the set of tags I am working with (output from 
     notmuch search --output=tags "*" | grep ^notmuch:: 
)

notmuch::bug   		     is a bug report
notmuch::feature	     provides a new feature
notmuch::fix		     fixes a bug
notmuch::obsolete	     replaced by some other patch
notmuch::patch		     
notmuch::portability	     improves portability
notmuch::pushed		     is pushed to master
notmuch::review		     is a review
notmuch::reviewed	     has been (well reviewed)
notmuch::test		     provides a new test/or improves testing

The prefix "notmuch::" is hardcoded into the script, but everything
else is easy to change with the usual notmuch operations.  Of course
we can change the prefix too. I decided on :: because it needed less
escaping.

On think I'm not sure about is how to handle ideas like "voting", and
"signing off" with tags. I suspect we can come up with some
conventions like "notmuch::review1", or
"notmuch::vote1".."notmuch::vote5". But maybe people have less kludgy
ideas.

Remember, perfect is the enemy of good ;)

David
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iJwEAQECAAYFAk6sLWQACgkQTiiN/0Um85k5JQP+PZ0ycrNTTZZ72PyXbDQzrv4+
dDKopxh+WpeORjWu8gvj+LNMM3A8n0t4/A12mxrG9cUJxg2HTPUtRo9Zi7dFQzct
uGvSOvQ6GocUretW0BaK/yp53v0R7b00qxvxAvUeoZPgIWGtqp52fXW8d0I2yAUn
079okQwPYdbowaPAX1A=
=+E8F
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch::
  2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
@ 2011-10-29 16:45         ` David Bremner
  2011-10-29 21:11           ` Ali Polatel
  2011-11-07  0:59           ` [PATCH] contrib/nmbug: new script for sharing tags with a given prefix David Bremner
  2011-10-29 17:27         ` [PATCH v2] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
  2011-10-30  0:44         ` Experimental Tag Sharing Jameson Graef Rollins
  2 siblings, 2 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29 16:45 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

In this initial version, we take care of only the base import and
export of the appropriate tags in line oriented format amenable to
easy merging.

The current plan to use git to share tags and resolve conflicts.
---
 contrib/nmbug |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 69 insertions(+), 0 deletions(-)
 create mode 100755 contrib/nmbug

diff --git a/contrib/nmbug b/contrib/nmbug
new file mode 100755
index 0000000..e9d1b14
--- /dev/null
+++ b/contrib/nmbug
@@ -0,0 +1,69 @@
+#!/bin/bash
+# Copyright (c) 2011 David Bremner
+# License: same as notmuch
+
+NMHOME=${HOME}/.nmbug
+
+NMTAGS=($(notmuch search --output=tags "*"|grep "^notmuch::"))
+
+function dump() {
+    notmuch dump -- $(printf  " tag:%s" ${NMTAGS[*]}) |\
+    while read  -r msgid rest 
+    do
+	outfile=$NMHOME/tags/$(echo $msgid | sha1sum - | cut -f1 -d' ')
+	echo $outfile
+	printf "msg-id: %s\n" $msgid > $outfile
+	tmp=${rest#\(}
+	read -r -a tags <<<${tmp%\)};
+	for tag in "${tags[@]}"; do
+	    case $tag in
+		notmuch::*)
+		    echo "tag: $tag" >> $outfile
+		    ;;
+		*)
+		# nothing
+	    esac
+	done
+    done
+    
+}
+
+function cat_file() {
+    tags=""
+    id=""
+    cat $1 |\
+    while read -r what data 
+    do
+	case $what in
+	    msg-id:)
+		printf "%s (" $data 
+		;;
+	    tag:)
+		printf "%s " $data
+		;;
+	    *)
+		echo "Syntax error $what"
+		exit 1
+	esac
+    done
+    echo ")"
+}
+
+function restore() {
+    find $NMHOME/tags -type f |\
+    while read -r filename
+    do
+	cat_file $filename
+    done | notmuch restore --match=notmuch::
+}
+case $1 in
+    dump)
+	dump
+	;;
+    restore)
+	restore
+	;;
+    *)
+	echo unknown command $1;
+esac
+   
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v2] contrib/nmbug: new script for sharing tags with prefix notmuch::
  2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
  2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
@ 2011-10-29 17:27         ` David Bremner
  2011-10-30  0:44         ` Experimental Tag Sharing Jameson Graef Rollins
  2 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29 17:27 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

In this initial version, we take care of import and export of the
appropriate tags in line oriented format amenable to easy merging.

We also provide (not very robust/clever) commands commit/push/pull
to deal with a git repo that as already been set up.
---

fixed silly debugging output.
add some convenience commands.

 contrib/nmbug |   77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 77 insertions(+), 0 deletions(-)
 create mode 100755 contrib/nmbug

diff --git a/contrib/nmbug b/contrib/nmbug
new file mode 100755
index 0000000..a449d60
--- /dev/null
+++ b/contrib/nmbug
@@ -0,0 +1,77 @@
+#!/bin/bash
+# Copyright (c) 2011 David Bremner
+# License: same as notmuch
+
+NMHOME=${HOME}/.nmbug
+
+NMTAGS=($(notmuch search --output=tags "*"|grep "^notmuch::"))
+
+function dump() {
+    notmuch dump -- $(printf  " tag:%s" ${NMTAGS[*]}) |\
+    while read  -r msgid rest 
+    do
+	outfile=$NMHOME/tags/$(echo $msgid | sha1sum - | cut -f1 -d' ')
+	printf "msg-id: %s\n" $msgid > $outfile
+	tmp=${rest#\(}
+	read -r -a tags <<<${tmp%\)};
+	for tag in "${tags[@]}"; do
+	    case $tag in
+		notmuch::*)
+		    echo "tag: $tag" >> $outfile
+		    ;;
+		*)
+		# nothing
+	    esac
+	done
+    done
+    
+}
+
+function cat_file() {
+    tags=""
+    id=""
+    cat $1 |\
+    while read -r what data 
+    do
+	case $what in
+	    msg-id:)
+		printf "%s (" $data 
+		;;
+	    tag:)
+		printf "%s " $data
+		;;
+	    *)
+		echo "Syntax error $what"
+		exit 1
+	esac
+    done
+    echo ")"
+}
+
+function restore() {
+    find $NMHOME/tags -type f |\
+    while read -r filename
+    do
+	cat_file $filename
+    done | notmuch restore --match=notmuch::
+}
+case $1 in
+    dump)
+	dump
+	;;
+    restore)
+	restore
+	;;
+    commit)
+	(cd $NMHOME && git add tags && git commit)
+	;;
+    push)
+	(cd $NMHOME && git push)
+	;;
+    pull)
+    	(cd $NMHOME && git pull)
+	;;
+    *)
+	echo unknown command $1;
+esac
+   
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
  2011-10-23 21:51 ` [PATCH] xregcomp: don't consider every regex compilation failure an internal error David Bremner
@ 2011-10-29 18:52 ` David Bremner
  2011-10-31  0:04 ` Austin Clements
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-29 18:52 UTC (permalink / raw)
  To: notmuch


[-- Attachment #1.1: Type: text/plain, Size: 598 bytes --]

On Sun, 23 Oct 2011 12:18:53 -0300, David Bremner <david@tethera.net> wrote:
> From: David Bremner <bremner@debian.org>
> 
> We keep the lib/xutil.c version. As a consequence, also factor out
> _internal_error and associated macros.  It might be overkill to make a
> new file error_util.c for this, but _internal_error does not really
> belong in database.cc.

It seems this patch was sent out base64 encoded, which confuses git-am.
I attach the patch here. You won't be able to git am this message, but
you can at least save the attachment and apply it. Sorry for the
inconvenience.


[-- Attachment #1.2: Type: application/pgp-signature, Size: 315 bytes --]

[-- Attachment #2: xutil patch --]
[-- Type: text/x-diff, Size: 21830 bytes --]

From 5c678c9bc96890ce9a5965d6654851ebbe2b4767 Mon Sep 17 00:00:00 2001
From: David Bremner <bremner@debian.org>
Date: Sun, 23 Oct 2011 12:05:13 -0300
Subject: [PATCH] xutil.c: remove duplicate copies, create new library
 libutil.a to contain xutil.

We keep the lib/xutil.c version. As a consequence, also factor out
_internal_error and associated macros.  It might be overkill to make a
new file error_util.c for this, but _internal_error does not really
belong in database.cc.
---
 Makefile              |    2 +-
 Makefile.local        |    7 +--
 lib/Makefile.local    |    5 +-
 lib/database.cc       |   15 -----
 lib/notmuch-private.h |   20 +-------
 lib/xutil.c           |  134 -----------------------------------------------
 lib/xutil.h           |   55 -------------------
 util/Makefile         |    5 ++
 util/Makefile.local   |   11 ++++
 util/error_util.c     |   41 +++++++++++++++
 util/error_util.h     |   45 ++++++++++++++++
 util/xutil.c          |  136 ++++++++++++++++++++++++++++++++++++++++++++++++
 util/xutil.h          |   55 +++++++++++++++++++
 xutil.c               |  138 -------------------------------------------------
 14 files changed, 300 insertions(+), 369 deletions(-)
 delete mode 100644 lib/xutil.c
 delete mode 100644 lib/xutil.h
 create mode 100644 util/Makefile
 create mode 100644 util/Makefile.local
 create mode 100644 util/error_util.c
 create mode 100644 util/error_util.h
 create mode 100644 util/xutil.c
 create mode 100644 util/xutil.h
 delete mode 100644 xutil.c

diff --git a/Makefile b/Makefile
index 11e3a3d..2fb2a61 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 all:
 
 # List all subdirectories here. Each contains its own Makefile.local
-subdirs = compat completion emacs lib test
+subdirs = compat completion emacs lib util test
 
 # We make all targets depend on the Makefiles themselves.
 global_deps = Makefile Makefile.config Makefile.local \
diff --git a/Makefile.local b/Makefile.local
index ec09f95..6ddef5c 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -39,7 +39,7 @@ PV_FILE=bindings/python/notmuch/version.py
 # Smash together user's values with our extra values
 FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
 FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS) $(extra_cflags) $(extra_cxxflags)
-FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
+FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
 FINAL_NOTMUCH_LINKER = CC
 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
 FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
@@ -299,12 +299,11 @@ notmuch_client_srcs =		\
 	notmuch-time.c		\
 	query-string.c		\
 	show-message.c		\
-	json.c			\
-	xutil.c
+	json.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
 
-notmuch: $(notmuch_client_modules) lib/libnotmuch.a
+notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a
 	$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
 
 notmuch-shared: $(notmuch_client_modules) lib/$(LINKER_NAME)
diff --git a/lib/Makefile.local b/lib/Makefile.local
index be51eaa..d58552c 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -54,8 +54,7 @@ libnotmuch_c_srcs =		\
 	$(dir)/message-file.c	\
 	$(dir)/messages.c	\
 	$(dir)/sha1.c		\
-	$(dir)/tags.c		\
-	$(dir)/xutil.c
+	$(dir)/tags.c
 
 libnotmuch_cxx_srcs =		\
 	$(dir)/database.cc	\
@@ -71,7 +70,7 @@ $(dir)/libnotmuch.a: $(libnotmuch_modules)
 	$(call quiet,AR) rcs $@ $^
 
 $(dir)/$(LIBNAME): $(libnotmuch_modules) notmuch.sym
-	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@
+	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@ -L$(srcdir)/util -lutil
 
 notmuch.sym: $(srcdir)/$(dir)/notmuch.h $(libnotmuch_modules)
 	sh $(srcdir)/$(lib)/gen-version-script.sh $< $(libnotmuch_modules) > $@
diff --git a/lib/database.cc b/lib/database.cc
index e77fd53..88be939 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -209,21 +209,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
     { "folder",			"XFOLDER"}
 };
 
-int
-_internal_error (const char *format, ...)
-{
-    va_list va_args;
-
-    va_start (va_args, format);
-
-    fprintf (stderr, "Internal error: ");
-    vfprintf (stderr, format, va_args);
-
-    exit (1);
-
-    return 1;
-}
-
 const char *
 _find_prefix (const char *name)
 {
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index d319530..0d3cc27 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -47,6 +47,7 @@ NOTMUCH_BEGIN_DECLS
 #include <talloc.h>
 
 #include "xutil.h"
+#include "error_util.h"
 
 #pragma GCC visibility push(hidden)
 
@@ -60,25 +61,6 @@ NOTMUCH_BEGIN_DECLS
 #define STRNCMP_LITERAL(var, literal) \
     strncmp ((var), (literal), sizeof (literal) - 1)
 
-/* There's no point in continuing when we've detected that we've done
- * something wrong internally (as opposed to the user passing in a
- * bogus value).
- *
- * Note that PRINTF_ATTRIBUTE comes from talloc.h
- */
-int
-_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
-
-/* There's no point in continuing when we've detected that we've done
- * something wrong internally (as opposed to the user passing in a
- * bogus value).
- *
- * Note that __location__ comes from talloc.h.
- */
-#define INTERNAL_ERROR(format, ...)			\
-    _internal_error (format " (%s).\n",			\
-		     ##__VA_ARGS__, __location__)
-
 #define unused(x) x __attribute__ ((unused))
 
 #ifdef __cplusplus
diff --git a/lib/xutil.c b/lib/xutil.c
deleted file mode 100644
index 268225b..0000000
--- a/lib/xutil.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* xutil.c - Various wrapper functions to abort on error.
- *
- * Copyright © 2009 Carl Worth
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see http://www.gnu.org/licenses/ .
- *
- * Author: Carl Worth <cworth@cworth.org>
- */
-
-#include "notmuch-private.h"
-
-#include <stdio.h>
-
-void *
-xcalloc (size_t nmemb, size_t size)
-{
-    void *ret;
-
-    ret = calloc (nmemb, size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-void *
-xmalloc (size_t size)
-{
-    void *ret;
-
-    ret = malloc (size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-void *
-xrealloc (void *ptr, size_t size)
-{
-    void *ret;
-
-    ret = realloc (ptr, size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-char *
-xstrdup (const char *s)
-{
-    char *ret;
-
-    ret = strdup (s);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-char *
-xstrndup (const char *s, size_t n)
-{
-    char *ret;
-
-    if (strlen (s) <= n)
-	n = strlen (s);
-
-    ret = malloc (n + 1);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-    memcpy (ret, s, n);
-    ret[n] = '\0';
-
-    return ret;
-}
-
-void
-xregcomp (regex_t *preg, const char *regex, int cflags)
-{
-    int rerr;
-
-    rerr = regcomp (preg, regex, cflags);
-    if (rerr) {
-	size_t error_size = regerror (rerr, preg, NULL, 0);
-	char *error = xmalloc (error_size);
-
-	regerror (rerr, preg, error, error_size);
-	INTERNAL_ERROR ("compiling regex %s: %s\n",
-			regex, error);
-    }
-}
-
-int
-xregexec (const regex_t *preg, const char *string,
-	  size_t nmatch, regmatch_t pmatch[], int eflags)
-{
-    unsigned int i;
-    int rerr;
-
-    rerr = regexec (preg, string, nmatch, pmatch, eflags);
-    if (rerr)
-	return rerr;
-
-    for (i = 0; i < nmatch; i++) {
-	if (pmatch[i].rm_so == -1)
-	    INTERNAL_ERROR ("matching regex against %s: Sub-match %d not found\n",
-			    string, i);
-    }
-
-    return 0;
-}
diff --git a/lib/xutil.h b/lib/xutil.h
deleted file mode 100644
index fd77f73..0000000
--- a/lib/xutil.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* xutil.h - Various wrapper functions to abort on error.
- *
- * Copyright © 2009 Carl Worth
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see http://www.gnu.org/licenses/ .
- *
- * Author: Carl Worth <cworth@cworth.org>
- */
-
-#ifndef NOTMUCH_XUTIL_H
-#define NOTMUCH_XUTIL_H
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <regex.h>
-
-#pragma GCC visibility push(hidden)
-
-/* xutil.c */
-void *
-xcalloc (size_t nmemb, size_t size);
-
-void *
-xmalloc (size_t size);
-
-void *
-xrealloc (void *ptrr, size_t size);
-
-char *
-xstrdup (const char *s);
-
-char *
-xstrndup (const char *s, size_t n);
-
-void
-xregcomp (regex_t *preg, const char *regex, int cflags);
-
-int
-xregexec (const regex_t *preg, const char *string,
-	  size_t nmatch, regmatch_t pmatch[], int eflags);
-
-#pragma GCC visibility pop
-
-#endif
diff --git a/util/Makefile b/util/Makefile
new file mode 100644
index 0000000..fa25832
--- /dev/null
+++ b/util/Makefile
@@ -0,0 +1,5 @@
+all:
+	$(MAKE) -C .. all
+
+.DEFAULT:
+	$(MAKE) -C .. $@
diff --git a/util/Makefile.local b/util/Makefile.local
new file mode 100644
index 0000000..2ff42b3
--- /dev/null
+++ b/util/Makefile.local
@@ -0,0 +1,11 @@
+# -*- makefile -*-
+
+dir := util
+extra_cflags += -I$(srcdir)/$(dir)
+
+libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c
+
+libutil_modules := $(libutil_c_srcs:.c=.o)
+
+$(dir)/libutil.a: $(libutil_modules)
+	$(call quiet,AR) rcs $@ $^
diff --git a/util/error_util.c b/util/error_util.c
new file mode 100644
index 0000000..630d228
--- /dev/null
+++ b/util/error_util.c
@@ -0,0 +1,41 @@
+/* error_util.c - internal error utilities for notmuch.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "error_util.h"
+
+int
+_internal_error (const char *format, ...)
+{
+    va_list va_args;
+
+    va_start (va_args, format);
+
+    fprintf (stderr, "Internal error: ");
+    vfprintf (stderr, format, va_args);
+
+    exit (1);
+
+    return 1;
+}
+
diff --git a/util/error_util.h b/util/error_util.h
new file mode 100644
index 0000000..0f1e5ef
--- /dev/null
+++ b/util/error_util.h
@@ -0,0 +1,45 @@
+/* error_util.h - Internal interfaces for notmuch.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef ERROR_UTIL_H
+#define ERROR_UTIL_H
+
+#include <talloc.h>
+
+/* There's no point in continuing when we've detected that we've done
+ * something wrong internally (as opposed to the user passing in a
+ * bogus value).
+ *
+ * Note that PRINTF_ATTRIBUTE comes from talloc.h
+ */
+int
+_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
+
+/* There's no point in continuing when we've detected that we've done
+ * something wrong internally (as opposed to the user passing in a
+ * bogus value).
+ *
+ * Note that __location__ comes from talloc.h.
+ */
+#define INTERNAL_ERROR(format, ...)			\
+    _internal_error (format " (%s).\n",			\
+		     ##__VA_ARGS__, __location__)
+
+#endif
diff --git a/util/xutil.c b/util/xutil.c
new file mode 100644
index 0000000..15ff765
--- /dev/null
+++ b/util/xutil.c
@@ -0,0 +1,136 @@
+/* xutil.c - Various wrapper functions to abort on error.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "xutil.h"
+#include "error_util.h"
+
+void *
+xcalloc (size_t nmemb, size_t size)
+{
+    void *ret;
+
+    ret = calloc (nmemb, size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xmalloc (size_t size)
+{
+    void *ret;
+
+    ret = malloc (size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xrealloc (void *ptr, size_t size)
+{
+    void *ret;
+
+    ret = realloc (ptr, size);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrdup (const char *s)
+{
+    char *ret;
+
+    ret = strdup (s);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrndup (const char *s, size_t n)
+{
+    char *ret;
+
+    if (strlen (s) <= n)
+	n = strlen (s);
+
+    ret = malloc (n + 1);
+    if (ret == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+    memcpy (ret, s, n);
+    ret[n] = '\0';
+
+    return ret;
+}
+
+void
+xregcomp (regex_t *preg, const char *regex, int cflags)
+{
+    int rerr;
+
+    rerr = regcomp (preg, regex, cflags);
+    if (rerr) {
+	size_t error_size = regerror (rerr, preg, NULL, 0);
+	char *error = xmalloc (error_size);
+
+	regerror (rerr, preg, error, error_size);
+	INTERNAL_ERROR ("compiling regex %s: %s\n",
+			regex, error);
+    }
+}
+
+int
+xregexec (const regex_t *preg, const char *string,
+	  size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+    unsigned int i;
+    int rerr;
+
+    rerr = regexec (preg, string, nmatch, pmatch, eflags);
+    if (rerr)
+	return rerr;
+
+    for (i = 0; i < nmatch; i++) {
+	if (pmatch[i].rm_so == -1)
+	    INTERNAL_ERROR ("matching regex against %s: Sub-match %d not found\n",
+			    string, i);
+    }
+
+    return 0;
+}
diff --git a/util/xutil.h b/util/xutil.h
new file mode 100644
index 0000000..fd77f73
--- /dev/null
+++ b/util/xutil.h
@@ -0,0 +1,55 @@
+/* xutil.h - Various wrapper functions to abort on error.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef NOTMUCH_XUTIL_H
+#define NOTMUCH_XUTIL_H
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#pragma GCC visibility push(hidden)
+
+/* xutil.c */
+void *
+xcalloc (size_t nmemb, size_t size);
+
+void *
+xmalloc (size_t size);
+
+void *
+xrealloc (void *ptrr, size_t size);
+
+char *
+xstrdup (const char *s);
+
+char *
+xstrndup (const char *s, size_t n);
+
+void
+xregcomp (regex_t *preg, const char *regex, int cflags);
+
+int
+xregexec (const regex_t *preg, const char *string,
+	  size_t nmatch, regmatch_t pmatch[], int eflags);
+
+#pragma GCC visibility pop
+
+#endif
diff --git a/xutil.c b/xutil.c
deleted file mode 100644
index 5f98f3f..0000000
--- a/xutil.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* xutil.c - Various wrapper functions to abort on error.
- *
- * Copyright © 2009 Carl Worth
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see http://www.gnu.org/licenses/ .
- *
- * Author: Carl Worth <cworth@cworth.org>
- */
-
-#include "notmuch-private.h"
-
-#include <stdio.h>
-
-void *
-xcalloc (size_t nmemb, size_t size)
-{
-    void *ret;
-
-    ret = calloc (nmemb, size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-void *
-xmalloc (size_t size)
-{
-    void *ret;
-
-    ret = malloc (size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-void *
-xrealloc (void *ptr, size_t size)
-{
-    void *ret;
-
-    ret = realloc (ptr, size);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-char *
-xstrdup (const char *s)
-{
-    char *ret;
-
-    ret = strdup (s);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-
-    return ret;
-}
-
-char *
-xstrndup (const char *s, size_t n)
-{
-    char *ret;
-
-    if (strlen (s) <= n)
-	n = strlen (s);
-
-    ret = malloc (n + 1);
-    if (ret == NULL) {
-	fprintf (stderr, "Out of memory.\n");
-	exit (1);
-    }
-    memcpy (ret, s, n);
-    ret[n] = '\0';
-
-    return ret;
-}
-
-void
-xregcomp (regex_t *preg, const char *regex, int cflags)
-{
-    int rerr;
-
-    rerr = regcomp (preg, regex, cflags);
-    if (rerr) {
-	size_t error_size = regerror (rerr, preg, NULL, 0);
-	char *error = xmalloc (error_size);
-
-	regerror (rerr, preg, error, error_size);
-	fprintf (stderr, "Internal error: compiling regex %s: %s\n",
-		 regex, error);
-	exit (1);
-    }
-}
-
-int
-xregexec (const regex_t *preg, const char *string,
-	  size_t nmatch, regmatch_t pmatch[], int eflags)
-{
-    unsigned int i;
-    int rerr;
-
-    rerr = regexec (preg, string, nmatch, pmatch, eflags);
-    if (rerr)
-	return rerr;
-
-    for (i = 0; i < nmatch; i++) {
-	if (pmatch[i].rm_so == -1) {
-	    fprintf (stderr, "Internal error: matching regex against %s:"
-		     "Sub-match %d not found\n",
-		     string, i);
-	    exit (1);
-	}
-    }
-
-    return 0;
-}
-- 
1.7.6.3


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH] docs: man, online help, and NEWS updated for notmuch restore --match
  2011-10-29  3:04     ` [PATCH 4/4] notmuch-restore: implement --match functionality David Bremner
  2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
@ 2011-10-29 20:55       ` David Bremner
  2011-10-31 19:01         ` [PATCH] Fix formatting for restore and dump help documentation Jameson Graef Rollins
  1 sibling, 1 reply; 35+ messages in thread
From: David Bremner @ 2011-10-29 20:55 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

I also reformatted the notmuch restore --accumulate docs to make it
work better with multiple options.
---
Better late docs than no docs...
also pushed to branch nmbug on git://pivot.cs.unb.ca/notmuch.git

 NEWS      |   12 ++++++++----
 notmuch.1 |   19 +++++++++++++++++--
 notmuch.c |   14 +++++++++++---
 3 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index e00452a..9034d5c 100644
--- a/NEWS
+++ b/NEWS
@@ -4,11 +4,15 @@ Notmuch 0.10 (2011-xx-xx)
 New command-line features
 -------------------------
 
-Add "notmuch restore --accumulate" option
+Add "--accumulate" and "--match" options to "notmuch restore".
 
-  The --accumulate switch causes the union of the existing and new tags to be
-  applied, instead of replacing each message's tags as they are read in from
-  the dump file.
+  The "--accumulate" switch causes the union of the existing and new
+  tags to be applied, instead of replacing each message's tags as they
+  are read in from the dump file.
+
+  The "--match=<regex>" option restricts the restore operation to tags
+  matching the regular expression regex. One potential application is
+  the implementation of namespaces for tags.
 
 Add search terms to  "notmuch dump"
 
diff --git a/notmuch.1 b/notmuch.1
index bba479e..57b2062 100644
--- a/notmuch.1
+++ b/notmuch.1
@@ -480,7 +480,7 @@ section below for details of the supported syntax for <search-terms>.
 .RE
 
 .TP
-.BR restore " [--accumulate] [<filename>]"
+.BR restore " [--accumulate] [--match=<regex>] [<filename>]"
 
 Restores the tags from the given file (see
 .BR "notmuch dump" ")."
@@ -494,10 +494,25 @@ So if you've previously been using sup for mail, then the
 command provides you a way to import all of your tags (or labels as
 sup calls them).
 
-The --accumulate switch causes the union of the existing and new tags to be
+Supported options for
+.B restore
+include
+.RS
+.TP 4
+.BR \-\-accumulate
+The union of the existing and new tags is
 applied, instead of replacing each message's tags as they are read in from the
 dump file.
 .RE
+.RS
+.TP 4
+.BR \-\-match=<regex>
+add or delete only tags matching 
+.B <regex>.
+Other tags in the input (or the database) are ignored.
+.RE
+
+.RE
 
 The
 .B part
diff --git a/notmuch.c b/notmuch.c
index e004c6c..3a51fee 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -401,9 +401,17 @@ static command_t commands[] = {
       "\tSo if you've previously been using sup for mail, then the\n"
       "\t\"notmuch restore\" command provides you a way to import\n"
       "\tall of your tags (or labels as sup calls them).\n"
-      "\tThe --accumulate switch causes the union of the existing and new\n"
-      "\ttags to be applied, instead of replacing each message's tags as\n"
-      "\tthey are read in from the dump file."},
+      "\tSupported options for restore include\n"
+      "\n"
+      "\t    --accumulate\n"
+      "\t      The union of the existing and new tags is applied, instead of\n"
+      "\t      replacing each message's tags as they are read  in  from  the\n"
+      "\t      dump file.\n"
+      "\n"
+      "\t    --match=<regex>\n"
+      "\t      Add or delete only tags matching <regex>.  Other tags in the\n"
+      "\t      input (or the database) are ignored.\n"
+    },
     { "config", notmuch_config_command,
       "[get|set] <section>.<item> [value ...]",
       "Get or set settings in the notmuch configuration file.",
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch::
  2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
@ 2011-10-29 21:11           ` Ali Polatel
  2011-10-29 22:12             ` David Bremner
  2011-11-07  0:59           ` [PATCH] contrib/nmbug: new script for sharing tags with a given prefix David Bremner
  1 sibling, 1 reply; 35+ messages in thread
From: Ali Polatel @ 2011-10-29 21:11 UTC (permalink / raw)
  To: David Bremner; +Cc: notmuch, David Bremner

[-- Attachment #1: Type: text/plain, Size: 2238 bytes --]

On Sat, Oct 29, 2011 at 01:45:07PM -0300, David Bremner wrote:
>From: David Bremner <bremner@debian.org>
>
>In this initial version, we take care of only the base import and
>export of the appropriate tags in line oriented format amenable to
>easy merging.
>
>The current plan to use git to share tags and resolve conflicts.
>---
> contrib/nmbug |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 69 insertions(+), 0 deletions(-)
> create mode 100755 contrib/nmbug
>
>diff --git a/contrib/nmbug b/contrib/nmbug
>new file mode 100755
>index 0000000..e9d1b14
>--- /dev/null
>+++ b/contrib/nmbug
>@@ -0,0 +1,69 @@
>+#!/bin/bash

^^ I'd make that:
#!/usr/bin/env bash
for systems where bash is not the default shell.

>+# Copyright (c) 2011 David Bremner
>+# License: same as notmuch
>+
>+NMHOME=${HOME}/.nmbug
>+
>+NMTAGS=($(notmuch search --output=tags "*"|grep "^notmuch::"))
>+
>+function dump() {
>+    notmuch dump -- $(printf  " tag:%s" ${NMTAGS[*]}) |\
>+    while read  -r msgid rest
>+    do
>+	outfile=$NMHOME/tags/$(echo $msgid | sha1sum - | cut -f1 -d' ')
>+	echo $outfile
>+	printf "msg-id: %s\n" $msgid > $outfile
>+	tmp=${rest#\(}
>+	read -r -a tags <<<${tmp%\)};
>+	for tag in "${tags[@]}"; do
>+	    case $tag in
>+		notmuch::*)
>+		    echo "tag: $tag" >> $outfile
>+		    ;;
>+		*)
>+		# nothing
>+	    esac
>+	done
>+    done
>+
>+}
>+
>+function cat_file() {
>+    tags=""
>+    id=""
>+    cat $1 |\
>+    while read -r what data
>+    do
>+	case $what in
>+	    msg-id:)
>+		printf "%s (" $data
>+		;;
>+	    tag:)
>+		printf "%s " $data
>+		;;
>+	    *)
>+		echo "Syntax error $what"
>+		exit 1
>+	esac
>+    done
>+    echo ")"
>+}
>+
>+function restore() {
>+    find $NMHOME/tags -type f |\
>+    while read -r filename
>+    do
>+	cat_file $filename
>+    done | notmuch restore --match=notmuch::
>+}
>+case $1 in
>+    dump)
>+	dump
>+	;;
>+    restore)
>+	restore
>+	;;
>+    *)
>+	echo unknown command $1;
>+esac
>+
>-- 
>1.7.6.3
>
>_______________________________________________
>notmuch mailing list
>notmuch@notmuchmail.org
>http://notmuchmail.org/mailman/listinfo/notmuch

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch::
  2011-10-29 21:11           ` Ali Polatel
@ 2011-10-29 22:12             ` David Bremner
  2011-10-30  0:21               ` Ali Polatel
  0 siblings, 1 reply; 35+ messages in thread
From: David Bremner @ 2011-10-29 22:12 UTC (permalink / raw)
  To: Ali Polatel; +Cc: notmuch

On Sun, 30 Oct 2011 00:11:35 +0300, Ali Polatel <polatel@gmail.com> wrote:
> ^^ I'd make that:
> #!/usr/bin/env bash
> for systems where bash is not the default shell.
> 

OK, I'll do that in the next version. I always forget about those people
who install bash in funny places.

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch::
  2011-10-29 22:12             ` David Bremner
@ 2011-10-30  0:21               ` Ali Polatel
  0 siblings, 0 replies; 35+ messages in thread
From: Ali Polatel @ 2011-10-30  0:21 UTC (permalink / raw)
  To: David Bremner; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 438 bytes --]

On Sat, Oct 29, 2011 at 07:12:52PM -0300, David Bremner wrote:
>On Sun, 30 Oct 2011 00:11:35 +0300, Ali Polatel <polatel@gmail.com> wrote:
>> ^^ I'd make that:
>> #!/usr/bin/env bash
>> for systems where bash is not the default shell.
>>
>
>OK, I'll do that in the next version. I always forget about those people
>who install bash in funny places.

Cool!
Just to note, it's the standards, not the people, which are funny...

>d

		-alip

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: Experimental Tag Sharing
  2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
  2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
  2011-10-29 17:27         ` [PATCH v2] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
@ 2011-10-30  0:44         ` Jameson Graef Rollins
  2011-10-30  2:18           ` David Bremner
  2 siblings, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-10-30  0:44 UTC (permalink / raw)
  To: David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 3479 bytes --]

On Sat, 29 Oct 2011 13:45:06 -0300, David Bremner <david@tethera.net> wrote:
> OK, here is my rough and ready attempt at tag sharing.  I figure we
> can smooth out the rough edges if/when we agree on a set of tags and
> preferably on an on-disk format.

Great!  Thank you so much for working on this, David.  This is very
exciting work.

Comments below.

> How to play?
> 
> - - Apply all the patches in this thread (starting
>   id:"1319383133-11006-1-git-send-email-david@tethera.net")

These patches did not apply cleanly for me from the list.  I was able to
get them from bremner's "nmbug" branch [0], though.

[0] git://pivot.cs.unb.ca/git/notmuch

> $ nmbug dump
> $ cd $HOME/.nmbug && git add tags && git commit

This last command seems to be included in nmbug as "commit".  Fwiw, I
don't like having to do two separate operations for dump and commit.
Could they be merged into a single operation?

> now you have to get the changes into the "master" repo.  I think
> hosting this on git.notmuchmail.org in a seperate repo will make
> sense, but for now, if you want to participate in the experiment, send
> me your public key (ideally in a gpg signed mail, but if not, oh well), 
> and you can push to my repo at
> 
>     git@pivot.cs.unb.ca:pub/nmbug

While I think having a central shared tag repo is ok to get started, I
would really like to see this work in a distributed way.  I don't think
it's impossible to extend this model you have here to work in a
distributed way, though, so that will be something good to work on down
the line.

> Probably the most crucial thing is that we agree on some set of tags.
> Here is the set of tags I am working with (output from 
>      notmuch search --output=tags "*" | grep ^notmuch:: 
> )
> 
> notmuch::bug   		     is a bug report
> notmuch::feature	     provides a new feature
> notmuch::fix		     fixes a bug
> notmuch::obsolete	     replaced by some other patch
> notmuch::patch		     
> notmuch::portability	     improves portability
> notmuch::pushed		     is pushed to master
> notmuch::review		     is a review
> notmuch::reviewed	     has been (well reviewed)
> notmuch::test		     provides a new test/or improves testing
> 
> The prefix "notmuch::" is hardcoded into the script, but everything
> else is easy to change with the usual notmuch operations.  Of course
> we can change the prefix too. I decided on :: because it needed less
> escaping.

I think these are a fine set to start with.

It might be nice to let the user configure what prefix they use
(ie. what goes before the "::" (currently "notmuch")).

Does this system support the removing of tags?  I guess I need to see
the documentation for the restore --match option...

It's too bad there's not a way to do scoped tag searches in notmuch
(ie. notmuch search tag:"notmuch::*").  Any idea how hard it would be to
support something like that?

> On think I'm not sure about is how to handle ideas like "voting", and
> "signing off" with tags. I suspect we can come up with some
> conventions like "notmuch::review1", or
> "notmuch::vote1".."notmuch::vote5". But maybe people have less kludgy
> ideas.

What issue are you trying to solve here?  Whatever it is, I don't think
I like enumerating tags as the solution.

Again, thanks so much for working on this, David.  This is going to be
very cool.  I really look forward to fleshing it out, and using it!

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: Experimental Tag Sharing
  2011-10-30  0:44         ` Experimental Tag Sharing Jameson Graef Rollins
@ 2011-10-30  2:18           ` David Bremner
  0 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-30  2:18 UTC (permalink / raw)
  To: Jameson Graef Rollins, notmuch

[-- Attachment #1: Type: text/plain, Size: 2849 bytes --]

On Sat, 29 Oct 2011 17:44:56 -0700, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> On Sat, 29 Oct 2011 13:45:06 -0300, David Bremner <david@tethera.net> wrote:
> 
> These patches did not apply cleanly for me from the list.  I was able to
> get them from bremner's "nmbug" branch [0], though.
> [0] git://pivot.cs.unb.ca/git/notmuch

I did resend the one patch that we figured out was corrupted, but for
the moment I will just rebase the nmbug branch that jrollins mentioned.

> > $ nmbug dump
> > $ cd $HOME/.nmbug && git add tags && git commit
[snip]

> Could they be merged into a single operation?

Yes, "commit" now (in my git repo) does both the dump and the git
commit.

> While I think having a central shared tag repo is ok to get started, I
> would really like to see this work in a distributed way.

So far I don't see any technical issues with working in a distributed
way, just social ones. Whatever organization we set up, I think it is
important for there to be a way to submit and manage bug reports for
"outsiders". Of course, requiring a notmuch install is already a burden.

> It might be nice to let the user configure what prefix they use
> (ie. what goes before the "::" (currently "notmuch")).

There are two simple-matters-of-programming here. One is how to do the
configuration; probably "nmbug config" can be an alias for "git config
nmbug.$1" or something. The other is translating between prefixes.
Neither is hard, I guess.

> Does this system support the removing of tags?  I guess I need to see
> the documentation for the restore --match option...

Yes, barring bugs, removing a tag in notmuch and running "nmbug commit"
should remove from the files in ~/.nmbug/tags

> It's too bad there's not a way to do scoped tag searches in notmuch
> (ie. notmuch search tag:"notmuch::*").  Any idea how hard it would be to
> support something like that?

I don't know. Carl thought it should work, but it doesn't seem to. 
My uneducated guess is it has something to do with query parsing.

> > On think I'm not sure about is how to handle ideas like "voting", and
> > "signing off" with tags. I suspect we can come up with some

> What issue are you trying to solve here?  Whatever it is, I don't think
> I like enumerating tags as the solution.

I'm trying to handle the issue of sharing non-boolean data. 
For example, 
 - how can we record priorities bugs/patches
 - how can we conveniently find all of the emacs patches (b.t.w I
   started using notmuch::emacs for those) that jrollins has endorsed
   but are not pushed.

Of course, nmbug could share more metadata than tags. OTOH I'd like to
avoid completely reinventing a distributed [0] bug tracker.

David


[0] well, at least potentially distributed, depending on definitions.






[-- Attachment #2: Type: application/pgp-signature, Size: 315 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
  2011-10-23 21:51 ` [PATCH] xregcomp: don't consider every regex compilation failure an internal error David Bremner
  2011-10-29 18:52 ` [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
@ 2011-10-31  0:04 ` Austin Clements
  2011-10-31  0:09 ` Jameson Graef Rollins
  2011-10-31  9:20 ` Jani Nikula
  4 siblings, 0 replies; 35+ messages in thread
From: Austin Clements @ 2011-10-31  0:04 UTC (permalink / raw)
  To: David Bremner; +Cc: notmuch, David Bremner

LGTM.  Code deduplication is always a good thing.

"Internal interfaces for notmuch." doesn't seem like the best summary
for util/error_util.h

On Sun, Oct 23, 2011 at 11:18 AM, David Bremner <david@tethera.net> wrote:
> From: David Bremner <bremner@debian.org>
>
> We keep the lib/xutil.c version. As a consequence, also factor out
> _internal_error and associated macros.  It might be overkill to make a
> new file error_util.c for this, but _internal_error does not really
> belong in database.cc.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
                   ` (2 preceding siblings ...)
  2011-10-31  0:04 ` Austin Clements
@ 2011-10-31  0:09 ` Jameson Graef Rollins
  2011-10-31  2:28   ` David Bremner
  2011-10-31  9:20 ` Jani Nikula
  4 siblings, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-10-31  0:09 UTC (permalink / raw)
  To: David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 978 bytes --]

On Sun, 23 Oct 2011 12:18:53 -0300, David Bremner <david@tethera.net> wrote:
> We keep the lib/xutil.c version. As a consequence, also factor out
> _internal_error and associated macros.  It might be overkill to make a
> new file error_util.c for this, but _internal_error does not really
> belong in database.cc.
> ---
> 
> This turned out to be more disruptive than I thought. On the other
> hand, having two copies of xutil.c seems like a recipe for disaster.
> I wanted to factor out the logic in xregcomp so I could use it in
> situations where miscompilation is not an internal error, but more
> likely a user error.

Hey, David.  Other than the fact that git couldn't digest the encoding
of this message, I would say the patch looks very reasonable to me.  It
looks to me like the right thing to do, and that you're doing it in a
reasonable way.

Once I got over the encoding issues, this patch applied cleanly and
seems to be working fine.

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-31  0:09 ` Jameson Graef Rollins
@ 2011-10-31  2:28   ` David Bremner
  0 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-31  2:28 UTC (permalink / raw)
  To: Jameson Graef Rollins, notmuch; +Cc: Austin Clements

[-- Attachment #1: Type: text/plain, Size: 377 bytes --]

On Sun, 30 Oct 2011 17:09:10 -0700, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> 
> Once I got over the encoding issues, this patch applied cleanly and
> seems to be working fine.
> 

OK, thanks Jamie and Austin for having a look. 

I pushed this patch, and the followup (changing xregcomp to return an
error code instead of exiting the program).

d

[-- Attachment #2: Type: application/pgp-signature, Size: 315 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
                   ` (3 preceding siblings ...)
  2011-10-31  0:09 ` Jameson Graef Rollins
@ 2011-10-31  9:20 ` Jani Nikula
  2011-10-31 10:51   ` David Bremner
  2011-10-31 18:50   ` Jameson Graef Rollins
  4 siblings, 2 replies; 35+ messages in thread
From: Jani Nikula @ 2011-10-31  9:20 UTC (permalink / raw)
  To: David Bremner, notmuch; +Cc: David Bremner


Hi David, this patch (commit 1dedfc90f6eee7cad10f1a1ceb39a7a1c4dbd1b1)
broke my build with:

CXX -O2 lib/libnotmuch.so.2.0.0
lib/message-file.o: In function `copy_header_unfolding':
message-file.c:(.text+0xea): undefined reference to `xrealloc'
lib/message-file.o: In function
`notmuch_message_file_restrict_headersv':
message-file.c:(.text+0x2cc): undefined reference to `xstrdup'
lib/message-file.o: In function `notmuch_message_file_get_header':
message-file.c:(.text+0x4af): undefined reference to `xstrndup'
message-file.c:(.text+0x5c8): undefined reference to `xmalloc'
lib/messages.o: In function `notmuch_messages_collect_tags':
messages.c:(.text+0x1c4): undefined reference to `xstrdup'
lib/sha1.o: In function `_hex_of_sha1_digest':
sha1.c:(.text+0x18): undefined reference to `xcalloc'
lib/thread.o: In function `_notmuch_thread_create':
thread.cc:(.text+0x34e): undefined reference to `xstrdup'
thread.cc:(.text+0x58c): undefined reference to `xstrdup'
/usr/bin/ld: lib/libnotmuch.so.2.0.0: hidden symbol `xmalloc' isn't
defined
/usr/bin/ld: final link failed: Bad value
collect2: ld returned 1 exit status
make: *** [lib/libnotmuch.so.2.0.0] Error 1

I don't have the time to debug this further right now, but I bisected
the failure to this commit and did a clean clone to make sure leftover
files weren't the cause.

BR,
Jani.


On Sun, 23 Oct 2011 12:18:53 -0300, David Bremner <david@tethera.net> wrote:
> From: David Bremner <bremner@debian.org>
> 
> We keep the lib/xutil.c version. As a consequence, also factor out
> _internal_error and associated macros.  It might be overkill to make a
> new file error_util.c for this, but _internal_error does not really
> belong in database.cc.
> ---
> 
> This turned out to be more disruptive than I thought. On the other
> hand, having two copies of xutil.c seems like a recipe for disaster.
> I wanted to factor out the logic in xregcomp so I could use it in
> situations where miscompilation is not an internal error, but more
> likely a user error.
> 
>  Makefile              |    2 +-
>  Makefile.local        |    7 +--
>  lib/Makefile.local    |    5 +-
>  lib/database.cc       |   15 -----
>  lib/notmuch-private.h |   20 +-------
>  lib/xutil.c           |  134 -----------------------------------------------
>  lib/xutil.h           |   55 -------------------
>  util/Makefile         |    5 ++
>  util/Makefile.local   |   11 ++++
>  util/error_util.c     |   41 +++++++++++++++
>  util/error_util.h     |   45 ++++++++++++++++
>  util/xutil.c          |  136 ++++++++++++++++++++++++++++++++++++++++++++++++
>  util/xutil.h          |   55 +++++++++++++++++++
>  xutil.c               |  138 -------------------------------------------------
>  14 files changed, 300 insertions(+), 369 deletions(-)
>  delete mode 100644 lib/xutil.c
>  delete mode 100644 lib/xutil.h
>  create mode 100644 util/Makefile
>  create mode 100644 util/Makefile.local
>  create mode 100644 util/error_util.c
>  create mode 100644 util/error_util.h
>  create mode 100644 util/xutil.c
>  create mode 100644 util/xutil.h
>  delete mode 100644 xutil.c
> 
> diff --git a/Makefile b/Makefile
> index 11e3a3d..2fb2a61 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -3,7 +3,7 @@
>  all:
>  
>  # List all subdirectories here. Each contains its own Makefile.local
> -subdirs = compat completion emacs lib test
> +subdirs = compat completion emacs lib util test
>  
>  # We make all targets depend on the Makefiles themselves.
>  global_deps = Makefile Makefile.config Makefile.local \
> diff --git a/Makefile.local b/Makefile.local
> index 38f6c17..e31defa 100644
> --- a/Makefile.local
> +++ b/Makefile.local
> @@ -39,7 +39,7 @@ PV_FILE=bindings/python/notmuch/version.py
>  # Smash together user's values with our extra values
>  FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
>  FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS) $(extra_cflags) $(extra_cxxflags)
> -FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
> +FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
>  FINAL_NOTMUCH_LINKER = CC
>  ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
>  FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
> @@ -288,12 +288,11 @@ notmuch_client_srcs =		\
>  	notmuch-time.c		\
>  	query-string.c		\
>  	show-message.c		\
> -	json.c			\
> -	xutil.c
> +	json.c			
>  
>  notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
>  
> -notmuch: $(notmuch_client_modules) lib/libnotmuch.a
> +notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a
>  	$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
>  
>  notmuch-shared: $(notmuch_client_modules) lib/$(LINKER_NAME)
> diff --git a/lib/Makefile.local b/lib/Makefile.local
> index ea20b2b..f148661 100644
> --- a/lib/Makefile.local
> +++ b/lib/Makefile.local
> @@ -49,8 +49,7 @@ libnotmuch_c_srcs =		\
>  	$(dir)/message-file.c	\
>  	$(dir)/messages.c	\
>  	$(dir)/sha1.c		\
> -	$(dir)/tags.c		\
> -	$(dir)/xutil.c
> +	$(dir)/tags.c		
>  
>  libnotmuch_cxx_srcs =		\
>  	$(dir)/database.cc	\
> @@ -66,7 +65,7 @@ $(dir)/libnotmuch.a: $(libnotmuch_modules)
>  	$(call quiet,AR) rcs $@ $^
>  
>  $(dir)/$(LIBNAME): $(libnotmuch_modules) notmuch.sym
> -	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@
> +	$(call quiet,CXX $(CXXFLAGS)) $(libnotmuch_modules) $(FINAL_LIBNOTMUCH_LDFLAGS) $(LIBRARY_LINK_FLAG) -o $@ -L$(srcdir)/util -lutil
>  
>  notmuch.sym: lib/notmuch.h $(libnotmuch_modules)
>  	sh lib/gen-version-script.sh $< $(libnotmuch_modules) > $@
> diff --git a/lib/database.cc b/lib/database.cc
> index e77fd53..88be939 100644
> --- a/lib/database.cc
> +++ b/lib/database.cc
> @@ -209,21 +209,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
>      { "folder",			"XFOLDER"}
>  };
>  
> -int
> -_internal_error (const char *format, ...)
> -{
> -    va_list va_args;
> -
> -    va_start (va_args, format);
> -
> -    fprintf (stderr, "Internal error: ");
> -    vfprintf (stderr, format, va_args);
> -
> -    exit (1);
> -
> -    return 1;
> -}
> -
>  const char *
>  _find_prefix (const char *name)
>  {
> diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
> index d319530..0d3cc27 100644
> --- a/lib/notmuch-private.h
> +++ b/lib/notmuch-private.h
> @@ -47,6 +47,7 @@ NOTMUCH_BEGIN_DECLS
>  #include <talloc.h>
>  
>  #include "xutil.h"
> +#include "error_util.h"
>  
>  #pragma GCC visibility push(hidden)
>  
> @@ -60,25 +61,6 @@ NOTMUCH_BEGIN_DECLS
>  #define STRNCMP_LITERAL(var, literal) \
>      strncmp ((var), (literal), sizeof (literal) - 1)
>  
> -/* There's no point in continuing when we've detected that we've done
> - * something wrong internally (as opposed to the user passing in a
> - * bogus value).
> - *
> - * Note that PRINTF_ATTRIBUTE comes from talloc.h
> - */
> -int
> -_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
> -
> -/* There's no point in continuing when we've detected that we've done
> - * something wrong internally (as opposed to the user passing in a
> - * bogus value).
> - *
> - * Note that __location__ comes from talloc.h.
> - */
> -#define INTERNAL_ERROR(format, ...)			\
> -    _internal_error (format " (%s).\n",			\
> -		     ##__VA_ARGS__, __location__)
> -
>  #define unused(x) x __attribute__ ((unused))
>  
>  #ifdef __cplusplus
> diff --git a/lib/xutil.c b/lib/xutil.c
> deleted file mode 100644
> index 268225b..0000000
> diff --git a/lib/xutil.h b/lib/xutil.h
> deleted file mode 100644
> index fd77f73..0000000
> diff --git a/util/Makefile b/util/Makefile
> new file mode 100644
> index 0000000..fa25832
> --- /dev/null
> +++ b/util/Makefile
> @@ -0,0 +1,5 @@
> +all:
> +	$(MAKE) -C .. all
> +
> +.DEFAULT:
> +	$(MAKE) -C .. $@
> diff --git a/util/Makefile.local b/util/Makefile.local
> new file mode 100644
> index 0000000..2ff42b3
> --- /dev/null
> +++ b/util/Makefile.local
> @@ -0,0 +1,11 @@
> +# -*- makefile -*-
> +
> +dir := util
> +extra_cflags += -I$(srcdir)/$(dir)
> +
> +libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c
> +
> +libutil_modules := $(libutil_c_srcs:.c=.o)
> +
> +$(dir)/libutil.a: $(libutil_modules)
> +	$(call quiet,AR) rcs $@ $^
> diff --git a/util/error_util.c b/util/error_util.c
> new file mode 100644
> index 0000000..630d228
> --- /dev/null
> +++ b/util/error_util.c
> @@ -0,0 +1,41 @@
> +/* error_util.c - internal error utilities for notmuch.
> + *
> + * Copyright © 2009 Carl Worth
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see http://www.gnu.org/licenses/ .
> + *
> + * Author: Carl Worth <cworth@cworth.org>
> + */
> +
> +#include <stdlib.h>
> +#include <stdarg.h>
> +#include <stdio.h>
> +
> +#include "error_util.h"
> +
> +int
> +_internal_error (const char *format, ...)
> +{
> +    va_list va_args;
> +
> +    va_start (va_args, format);
> +
> +    fprintf (stderr, "Internal error: ");
> +    vfprintf (stderr, format, va_args);
> +
> +    exit (1);
> +
> +    return 1;
> +}
> +
> diff --git a/util/error_util.h b/util/error_util.h
> new file mode 100644
> index 0000000..0f1e5ef
> --- /dev/null
> +++ b/util/error_util.h
> @@ -0,0 +1,45 @@
> +/* error_util.h - Internal interfaces for notmuch.
> + *
> + * Copyright © 2009 Carl Worth
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see http://www.gnu.org/licenses/ .
> + *
> + * Author: Carl Worth <cworth@cworth.org>
> + */
> +
> +#ifndef ERROR_UTIL_H
> +#define ERROR_UTIL_H
> +
> +#include <talloc.h>
> +
> +/* There's no point in continuing when we've detected that we've done
> + * something wrong internally (as opposed to the user passing in a
> + * bogus value).
> + *
> + * Note that PRINTF_ATTRIBUTE comes from talloc.h
> + */
> +int
> +_internal_error (const char *format, ...) PRINTF_ATTRIBUTE (1, 2);
> +
> +/* There's no point in continuing when we've detected that we've done
> + * something wrong internally (as opposed to the user passing in a
> + * bogus value).
> + *
> + * Note that __location__ comes from talloc.h.
> + */
> +#define INTERNAL_ERROR(format, ...)			\
> +    _internal_error (format " (%s).\n",			\
> +		     ##__VA_ARGS__, __location__)
> +
> +#endif
> diff --git a/util/xutil.c b/util/xutil.c
> new file mode 100644
> index 0000000..15ff765
> --- /dev/null
> +++ b/util/xutil.c
> @@ -0,0 +1,136 @@
> +/* xutil.c - Various wrapper functions to abort on error.
> + *
> + * Copyright © 2009 Carl Worth
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see http://www.gnu.org/licenses/ .
> + *
> + * Author: Carl Worth <cworth@cworth.org>
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include "xutil.h"
> +#include "error_util.h"
> +
> +void *
> +xcalloc (size_t nmemb, size_t size)
> +{
> +    void *ret;
> +
> +    ret = calloc (nmemb, size);
> +    if (ret == NULL) {
> +	fprintf (stderr, "Out of memory.\n");
> +	exit (1);
> +    }
> +
> +    return ret;
> +}
> +
> +void *
> +xmalloc (size_t size)
> +{
> +    void *ret;
> +
> +    ret = malloc (size);
> +    if (ret == NULL) {
> +	fprintf (stderr, "Out of memory.\n");
> +	exit (1);
> +    }
> +
> +    return ret;
> +}
> +
> +void *
> +xrealloc (void *ptr, size_t size)
> +{
> +    void *ret;
> +
> +    ret = realloc (ptr, size);
> +    if (ret == NULL) {
> +	fprintf (stderr, "Out of memory.\n");
> +	exit (1);
> +    }
> +
> +    return ret;
> +}
> +
> +char *
> +xstrdup (const char *s)
> +{
> +    char *ret;
> +
> +    ret = strdup (s);
> +    if (ret == NULL) {
> +	fprintf (stderr, "Out of memory.\n");
> +	exit (1);
> +    }
> +
> +    return ret;
> +}
> +
> +char *
> +xstrndup (const char *s, size_t n)
> +{
> +    char *ret;
> +
> +    if (strlen (s) <= n)
> +	n = strlen (s);
> +
> +    ret = malloc (n + 1);
> +    if (ret == NULL) {
> +	fprintf (stderr, "Out of memory.\n");
> +	exit (1);
> +    }
> +    memcpy (ret, s, n);
> +    ret[n] = '\0';
> +
> +    return ret;
> +}
> +
> +void
> +xregcomp (regex_t *preg, const char *regex, int cflags)
> +{
> +    int rerr;
> +
> +    rerr = regcomp (preg, regex, cflags);
> +    if (rerr) {
> +	size_t error_size = regerror (rerr, preg, NULL, 0);
> +	char *error = xmalloc (error_size);
> +
> +	regerror (rerr, preg, error, error_size);
> +	INTERNAL_ERROR ("compiling regex %s: %s\n",
> +			regex, error);
> +    }
> +}
> +
> +int
> +xregexec (const regex_t *preg, const char *string,
> +	  size_t nmatch, regmatch_t pmatch[], int eflags)
> +{
> +    unsigned int i;
> +    int rerr;
> +
> +    rerr = regexec (preg, string, nmatch, pmatch, eflags);
> +    if (rerr)
> +	return rerr;
> +
> +    for (i = 0; i < nmatch; i++) {
> +	if (pmatch[i].rm_so == -1)
> +	    INTERNAL_ERROR ("matching regex against %s: Sub-match %d not found\n",
> +			    string, i);
> +    }
> +
> +    return 0;
> +}
> diff --git a/util/xutil.h b/util/xutil.h
> new file mode 100644
> index 0000000..fd77f73
> --- /dev/null
> +++ b/util/xutil.h
> @@ -0,0 +1,55 @@
> +/* xutil.h - Various wrapper functions to abort on error.
> + *
> + * Copyright © 2009 Carl Worth
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see http://www.gnu.org/licenses/ .
> + *
> + * Author: Carl Worth <cworth@cworth.org>
> + */
> +
> +#ifndef NOTMUCH_XUTIL_H
> +#define NOTMUCH_XUTIL_H
> +
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include <regex.h>
> +
> +#pragma GCC visibility push(hidden)
> +
> +/* xutil.c */
> +void *
> +xcalloc (size_t nmemb, size_t size);
> +
> +void *
> +xmalloc (size_t size);
> +
> +void *
> +xrealloc (void *ptrr, size_t size);
> +
> +char *
> +xstrdup (const char *s);
> +
> +char *
> +xstrndup (const char *s, size_t n);
> +
> +void
> +xregcomp (regex_t *preg, const char *regex, int cflags);
> +
> +int
> +xregexec (const regex_t *preg, const char *string,
> +	  size_t nmatch, regmatch_t pmatch[], int eflags);
> +
> +#pragma GCC visibility pop
> +
> +#endif
> diff --git a/xutil.c b/xutil.c
> deleted file mode 100644
> index 5f98f3f..0000000
> -- 
> 1.7.6.3
> 
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-31  9:20 ` Jani Nikula
@ 2011-10-31 10:51   ` David Bremner
  2011-10-31 18:50   ` Jameson Graef Rollins
  1 sibling, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-10-31 10:51 UTC (permalink / raw)
  To: Jani Nikula; +Cc: notmuch

On Mon, 31 Oct 2011 09:20:13 +0000, Jani Nikula <jani@nikula.org> wrote:
> 
> Hi David, this patch (commit 1dedfc90f6eee7cad10f1a1ceb39a7a1c4dbd1b1)
> broke my build with:
> 
> CXX -O2 lib/libnotmuch.so.2.0.0

Can you try "make V=1"? The output should look like

g++ lib/filenames.o lib/string-list.o lib/libsha1.o lib/message-file.o lib/messages.o lib/sha1.o lib/tags.o lib/database.o lib/directory.o lib/index.o lib/message.o lib/query.o lib/thread.o  -Wl,--as-needed -pthread -lgmime-2.4 -lgobject-2.0 -lgthread-2.0 -lrt -lglib-2.0   -ltalloc   -lxapian -shared -Wl,--version-script=notmuch.sym,-soname=libnotmuch.so.2 -o lib/libnotmuch.so.2.0.0 -L./util -lutil

The last part, "-L./util -lutil" is where all those symbols are.

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-31  9:20 ` Jani Nikula
  2011-10-31 10:51   ` David Bremner
@ 2011-10-31 18:50   ` Jameson Graef Rollins
  2011-10-31 19:19     ` Jani Nikula
  1 sibling, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-10-31 18:50 UTC (permalink / raw)
  To: Jani Nikula, David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 298 bytes --]

On Mon, 31 Oct 2011 09:20:13 +0000, Jani Nikula <jani@nikula.org> wrote:
> I don't have the time to debug this further right now, but I bisected
> the failure to this commit and did a clean clone to make sure leftover
> files weren't the cause.

Does it work if you make clean before make?

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH] Fix formatting for restore and dump help documentation.
  2011-10-29 20:55       ` [PATCH] docs: man, online help, and NEWS updated for notmuch restore --match David Bremner
@ 2011-10-31 19:01         ` Jameson Graef Rollins
  2011-11-01  0:19           ` David Bremner
  0 siblings, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-10-31 19:01 UTC (permalink / raw)
  To: Notmuch Mail

This normalizes the code and output formatting here to look like the
rest of the help output.
---
 notmuch.c |   22 +++++++++++-----------
 1 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/notmuch.c b/notmuch.c
index 3a51fee..14e5ad5 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -390,8 +390,8 @@ static command_t commands[] = {
       "\tdatabase will be generated. A \"--\" argument instructs\n"
       "\tnotmuch that the remaining arguments are search terms.\n"
       "\n"
-      "\tSee \"notmuch help search-terms\" for the search-term syntax.\n"      
- },
+      "\tSee \"notmuch help search-terms\" for details of the search\n"
+      "\tterms syntax." },
     { "restore", notmuch_restore_command,
       "[--accumulate] [<filename>]",
       "Restore the tags from the given dump file (see 'dump').",
@@ -401,17 +401,17 @@ static command_t commands[] = {
       "\tSo if you've previously been using sup for mail, then the\n"
       "\t\"notmuch restore\" command provides you a way to import\n"
       "\tall of your tags (or labels as sup calls them).\n"
-      "\tSupported options for restore include\n"
       "\n"
-      "\t    --accumulate\n"
-      "\t      The union of the existing and new tags is applied, instead of\n"
-      "\t      replacing each message's tags as they are read  in  from  the\n"
-      "\t      dump file.\n"
+      "\tSupported options for restore include:\n"
+      "\n"
+      "\t--accumulate\n"
+      "\t\tThe union of the existing and new tags is applied, instead of\n"
+      "\t\treplacing each message's tags as they are read  in  from  the\n"
+      "\t\tdump file.\n"
       "\n"
-      "\t    --match=<regex>\n"
-      "\t      Add or delete only tags matching <regex>.  Other tags in the\n"
-      "\t      input (or the database) are ignored.\n"
-    },
+      "\t--match=<regex>\n"
+      "\t\tAdd or delete only tags matching <regex>.  Other tags in the\n"
+      "\t\tinput (or the database) are ignored." },
     { "config", notmuch_config_command,
       "[get|set] <section>.<item> [value ...]",
       "Get or set settings in the notmuch configuration file.",
-- 
1.7.7

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-31 18:50   ` Jameson Graef Rollins
@ 2011-10-31 19:19     ` Jani Nikula
  2011-10-31 19:20       ` Jameson Graef Rollins
  0 siblings, 1 reply; 35+ messages in thread
From: Jani Nikula @ 2011-10-31 19:19 UTC (permalink / raw)
  To: Jameson Graef Rollins, David Bremner, notmuch

On Mon, 31 Oct 2011 11:50:30 -0700, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> On Mon, 31 Oct 2011 09:20:13 +0000, Jani Nikula <jani@nikula.org> wrote:
> > I don't have the time to debug this further right now, but I bisected
> > the failure to this commit and did a clean clone to make sure leftover
> > files weren't the cause.
> 
> Does it work if you make clean before make?

Hi, this was fixed by id:"yf67h3lgyjr.fsf@taco2.nixu.fi", committed as
556c5fa8900d14c61544f3ac016d3cca1db5a91a.

BR,
Jani.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil.
  2011-10-31 19:19     ` Jani Nikula
@ 2011-10-31 19:20       ` Jameson Graef Rollins
  0 siblings, 0 replies; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-10-31 19:20 UTC (permalink / raw)
  To: Jani Nikula, David Bremner, notmuch

On Mon, 31 Oct 2011 21:19:09 +0200, Jani Nikula <jani@nikula.org> wrote:
> Hi, this was fixed by id:"yf67h3lgyjr.fsf@taco2.nixu.fi", committed as
> 556c5fa8900d14c61544f3ac016d3cca1db5a91a.

Ah, ok.  Great.

jamie.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fix formatting for restore and dump help documentation.
  2011-10-31 19:01         ` [PATCH] Fix formatting for restore and dump help documentation Jameson Graef Rollins
@ 2011-11-01  0:19           ` David Bremner
  0 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-11-01  0:19 UTC (permalink / raw)
  To: Jameson Graef Rollins, Notmuch Mail

On Mon, 31 Oct 2011 12:01:25 -0700, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> This normalizes the code and output formatting here to look like the
> rest of the help output.

Thanks. I rebased that into the corresponding patch in branch nmbug at

        git://pivot.cs.unb.ca/notmuch.git

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
  2011-10-29 21:11           ` Ali Polatel
@ 2011-11-07  0:59           ` David Bremner
  2011-11-07 12:58             ` Tomi Ollila
  2011-11-11 15:08             ` [PATCH v3] " David Bremner
  1 sibling, 2 replies; 35+ messages in thread
From: David Bremner @ 2011-11-07  0:59 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

The main idea is consider the notmuch database as analogous to the
work-tree. A bare git repo is maintained in the users home directory,
with a tree of the form tags/$message-id/$tag

Like notmuch and git, we have a set of subcommnds, mainly modelled on
git.

The most important commands are

    commit	xapian -> git
    checkout	git -> xapian
    merge	fetched git + git -> xapian
    status	find differences between xapian, git, and remote git.

There are also some convenience wrappers around git commands.

In order to encode tags (viewed as octet sequences) into filenames,
we whitelist a smallish set of characters and %hex escape anything outside.

The prefix is omitted in git, which lets one save and restore to
different prefixes (although this is only lightly tested).
---

Many things have changed, time for a repost. It no long needs the
"restore --match" patches. 

 contrib/nmbug |  565 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 565 insertions(+), 0 deletions(-)
 create mode 100755 contrib/nmbug

diff --git a/contrib/nmbug b/contrib/nmbug
new file mode 100755
index 0000000..2128b95
--- /dev/null
+++ b/contrib/nmbug
@@ -0,0 +1,565 @@
+#!/usr/bin/env perl
+# Copyright (c) 2011 David Bremner
+# License: same as notmuch
+
+use strict;
+use File::Path qw(remove_tree make_path);
+use File::Temp qw(tempdir tempfile);
+use File::Basename;
+use Pod::Usage;
+
+no encoding;
+
+my $NMBGIT = $ENV{NMBGIT} || $ENV{HOME}."/.nmbug";
+
+$NMBGIT .= '/.git' if (-d $NMBGIT.'/.git');
+
+my $TAGPREFIX = $ENV{NMBPREFIX} || "notmuch::";
+
+# magic hashes for git
+my $EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391';
+my $EMPTYTREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+
+# for encoding
+
+my $ESCAPE_CHAR='%';
+my $NO_ESCAPE= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_@=.:,";
+my $MUST_ENCODE=qr{[^\Q$NO_ESCAPE\E]};
+my $ESCAPED_RX=qr{$ESCAPE_CHAR[A-Fa-f0-9]{2}};
+
+my %command=(
+	     archive	=> \&do_archive,
+	     checkout	=>\&do_checkout,
+	     commit	=> \&do_commit,
+	     fetch	=> \&do_fetch,
+	     help	=> \&do_help,
+	     log	=> \&do_log,
+	     merge	=> \&do_merge,
+	     pull	=> \&do_pull,
+	     push	=> \&do_push,
+	     status	=> \&do_status,
+	     );
+
+my $subcommand=shift;
+
+if (!exists $command{$subcommand}){
+  usage();
+}
+
+&{$command{$subcommand}}(@ARGV);
+
+sub get_tags {
+  my $prefix=shift;
+  my @tags;
+  open my $fh, 'notmuch search --output=tags "*"|' or die "error dumping tags";
+  while (<$fh>) {
+    chomp();
+    push @tags, $_ if (m/^$prefix/);
+  }
+  return @tags;
+}
+
+sub do_archive {
+  system ('git', "--git-dir=$NMBGIT", 'archive', 'HEAD');
+}
+
+sub is_committed(){
+  my $status = compute_status();
+  return scalar (@{$status->{added}} ) + scalar (@{$status->{deleted}} )==0
+}
+
+sub do_commit {
+  my @args=@_;
+
+  if ( is_committed() ){
+    print "Nothing to commit\n";
+    return;
+  }
+
+  my $index=index_tags();
+
+  my $tree= git ('write-tree', { GIT_INDEX_FILE=>$index })
+    or die "no output from write-tree";
+
+  my $parent = git ( 'rev-parse', 'HEAD'  )
+    or die "no output from rev-parse";
+
+  my $commit = git ('commit-tree', $tree, '-p', $parent, {},  @args);
+
+  git ('update-ref', 'HEAD', $commit);
+
+  unlink $index || die "unlink: $!";
+
+}
+
+sub do_fetch {
+  my $remote = shift || "origin";
+
+  git ('fetch', $remote);
+}
+
+sub git {
+
+  return subcommand('git',@_);
+
+}
+
+sub subcommand {
+  die 'command and subcommand needed' unless (scalar(@_) >= 2);
+
+  return run (@_);
+}
+
+# arguments are any number of strings for ARGV, an optional hash ref
+# for settings for the environment, followed by any number of strings as 
+# as lines for stdin.
+
+sub run {
+
+  my $command=shift 
+    or die "command not specified";
+
+  my @args;
+
+
+  while ( scalar(@_) && !ref($_[0]) ) {
+    push @args, shift;
+  };
+
+  my %SETENV={};
+  if(ref($_[0]) eq 'HASH'){
+    my $ref=shift;
+    %SETENV=%{$ref};
+  }
+
+  $SETENV{GIT_DIR} ||= $NMBGIT;
+
+  my @input=@_;
+  my @output;
+
+  my $pid = open my $child, '-|';
+  if ($pid){
+    while(<$child>){
+      chomp();
+      push @output,$_;
+    }
+    close ($child);
+  } else {
+
+    # setup child environment
+    while (my ($key,$val) = each %SETENV) {
+      $ENV{$key}=$val;
+    }
+
+    read_from(@input);
+    exec($command,@args) || die "exec $command @args: $!";
+  }
+
+  if (wantarray()) {
+    return @output;
+  }
+  elsif (defined wantarray()) {
+    return join("\n",@output);
+  }
+  else {
+    return;
+  }
+}
+
+sub read_from {
+    close STDIN;
+    if (!scalar(@_)){
+      open STDIN, '<', '/dev/null' || die "reopening stdin: $!";
+    } else {
+      my ($fh,$tempfile) = tempfile();
+      foreach my $line (@_){
+	print $fh $line;
+      }
+      close $fh || die "closing";
+
+      open STDIN, '<', $tempfile or
+	die "reopening stdin: $!";
+    }
+}
+
+sub notmuch {
+  my @args=@_;
+  system ('notmuch', @args) == 0 or die  "notmuch @args failed: $?"
+}
+
+
+sub index_tags {
+
+  my $index=$NMBGIT."/nmbug.index";
+
+  my $query = join " ", map ("tag:$_", get_tags($TAGPREFIX));
+  open my $fh, "notmuch dump -- $query|" or die "notmuch dump: $!";
+
+  git ('read-tree', $EMPTYTREE);
+  open my $git, "|GIT_DIR=$NMBGIT GIT_INDEX_FILE=$index git update-index --index-info" or die "git update-index";
+
+  while (<$fh>) {
+    m/ ( [^ ]* ) \s+ \( ([^\)]* ) \) /x || die "syntax error in dump";
+    my ($id,$rest) = ($1,$2);
+    index_tags_for_msg ($git,$id, split(" ", $rest));
+  }
+
+  close $git;
+  return $index;
+}
+
+sub index_tags_for_msg {
+  my $fh=shift;
+  my $msgid = shift;
+  my @tags=@_;
+
+  foreach my $tag (@tags){
+    # insist prefix is there, but remove it before writing
+    next unless ($tag =~ s/^$TAGPREFIX//);
+    my $tagpath = 'tags/' . encode_for_fs($msgid)  . '/' . encode_for_fs ($tag);
+    print $fh "100644 blob $EMPTYBLOB\t$tagpath\n";
+  }
+}
+
+sub do_checkout {
+  do_sync (action => 'checkout');
+}
+
+sub do_sync {
+
+  my %args=@_;
+
+  my $status=compute_status();
+  my ($A_action, $D_action);
+
+  if ($args{action} eq 'checkout') {
+    $A_action = '-';
+    $D_action = '+';
+  } else {
+    $A_action = '+';
+    $D_action = '-';
+  }
+
+  foreach my $pair (@{$status->{added}}){
+
+    notmuch ('tag', $A_action.$TAGPREFIX.$pair->{tag},
+	     'id:'.$pair->{id});
+  }
+
+  foreach my $pair (@{$status->{deleted}}){
+    notmuch ('tag', $D_action.$TAGPREFIX.$pair->{tag},
+	     'id:'.$pair->{id});
+  }
+
+}
+
+sub insist_committed {
+
+  if ( !is_committed () ){
+    print "Uncommitted changes to $TAGPREFIX* tags in notmuch
+
+For a summary of changes, run 'nmbug status'
+To save your changes,     run 'nmbug commit' before merging/pull
+To discard your changes,  run 'nmbug checkout'
+";
+    exit(1);
+  }
+
+}
+
+sub do_pull {
+  my $remote = shift || "origin";
+
+  git ( 'fetch', $remote);
+
+  do_merge();
+}
+
+sub do_merge {
+  insist_committed();
+
+  my $tempwork= tempdir ("/tmp/nmbug-merge.XXXXXX", CLEANUP=>1);
+
+  git ( 'checkout', '-f', 'HEAD', { GIT_WORK_TREE=> $tempwork });
+
+  git ( 'merge', 'FETCH_HEAD', { GIT_WORK_TREE=> $tempwork });
+
+  do_checkout();
+}
+
+sub do_log {
+  # we don't want output trapping here, because we want the pager.
+  system ( 'git', "--git-dir=$NMBGIT", 'log', '--name-status', @_);
+}
+
+sub do_push {
+  my $remote = shift || "origin";
+
+  git ('push', $remote);
+}
+
+sub do_status {
+  my $status = compute_status();
+  
+  foreach my $pair (@{$status->{added}}){
+    printf "A\t%s\t%s\n",$pair->{id}, $pair->{tag};
+  }
+
+  foreach my $pair (@{$status->{deleted}}){
+    printf "D\t%s\t%s\n",$pair->{id}, $pair->{tag};
+  }
+
+  foreach my $id (@{$status->{missing}}){
+    print "U\t$id\n",
+  }
+
+  if (is_unmerged ()) {
+    foreach my $pair (diff_refs('A')){
+      printf "a\t%s\t%s\n",$pair->{id}, $pair->{tag};
+    }
+
+    foreach my $pair (diff_refs('D')){
+      printf "d\t%s\t%s\n",$pair->{id}, $pair->{tag};
+    }
+  }
+
+}
+
+sub is_unmerged {
+  my $fetch_head = git ('rev-parse', 'FETCH_HEAD');
+  my $base = git ( 'merge-base', 'HEAD', 'FETCH_HEAD');
+
+  return ($base ne $fetch_head);
+
+}
+sub compute_status {
+  my %args=@_;
+
+  my @added;
+  my @deleted;
+  my @missing;
+
+  my $index=index_tags();
+
+  my @maybe_deleted = diff_index($index,'D');
+
+  foreach my $pair (@maybe_deleted){
+
+    my $id = $pair->{id};
+
+    open my $fh, "notmuch search --output=files id:$id |"
+      or die "searching for $id";
+    if (!<$fh>) {
+      push @missing, $id;
+    } else {
+      push @deleted, $pair;
+    }
+  }
+
+
+  @added = diff_index ($index, 'A');
+
+  unlink $index || die "unlink $index: $!";
+
+  return { added => [@added], deleted => [@deleted], missing=> [@missing] };
+}
+
+sub diff_index {
+  my $index=shift;
+  my $filter=shift;
+
+  my @lines=git( qw/diff-index --cached/,
+		 "--diff-filter=$filter", qw/--name-only HEAD/,
+		 {GIT_INDEX_FILE=>$index} );
+
+  return unpack_diff_lines(@lines);
+}
+
+sub diff_refs {
+  my $filter=shift;
+  my $ref1 = shift || 'HEAD';
+  my $ref2 = shift || 'FETCH_HEAD';
+
+  my @lines=git( 'diff', "--diff-filter=$filter", '--name-only',
+		 $ref1, $ref2);
+
+  return unpack_diff_lines(@lines);
+}
+
+
+sub unpack_diff_lines {
+  my @found;
+
+  foreach (@_){
+    chomp();
+    my ($id,$tag) = m@tags/ ([^/]+) / ([^/]+) @x;
+
+    $id = decode_from_fs($id);
+    $tag = decode_from_fs($tag);
+
+    push @found, { id => $id, tag => $tag };
+  }
+
+  return @found;
+}
+
+sub encode_for_fs{
+  my $str=shift;
+
+  $str=~ s/($MUST_ENCODE)/"$ESCAPE_CHAR".sprintf("%02x",ord($1))/ge;
+  return $str;
+}
+
+sub decode_from_fs{
+  my $str=shift;
+
+  $str=~ s/$ESCAPED_RX/ hex($1)/eg;
+
+  return $str;
+
+}
+
+
+sub usage {
+  pod2usage();
+  exit(1);
+}
+
+sub do_help {
+  pod2usage( -verbose=>2 );
+  exit(0);
+}
+
+__END__
+
+=head1 NAME
+
+nmbug - manage notmuch tags about notmuch
+
+=head1 SYNOPSIS
+
+nmbug subcommand [options]
+
+B<nmbug help> for more help
+
+=head1 OPTIONS
+
+=head2 Most common commands
+
+=over 8
+
+=item B<commit> [message]
+
+Commit appropriately prefixed tags from the notmuch database to
+git. Any extra arguments are used (one per line) as a commit message.
+
+=item  B<push> [remote]
+
+push local nmbug git state to remote repo
+
+=item  B<pull> [remote]
+
+pull (merge) remote repo changes to notmuch. B<pull> is equivalent to
+B<fetch> followed by B<merge>.
+
+=back
+
+=head2 Other Useful Commands
+
+=over 8
+
+=item B<checkout>
+
+Update the notmuch database from git. This is mainly useful to discard
+your changes in notmuch relative to git.
+
+=item B<fetch> [remote]
+
+Fetch changes from the remote repo (see merge to bring those changes
+into notmuch). 
+
+=item B<help> [subcommand]
+
+print help [for subcommand]
+
+=item B<log> [parameters]
+
+A simple wrapper for git log. After running C<nmbug fetch>, you can
+inspect the changes with C<nmbug log HEAD..FETCH_HEAD>
+
+=item B<merge> 
+
+Merge changes from FETCH_HEAD into HEAD, and load the result into
+notmuch.
+
+=item  B<status>
+
+Show pending updates in notmuch or git repo. See below for more
+information about the output format.
+
+=back
+
+=head2 Less common commands
+
+=over 8
+
+=item B<archive>
+
+Dump a tar archive (using git archive) of the current nmbug tag set.
+
+=back
+
+
+=head1 STATUS FORMAT
+
+B<nmbug status> prints lines of the form
+
+   c Message-Id tag
+
+where c is
+
+=over 8
+
+=item B<A>
+
+Tag is present in notmuch database, but not committed to nmbug
+(equivalently, tag has been deleted in nmbug repo, e.g. by a pull, but
+not restored to notmuch database).
+
+=item B<a>
+
+Tag is fetched, but not merged into notmuch.
+
+=item B<D>
+
+Tag is present in nmbug repo, but not restored to notmuch database
+(equivalently, tag has been deleted in notmuch)
+
+=item B<d>
+
+Tag deletion is fetched, but not merged into notmuch.
+
+=item B<U>
+
+Message is unknown (missing from local notmuch database)
+
+=back
+
+=head1 DUMP FORMAT
+
+Each tag $tag for message with Message-Id $id is written to
+an empty file
+
+	tags/encode($id)/encode($tag)
+
+The encoding preserves alphanumerics, and the characters "+-_@=.:,"
+(not the quotes).  All other octets are replaced with '%' followed by
+a two digit hex number.
+
+=head1 ENVIRONMENT
+
+B<NMBGIT> specifies the location of the git repository used by nmbug. 
+If not specified $HOME/.nmbug is used.
+
+B<NMBPREFIX> specifies the prefix in the notmuch database for tags of
+interest to nmbug. If not specified 'notmuch::' is used.
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-07  0:59           ` [PATCH] contrib/nmbug: new script for sharing tags with a given prefix David Bremner
@ 2011-11-07 12:58             ` Tomi Ollila
  2011-11-11 15:08             ` [PATCH v3] " David Bremner
  1 sibling, 0 replies; 35+ messages in thread
From: Tomi Ollila @ 2011-11-07 12:58 UTC (permalink / raw)
  To: David Bremner, notmuch; +Cc: David Bremner

On Sun,  6 Nov 2011 20:59:46 -0400, David Bremner <david@tethera.net> wrote:
> From: David Bremner <bremner@debian.org>
> 
> The main idea is consider the notmuch database as analogous to the
> work-tree. A bare git repo is maintained in the users home directory,
> with a tree of the form tags/$message-id/$tag
> 
> Like notmuch and git, we have a set of subcommnds, mainly modelled on
> git.
> 
> The most important commands are
> 
>     commit	xapian -> git
>     checkout	git -> xapian
>     merge	fetched git + git -> xapian
>     status	find differences between xapian, git, and remote git.
> 
> There are also some convenience wrappers around git commands.
> 
> In order to encode tags (viewed as octet sequences) into filenames,
> we whitelist a smallish set of characters and %hex escape anything outside.
> 
> The prefix is omitted in git, which lets one save and restore to
> different prefixes (although this is only lightly tested).
> ---
> 
> Many things have changed, time for a repost. It no long needs the
> "restore --match" patches. 

Good work. Could you do some simple things to help our reviewer's work:

*) consistent spacing (around '='s and '{':s (and '=>':s) etc.
*) consistent amount of newlines
*) consitent quoting (use same ':s or ":s in similar context)
*) M-x whitespace-cleanup (or M-x delete-trailing-whitespace) in pivot...
*) something else than @ in m@ ... @x (at least in unpack_diff_lines) (confuses viewers)

Tomi

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-07  0:59           ` [PATCH] contrib/nmbug: new script for sharing tags with a given prefix David Bremner
  2011-11-07 12:58             ` Tomi Ollila
@ 2011-11-11 15:08             ` David Bremner
  2011-11-11 23:26               ` Jameson Graef Rollins
  2011-11-13  1:39               ` David Bremner
  1 sibling, 2 replies; 35+ messages in thread
From: David Bremner @ 2011-11-11 15:08 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

From: David Bremner <bremner@debian.org>

The main idea is consider the notmuch database as analogous to the
work-tree. A bare git repo is maintained in the users home directory,
with a tree of the form tags/$message-id/$tag

Like notmuch and git, we have a set of subcommnds, mainly modelled on
git.

The most important commands are

    commit	xapian -> git
    checkout	git -> xapian
    merge	fetched git + git -> xapian
    status	find differences between xapian, git, and remote git.

There are also some convenience wrappers around git commands.

In order to encode tags (viewed as octet sequences) into filenames,
we whitelist a smallish set of characters and %hex escape anything outside.

The prefix is omitted in git, which lets one save and restore to
different prefixes (although this is only lightly tested).
---

The main user visible change is the new format for status; I realized
it was not that unusual (for me) to have the same message-id/tag combo
added in both (or deleted in both) remote git and local xapian.

Any important bug fix is that "nmbug commit" now uses the results of
"nmbug status", which prevents missing messages from acting as tag
deletions.

Other than that there is a bunch refactoring with help from 
Tomi Ollila.

One question for the audience is whether this should get some more
generic name before being pushed to master. Several people including
me are interested in using this script or a modified version as a
generic tag sharing tool.  Of course we can always rename things
later, but then we have to retrain people's fingers.

 contrib/nmbug |  623 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 623 insertions(+), 0 deletions(-)
 create mode 100755 contrib/nmbug

diff --git a/contrib/nmbug b/contrib/nmbug
new file mode 100755
index 0000000..47746d2
--- /dev/null
+++ b/contrib/nmbug
@@ -0,0 +1,623 @@
+#!/usr/bin/env perl
+# Copyright (c) 2011 David Bremner
+# License: same as notmuch
+
+use strict;
+use warnings;
+use File::Path qw(remove_tree make_path);
+use File::Temp qw(tempdir tempfile);
+use File::Basename;
+use Pod::Usage;
+
+no encoding;
+
+my $NMBGIT = $ENV{NMBGIT} || $ENV{HOME}.'/.nmbug';
+
+$NMBGIT .= '/.git' if (-d $NMBGIT.'/.git');
+
+my $TAGPREFIX = $ENV{NMBPREFIX} || 'notmuch::';
+
+# magic hashes for git
+my $EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391';
+my $EMPTYTREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+
+# for encoding
+
+my $ESCAPE_CHAR =	'%';
+my $NO_ESCAPE =		'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.
+			'+-_@=.:,';
+my $MUST_ENCODE =	qr{[^\Q$NO_ESCAPE\E]};
+my $ESCAPED_RX =	qr{$ESCAPE_CHAR([A-Fa-f0-9]{2})};
+
+my %command = (
+	     archive	=> \&do_archive,
+	     checkout	=> \&do_checkout,
+	     commit	=> \&do_commit,
+	     fetch	=> \&do_fetch,
+	     help	=> \&do_help,
+	     log	=> \&do_log,
+	     merge	=> \&do_merge,
+	     pull	=> \&do_pull,
+	     push	=> \&do_push,
+	     status	=> \&do_status,
+	     );
+
+my $subcommand = shift;
+
+if (!exists $command{$subcommand}){
+  usage ();
+}
+
+&{$command{$subcommand}}(@ARGV);
+
+sub git_pipe {
+  my $envref = (ref $_[0] eq 'HASH') ? shift : {};
+  my $ioref  = (ref $_[0] eq 'ARRAY') ? shift : undef;
+  my $dir = ($_[0] eq '-|' or $_[0] eq '|-') ? shift : undef;
+
+  unshift @_, 'git';
+  $envref->{GIT_DIR} ||= $NMBGIT;
+  spawn ($envref, defined $ioref ? $ioref : (), defined $dir ? $dir : (), @_);
+}
+
+sub git {
+  my $fh = git_pipe (@_);
+  my $str = join ('', <$fh>);
+  chomp($str);
+  return $str;
+}
+
+sub spawn {
+  my $envref = (ref $_[0] eq 'HASH') ? shift : {};
+  my $ioref  = (ref $_[0] eq 'ARRAY') ? shift : undef;
+  my $dir = ($_[0] eq '-|' or $_[0] eq '|-') ? shift : '-|';
+
+  die unless @_;
+
+  if (open my $child, $dir) {
+    return $child;
+  }
+  # child
+  while (my ($key, $value) = each %{$envref}) {
+    $ENV{$key} = $value;
+  }
+
+  if (defined $ioref && $dir eq '-|') {
+      open my $fh, '|-', @_ or die "open $dir @_: $!";
+      foreach my $line (@{$ioref}) {
+	print $fh $line, "\n";
+      }
+      exit 0;
+    } else {
+      if ($dir ne '|-') {
+	open STDIN, '<', '/dev/null' or die "reopening stdin: $!"
+      }
+      exec @_;
+      die "exec @_: $!";
+    }
+}
+
+
+sub get_tags {
+  my $prefix = shift;
+  my @tags;
+
+  my $fh = spawn ('-|', qw/notmuch search --output=tags/, "*")
+    or die 'error dumping tags';
+
+  while (<$fh>) {
+    chomp ();
+    push @tags, $_ if (m/^$prefix/);
+  }
+  return @tags;
+}
+
+
+sub do_archive {
+  system ('git', "--git-dir=$NMBGIT", 'archive', 'HEAD');
+}
+
+
+sub is_committed {
+  my $status = shift;
+  return scalar (@{$status->{added}} ) + scalar (@{$status->{deleted}} )==0
+}
+
+
+sub do_commit {
+  my @args = @_;
+
+  my $status = compute_status ();
+
+  if ( is_committed ($status) ){
+    print "Nothing to commit\n";
+    return;
+  }
+
+  my $index = read_tree ('HEAD');
+
+  update_index ($index, $status);
+
+  my $tree = git ( { GIT_INDEX_FILE => $index }, 'write-tree')
+    or die 'no output from write-tree';
+
+  my $parent = git ( 'rev-parse', 'HEAD'  )
+    or die 'no output from rev-parse';
+
+  my $commit = git ([ @args ], 'commit-tree', $tree, '-p', $parent)
+    or die 'commit-tree';
+
+  git ('update-ref', 'HEAD', $commit);
+
+  unlink $index || die "unlink: $!";
+
+}
+
+sub read_tree {
+  my $treeish = shift;
+  my $index = $NMBGIT.'/nmbug.index';
+  git ({GIT_INDEX_FILE => $index}, 'read-tree', $EMPTYTREE);
+  git ({GIT_INDEX_FILE => $index}, 'read-tree', $treeish);
+  return $index;
+}
+
+sub update_index {
+  my $index = shift;
+  my $status = shift;
+
+  my $git = spawn ({ GIT_DIR => $NMBGIT, GIT_INDEX_FILE => $index },
+		     '|-', qw/git update-index --index-info/)
+    or die 'git update-index';
+
+  foreach my $pair (@{$status->{deleted}}){
+    index_tags_for_msg ($git, $pair->{id}, 'D', $pair->{tag})
+  }
+
+  foreach my $pair (@{$status->{added}}){
+     index_tags_for_msg ($git, $pair->{id}, 'A', $pair->{tag})
+  }
+}
+
+sub do_fetch {
+  my $remote = shift || 'origin';
+
+  git ('fetch', $remote);
+}
+
+
+sub notmuch {
+  my @args = @_;
+  system ('notmuch', @args) == 0 or die  "notmuch @args failed: $?"
+}
+
+
+sub index_tags {
+
+  my $index = $NMBGIT.'/nmbug.index';
+
+  my $query = join ' ', map ("tag:$_", get_tags ($TAGPREFIX));
+
+  my $fh = spawn ('-|', qw/notmuch dump --/, $query)
+    or die "notmuch dump: $!";
+
+  git ('read-tree', $EMPTYTREE);
+  my $git = spawn ({ GIT_DIR => $NMBGIT, GIT_INDEX_FILE => $index },
+		     '|-', qw/git update-index --index-info/)
+    or die 'git update-index';
+
+  while (<$fh>) {
+    m/ ( [^ ]* ) \s+ \( ([^\)]* ) \) /x || die 'syntax error in dump';
+    my ($id,$rest) = ($1,$2);
+
+    #strip prefixes before writing
+    my @tags = grep { s/^$TAGPREFIX//; } split (' ', $rest);
+    index_tags_for_msg ($git,$id, 'A', @tags);
+  }
+
+  close $git;
+  return $index;
+}
+
+sub index_tags_for_msg {
+  my $fh = shift;
+  my $msgid = shift;
+  my $mode = shift;
+
+  my $hash = $EMPTYBLOB;
+  my $blobmode = '100644';
+
+  if ($mode eq 'D') {
+    $blobmode = '0';
+    $hash = '0000000000000000000000000000000000000000';
+  }
+
+  foreach my $tag (@_){
+    my $tagpath = 'tags/' . encode_for_fs ($msgid)  . '/' . encode_for_fs ($tag);
+    print $fh "$blobmode $hash\t$tagpath\n";
+  }
+}
+
+
+sub do_checkout {
+  do_sync (action => 'checkout');
+}
+
+
+sub do_sync {
+
+  my %args = @_;
+
+  my $status = compute_status ();
+  my ($A_action, $D_action);
+
+  if ($args{action} eq 'checkout') {
+    $A_action = '-';
+    $D_action = '+';
+  } else {
+    $A_action = '+';
+    $D_action = '-';
+  }
+
+  foreach my $pair (@{$status->{added}}){
+
+    notmuch ('tag', $A_action.$TAGPREFIX.$pair->{tag},
+	     'id:'.$pair->{id});
+  }
+
+  foreach my $pair (@{$status->{deleted}}){
+    notmuch ('tag', $D_action.$TAGPREFIX.$pair->{tag},
+	     'id:'.$pair->{id});
+  }
+
+}
+
+
+sub insist_committed {
+
+  my $status=compute_status();
+  if ( !is_committed ($status) ){
+    print "Uncommitted changes to $TAGPREFIX* tags in notmuch
+
+For a summary of changes, run 'nmbug status'
+To save your changes,     run 'nmbug commit' before merging/pull
+To discard your changes,  run 'nmbug checkout'
+";
+    exit (1);
+  }
+
+}
+
+
+sub do_pull {
+  my $remote = shift || 'origin';
+
+  git ( 'fetch', $remote);
+
+  do_merge ();
+}
+
+
+sub do_merge {
+  insist_committed ();
+
+  my $tempwork = tempdir ('/tmp/nmbug-merge.XXXXXX', CLEANUP => 1);
+
+  git ( { GIT_WORK_TREE => $tempwork }, 'checkout', '-f', 'HEAD');
+
+  git ( { GIT_WORK_TREE => $tempwork }, 'merge', 'FETCH_HEAD');
+
+  do_checkout ();
+}
+
+
+sub do_log {
+  # we don't want output trapping here, because we want the pager.
+  system ( 'git', "--git-dir=$NMBGIT", 'log', '--name-status', @_);
+}
+
+
+sub do_push {
+  my $remote = shift || 'origin';
+
+  git ('push', $remote);
+}
+
+
+sub do_status {
+  my $status = compute_status ();
+
+  my %output = ();
+  foreach my $pair (@{$status->{added}}){
+    $output{$pair->{id}} ||= {};
+    $output{$pair->{id}}{$pair->{tag}} = 'A'
+  }
+
+  foreach my $pair (@{$status->{deleted}}){
+    $output{$pair->{id}} ||= {};
+    $output{$pair->{id}}{$pair->{tag}} = 'D'
+  }
+
+  foreach my $pair (@{$status->{missing}}){
+    $output{$pair->{id}} ||= {};
+    $output{$pair->{id}}{$pair->{tag}} = 'U'
+  }
+
+  if (is_unmerged ()) {
+    foreach my $pair (diff_refs ('A')){
+      $output{$pair->{id}} ||= {};
+      $output{$pair->{id}}{$pair->{tag}} ||= ' ';
+      $output{$pair->{id}}{$pair->{tag}} .= 'a';
+    }
+
+    foreach my $pair (diff_refs ('D')){
+      $output{$pair->{id}} ||= {};
+      $output{$pair->{id}}{$pair->{tag}} ||= ' ';
+      $output{$pair->{id}}{$pair->{tag}} .= 'd';
+    }
+  }
+
+  foreach my $id (sort keys %output) {
+    foreach my $tag (sort keys %{$output{$id}}) {
+      printf "%s\t%s\t%s\n",$output{$id}{$tag},$id,$tag;
+    }
+  }
+}
+
+
+sub is_unmerged {
+
+  return 0 if (! -f $NMBGIT.'/FETCH_HEAD');
+
+  my $fetch_head = git ('rev-parse', 'FETCH_HEAD');
+  my $base = git ( 'merge-base', 'HEAD', 'FETCH_HEAD');
+
+  return ($base ne $fetch_head);
+
+}
+
+sub compute_status {
+  my %args = @_;
+
+  my @added;
+  my @deleted;
+  my @missing;
+
+  my $index = index_tags ();
+
+  my @maybe_deleted = diff_index ($index,'D');
+
+  foreach my $pair (@maybe_deleted){
+
+    my $id = $pair->{id};
+
+    my $fh = spawn ('-|', qw/notmuch search --output=files/,"id:$id")
+      or die "searching for $id";
+    if (!<$fh>) {
+      push @missing, $pair;
+    } else {
+      push @deleted, $pair;
+    }
+  }
+
+
+  @added = diff_index ($index, 'A');
+
+  unlink $index || die "unlink $index: $!";
+
+  return { added => [@added], deleted => [@deleted], missing => [@missing] };
+}
+
+
+sub diff_index {
+  my $index = shift;
+  my $filter = shift;
+
+  my $fh = git_pipe ({GIT_INDEX_FILE => $index},
+		  qw/diff-index --cached/,
+		 "--diff-filter=$filter", qw/--name-only HEAD/ );
+
+  return unpack_diff_lines ($fh);
+}
+
+
+sub diff_refs {
+  my $filter=shift;
+  my $ref1 = shift || 'HEAD';
+  my $ref2 = shift || 'FETCH_HEAD';
+
+  my $fh= git_pipe ( 'diff', "--diff-filter=$filter", '--name-only',
+		 $ref1, $ref2);
+
+  return unpack_diff_lines ($fh);
+}
+
+
+sub unpack_diff_lines {
+  my $fh=shift;
+
+  my @found;
+  while(<$fh>){
+    chomp ();
+    my ($id,$tag) = m|tags/ ([^/]+) / ([^/]+) |x;
+
+    $id = decode_from_fs ($id);
+    $tag = decode_from_fs ($tag);
+
+    push @found, { id => $id, tag => $tag };
+  }
+
+  return @found;
+}
+
+
+sub encode_for_fs{
+  my $str = shift;
+
+  $str =~ s/($MUST_ENCODE)/"$ESCAPE_CHAR".sprintf ("%02x",ord ($1))/ge;
+  return $str;
+}
+
+
+sub decode_from_fs{
+  my $str = shift;
+
+  $str =~ s/$ESCAPED_RX/ chr (hex ($1))/eg;
+
+  return $str;
+
+}
+
+
+sub usage {
+  pod2usage ();
+  exit (1);
+}
+
+
+sub do_help {
+  pod2usage ( -verbose => 2 );
+  exit (0);
+}
+
+__END__
+
+=head1 NAME
+
+nmbug - manage notmuch tags about notmuch
+
+=head1 SYNOPSIS
+
+nmbug subcommand [options]
+
+B<nmbug help> for more help
+
+=head1 OPTIONS
+
+=head2 Most common commands
+
+=over 8
+
+=item B<commit> [message]
+
+Commit appropriately prefixed tags from the notmuch database to
+git. Any extra arguments are used (one per line) as a commit message.
+
+=item  B<push> [remote]
+
+push local nmbug git state to remote repo
+
+=item  B<pull> [remote]
+
+pull (merge) remote repo changes to notmuch. B<pull> is equivalent to
+B<fetch> followed by B<merge>.
+
+=back
+
+=head2 Other Useful Commands
+
+=over 8
+
+=item B<checkout>
+
+Update the notmuch database from git. This is mainly useful to discard
+your changes in notmuch relative to git.
+
+=item B<fetch> [remote]
+
+Fetch changes from the remote repo (see merge to bring those changes
+into notmuch).
+
+=item B<help> [subcommand]
+
+print help [for subcommand]
+
+=item B<log> [parameters]
+
+A simple wrapper for git log. After running C<nmbug fetch>, you can
+inspect the changes with C<nmbug log HEAD..FETCH_HEAD>
+
+=item B<merge>
+
+Merge changes from FETCH_HEAD into HEAD, and load the result into
+notmuch.
+
+=item  B<status>
+
+Show pending updates in notmuch or git repo. See below for more
+information about the output format.
+
+=back
+
+=head2 Less common commands
+
+=over 8
+
+=item B<archive>
+
+Dump a tar archive (using git archive) of the current nmbug tag set.
+
+=back
+
+=head1 STATUS FORMAT
+
+B<nmbug status> prints lines of the form
+
+   ng Message-Id tag
+
+where n is a single character representing notmuch database status
+
+=over 8
+
+=item B<A>
+
+Tag is present in notmuch database, but not committed to nmbug
+(equivalently, tag has been deleted in nmbug repo, e.g. by a pull, but
+not restored to notmuch database).
+
+=item B<D>
+
+Tag is present in nmbug repo, but not restored to notmuch database
+(equivalently, tag has been deleted in notmuch)
+
+=item B<U>
+
+Message is unknown (missing from local notmuch database)
+
+=back
+
+The second character (if present) represents a difference between remote
+git and local. Typically C<nmbug fetch> needs to be run to update this.
+
+=over 8
+
+
+=item B<a>
+
+Tag is present in remote, but not in local git.
+
+
+=item B<d>
+
+Tag is present in local git, but not in remote git.
+
+
+=back
+
+=head1 DUMP FORMAT
+
+Each tag $tag for message with Message-Id $id is written to
+an empty file
+
+	tags/encode($id)/encode($tag)
+
+The encoding preserves alphanumerics, and the characters "+-_@=.:,"
+(not the quotes).  All other octets are replaced with '%' followed by
+a two digit hex number.
+
+=head1 ENVIRONMENT
+
+B<NMBGIT> specifies the location of the git repository used by nmbug.
+If not specified $HOME/.nmbug is used.
+
+B<NMBPREFIX> specifies the prefix in the notmuch database for tags of
+interest to nmbug. If not specified 'notmuch::' is used.
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-11 15:08             ` [PATCH v3] " David Bremner
@ 2011-11-11 23:26               ` Jameson Graef Rollins
  2011-11-11 23:55                 ` David Bremner
  2011-11-13  1:39               ` David Bremner
  1 sibling, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-11-11 23:26 UTC (permalink / raw)
  To: David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 974 bytes --]

On Fri, 11 Nov 2011 10:08:15 -0500, David Bremner <david@tethera.net> wrote:
> The most important commands are
> 
>     commit	xapian -> git
>     checkout	git -> xapian
>     merge	fetched git + git -> xapian
>     status	find differences between xapian, git, and remote git.

Hey, David.  Just for clarification, the above seems a little
inconsistent with the documentation included with the script, which
implies that the "most common commands", which are I believe meant to be
the only ones that the user should routinely have to use, are "commit",
"push", and "pull".  I got the impression that "checkout" and "merge"
should only be needed if you're trying to recover a broken system.

> There are also some convenience wrappers around git commands.

Isn't much of nmbug "convenience wrappers around git commands"?  It
seems to me that the fact that nmbug is using git underneath should be
almost completely abstracted away from the user.

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-11 23:26               ` Jameson Graef Rollins
@ 2011-11-11 23:55                 ` David Bremner
  2011-11-12  2:43                   ` Jameson Graef Rollins
  0 siblings, 1 reply; 35+ messages in thread
From: David Bremner @ 2011-11-11 23:55 UTC (permalink / raw)
  To: Jameson Graef Rollins, notmuch

On Fri, 11 Nov 2011 15:26:28 -0800, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> On Fri, 11 Nov 2011 10:08:15 -0500, David Bremner <david@tethera.net> wrote:
> > The most important commands are
> > 
> >     commit	xapian -> git
> >     checkout	git -> xapian
> >     merge	fetched git + git -> xapian
> >     status	find differences between xapian, git, and remote git.
> 
> Hey, David.  Just for clarification, the above seems a little
> inconsistent with the documentation included with the script, which
> implies that the "most common commands", which are I believe meant to be
> the only ones that the user should routinely have to use, are "commit",
> "push", and "pull".  I got the impression that "checkout" and "merge"
> should only be needed if you're trying to recover a broken system.

Hi Jamie;

I think the commit message and the online help are aimed a different
audience, so it is not crucial (or even desirable) that they be exactly
the same.  Perhaps it would be more clear to say that these are the
"non-trivial" commands.

The "most commonly used commands" could be retitled "the minimal set of
commands to use nmbug". My own workflow involves fetch, merge, and the
occasional checkout. Your might or might not; in my case it has to do
with the fact that I am syncing tags in several different ways.

> > There are also some convenience wrappers around git commands.
> Isn't much of nmbug "convenience wrappers around git commands"? 

The distinction I'm trying to make here is between one-liners like
"fetch" and slightly hairy things like "commit", where I had to learn
new things about git to implement the latter.

> It seems to me that the fact that nmbug is using git underneath should
> be almost completely abstracted away from the user.

Well, in the sense that user should not normally have to use raw git
commands, I agree. I don't really see any point in hiding the fact that
it is using git e.g. in the documentation.  It doesn't seem likely that
nmbug will support more than one version control system without a
complete rewrite.

And of course the commit messages are supposed to tell how things work,
so no point being coy there.

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-11 23:55                 ` David Bremner
@ 2011-11-12  2:43                   ` Jameson Graef Rollins
  2011-11-12  5:52                     ` David Bremner
  0 siblings, 1 reply; 35+ messages in thread
From: Jameson Graef Rollins @ 2011-11-12  2:43 UTC (permalink / raw)
  To: David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 1581 bytes --]

On Fri, 11 Nov 2011 18:55:15 -0500, David Bremner <david@tethera.net> wrote:
> I think the commit message and the online help are aimed a different
> audience, so it is not crucial (or even desirable) that they be exactly
> the same.  Perhaps it would be more clear to say that these are the
> "non-trivial" commands.

At this point I actually think they're probably exactly the same
audience, aren't they?  Isn't nmbug primarily aimed at notmuch
developers that are reading patches sent to the list?

> The "most commonly used commands" could be retitled "the minimal set of
> commands to use nmbug". My own workflow involves fetch, merge, and the
> occasional checkout. Your might or might not; in my case it has to do
> with the fact that I am syncing tags in several different ways.

This is the sort of thing that I'm finding confusing.  Is this your work
flow because you are the primary developer and are therefore frequently
working under the hood, or because this is the prescribed procedure?

The tag sharing process that nmbug is using is complicated and subtle
enough that you should make it very clear what the "proper" procedure
is, and what are the "under the hood" commands that one should never
really have to use.  I've been following pretty closely and I'm still
kind of confused.  I'm sure that others coming to it now because they're
seeing this posting to the list might share that confusion.  Maybe it
would help if you could follow up with a good how-to that describes how
someone could start using nmbug with a nicely defined prescribed
procedure.

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-12  2:43                   ` Jameson Graef Rollins
@ 2011-11-12  5:52                     ` David Bremner
  0 siblings, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-11-12  5:52 UTC (permalink / raw)
  To: Jameson Graef Rollins, notmuch

On Fri, 11 Nov 2011 18:43:38 -0800, Jameson Graef Rollins <jrollins@finestructure.net> wrote:
> On Fri, 11 Nov 2011 18:55:15 -0500, David Bremner <david@tethera.net> wrote:

> At this point I actually think they're probably exactly the same
> audience, aren't they?  Isn't nmbug primarily aimed at notmuch
> developers that are reading patches sent to the list?

Well, same people, different roles.  I maintain that the purpose of
commit messages and online help is different. Commit messages are
intended to help understand the implementation, help message how to use
it.

> > My own workflow involves fetch, merge, and the occasional
> > checkout. Your might or might not; in my case it has to do with the
> > fact that I am syncing tags in several different ways.

> This is the sort of thing that I'm finding confusing.  Is this your work
> flow because you are the primary developer and are therefore frequently
> working under the hood, or because this is the prescribed procedure?

From my point of view the analogy with git is fairly precise. Some
people prefer the convenience of pull, some people prefer to sanity
check the fetched changes (in the case of nmbug, using status) before
merging.  I guess people only using notmuch on one primary machine are
less likely need checkout. The error message from nmbug pull explains
when you might want checkout.

> Maybe it would help if you could follow up with a good how-to that
> describes how someone could start using nmbug with a nicely defined
> prescribed procedure.

I have rewritten the page http://notmuchmail/nmbug to compare the 
"short and simple" workflow with "expanded and fussy" workflow.

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3] contrib/nmbug: new script for sharing tags with a given prefix.
  2011-11-11 15:08             ` [PATCH v3] " David Bremner
  2011-11-11 23:26               ` Jameson Graef Rollins
@ 2011-11-13  1:39               ` David Bremner
  1 sibling, 0 replies; 35+ messages in thread
From: David Bremner @ 2011-11-13  1:39 UTC (permalink / raw)
  To: notmuch

On Fri, 11 Nov 2011 10:08:15 -0500, David Bremner <david@tethera.net> wrote:
> From: David Bremner <bremner@debian.org>
> 
> The main idea is consider the notmuch database as analogous to the
> work-tree. A bare git repo is maintained in the users home directory,
> with a tree of the form tags/$message-id/$tag

I pushed a slightly modified version of this to notmuch master.

d

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2011-11-13  1:39 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-10-23 15:18 [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
2011-10-23 21:51 ` [PATCH] xregcomp: don't consider every regex compilation failure an internal error David Bremner
2011-10-29  3:04   ` David Bremner
2011-10-29  3:04     ` [PATCH 1/4] notmuch-restore: add tests for new --match command line argument David Bremner
2011-10-29  3:04     ` [PATCH 2/4] notmuch-restore: implement argument parsing for --match David Bremner
2011-10-29  3:04     ` [PATCH 3/4] test/dump-restore: add tests for notmuch restore --match David Bremner
2011-10-29  3:04     ` [PATCH 4/4] notmuch-restore: implement --match functionality David Bremner
2011-10-29 16:45       ` Experimental Tag Sharing David Bremner
2011-10-29 16:45         ` [PATCH] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
2011-10-29 21:11           ` Ali Polatel
2011-10-29 22:12             ` David Bremner
2011-10-30  0:21               ` Ali Polatel
2011-11-07  0:59           ` [PATCH] contrib/nmbug: new script for sharing tags with a given prefix David Bremner
2011-11-07 12:58             ` Tomi Ollila
2011-11-11 15:08             ` [PATCH v3] " David Bremner
2011-11-11 23:26               ` Jameson Graef Rollins
2011-11-11 23:55                 ` David Bremner
2011-11-12  2:43                   ` Jameson Graef Rollins
2011-11-12  5:52                     ` David Bremner
2011-11-13  1:39               ` David Bremner
2011-10-29 17:27         ` [PATCH v2] contrib/nmbug: new script for sharing tags with prefix notmuch:: David Bremner
2011-10-30  0:44         ` Experimental Tag Sharing Jameson Graef Rollins
2011-10-30  2:18           ` David Bremner
2011-10-29 20:55       ` [PATCH] docs: man, online help, and NEWS updated for notmuch restore --match David Bremner
2011-10-31 19:01         ` [PATCH] Fix formatting for restore and dump help documentation Jameson Graef Rollins
2011-11-01  0:19           ` David Bremner
2011-10-29 18:52 ` [PATCH] xutil.c: remove duplicate copies, create new library libutil.a to contain xutil David Bremner
2011-10-31  0:04 ` Austin Clements
2011-10-31  0:09 ` Jameson Graef Rollins
2011-10-31  2:28   ` David Bremner
2011-10-31  9:20 ` Jani Nikula
2011-10-31 10:51   ` David Bremner
2011-10-31 18:50   ` Jameson Graef Rollins
2011-10-31 19:19     ` Jani Nikula
2011-10-31 19:20       ` Jameson Graef Rollins

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).