* [Patch v4 01/12] configure: detect Xapian:FieldProcessor
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 02/12] lib: optionally support single argument date: queries David Bremner
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
Rather than check versions, it seems more robust to do a test compile.
---
configure | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/configure b/configure
index c24a9c8..03e4318 100755
--- a/configure
+++ b/configure
@@ -358,9 +358,10 @@ if [ ${have_xapian} = "0" ]; then
errors=$((errors + 1))
fi
-# Compaction is only supported on Xapian > 1.2.6
have_xapian_compact=0
+have_xapian_field_processor=0
if [ ${have_xapian} = "1" ]; then
+ # Compaction is only supported on Xapian > 1.2.6
printf "Checking for Xapian compaction support... "
case "${xapian_version}" in
0.*|1.[01].*|1.2.[0-5])
@@ -371,10 +372,23 @@ if [ ${have_xapian} = "1" ]; then
*)
printf "Unknown version.\n" ;;
esac
-fi
-default_xapian_backend=""
-if [ ${have_xapian} = "1" ]; then
+ printf "Checking for Xapian FieldProcessor API... "
+ cat>_field_processor.cc<<EOF
+#include <xapian.h>
+class TitleFieldProcessor : public Xapian::FieldProcessor { };
+EOF
+ if ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} -c _field_processor.cc -o _field_processor.o > /dev/null 2>&1
+ then
+ have_xapian_field_processor=1
+ printf "Yes.\n"
+ else
+ printf "No. (optional)\n"
+ fi
+
+ rm -f _field_processor.o _field_processor.cc
+
+ default_xapian_backend=""
printf "Testing default Xapian backend... "
cat >_default_backend.cc <<EOF
#include <xapian.h>
@@ -392,6 +406,7 @@ EOF
printf "%s\n" "${default_xapian_backend}";
rm -rf test.db _default_backend _default_backend.cc
fi
+
# we need to have a version >= 2.6.5 to avoid a crypto bug. We need
# 2.6.7 for permissive "From " header handling.
GMIME_MINVER=2.6.7
@@ -1001,6 +1016,9 @@ HAVE_D_TYPE = ${have_d_type}
# Whether the Xapian version in use supports compaction
HAVE_XAPIAN_COMPACT = ${have_xapian_compact}
+# Whether the Xapian version in use supports field processors
+HAVE_XAPIAN_FIELD_PROCESSOR = ${have_xapian_field_processor}
+
# Whether the getpwuid_r function is standards-compliant
# (if not, then notmuch will #define _POSIX_PTHREAD_SEMANTICS
# to enable the standards-compliant version -- needed for Solaris)
@@ -1075,6 +1093,7 @@ CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
-DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\
+ -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_PROCESSOR) \\
-DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
@@ -1089,6 +1108,7 @@ CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
-DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\
+ -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\
-DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS)
@@ -1102,6 +1122,9 @@ cat > sh.config <<EOF
# Whether the Xapian version in use supports compaction
NOTMUCH_HAVE_XAPIAN_COMPACT=${have_xapian_compact}
+# Whether the Xapian version in use supports field processors
+NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR=${have_xapian_field_processor}
+
# Which backend will Xapian use by default?
NOTMUCH_DEFAULT_XAPIAN_BACKEND=${default_xapian_backend}
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 02/12] lib: optionally support single argument date: queries
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
2016-05-08 0:04 ` [Patch v4 01/12] configure: detect Xapian:FieldProcessor David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 03/12] lib/cli: add library API / CLI for compile time options David Bremner
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
This relies on the FieldProcessor API, which is only present in xapian
>= 1.3.
---
doc/man7/notmuch-search-terms.rst | 22 +++++++++++++++++++---
lib/database-private.h | 3 +++
lib/database.cc | 6 ++++++
lib/parse-time-vrp.cc | 21 +++++++++++++++++++++
lib/parse-time-vrp.h | 5 +++++
test/T500-search-date.sh | 6 ++++++
6 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst
index 2fbc16d..adedf5a 100644
--- a/doc/man7/notmuch-search-terms.rst
+++ b/doc/man7/notmuch-search-terms.rst
@@ -281,9 +281,10 @@ matches from the beginning of January to the end of February.
date:<expr>..! can be used as a shorthand for date:<expr>..<expr>. The
expansion takes place before interpretation, and thus, for example,
date:monday..! matches from the beginning of Monday until the end of
-Monday. (Note that entering date:<expr> without "..", for example
-date:yesterday, won't work, as it's not interpreted as a range
-expression at all. Again, use date:yesterday..!)
+Monday.
+With **Xapian Field Processor** support (see below), non-range
+date queries such as date:yesterday will work, but otherwise
+will give unexpected results; if in doubt use date:yesterday..!
Currently, we do not support spaces in range expressions. You can
replace the spaces with '\_', or (in most cases) '-', or (in some cases)
@@ -370,6 +371,21 @@ Time zones
Some time zone codes, e.g. UTC, EET.
+XAPIAN FIELD PROCESSORS
+=======================
+
+Certain optional features of the notmuch query processor rely on the
+presence of the Xapian field processor API. You can determine if your
+notmuch was built against a sufficiently recent version of Xapian by running
+
+::
+
+ % notmuch config get built_with.field_processor
+
+Currently the following features require field processor support:
+
+- non-range date queries, e.g. "date:today"
+
SEE ALSO
========
diff --git a/lib/database-private.h b/lib/database-private.h
index 3fb10f7..e1962f4 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -176,6 +176,9 @@ struct _notmuch_database {
Xapian::TermGenerator *term_gen;
Xapian::ValueRangeProcessor *value_range_processor;
Xapian::ValueRangeProcessor *date_range_processor;
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+ Xapian::FieldProcessor *date_field_processor;
+#endif
Xapian::ValueRangeProcessor *last_mod_range_processor;
};
diff --git a/lib/database.cc b/lib/database.cc
index c8c5e26..ebe019f 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -1000,6 +1000,12 @@ notmuch_database_open_verbose (const char *path,
notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+ /* This currently relies on the query parser to pass anything
+ * with a .. to the range processor */
+ notmuch->date_field_processor = new DateFieldProcessor();
+ notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_processor);
+#endif
notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
diff --git a/lib/parse-time-vrp.cc b/lib/parse-time-vrp.cc
index 03804cf..b15b77c 100644
--- a/lib/parse-time-vrp.cc
+++ b/lib/parse-time-vrp.cc
@@ -64,3 +64,24 @@ ParseTimeValueRangeProcessor::operator() (std::string &begin, std::string &end)
return valno;
}
+
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+/* XXX TODO: is throwing an exception the right thing to do here? */
+Xapian::Query DateFieldProcessor::operator()(const std::string & str) {
+ time_t from, to, now;
+
+ /* Use the same 'now' for begin and end. */
+ if (time (&now) == (time_t) -1)
+ throw Xapian::QueryParserError("Unable to get current time");
+
+ if (parse_time_string (str.c_str (), &from, &now, PARSE_TIME_ROUND_DOWN))
+ throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'");
+
+ if (parse_time_string (str.c_str (), &to, &now, PARSE_TIME_ROUND_UP_INCLUSIVE))
+ throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'");
+
+ return Xapian::Query(Xapian::Query::OP_AND,
+ Xapian::Query(Xapian::Query::OP_VALUE_GE, 0, Xapian::sortable_serialise ((double) from)),
+ Xapian::Query(Xapian::Query::OP_VALUE_LE, 0, Xapian::sortable_serialise ((double) to)));
+}
+#endif
diff --git a/lib/parse-time-vrp.h b/lib/parse-time-vrp.h
index 094c4f8..3bd12bf 100644
--- a/lib/parse-time-vrp.h
+++ b/lib/parse-time-vrp.h
@@ -37,4 +37,9 @@ public:
Xapian::valueno operator() (std::string &begin, std::string &end);
};
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+class DateFieldProcessor : public Xapian::FieldProcessor {
+ Xapian::Query operator()(const std::string & str);
+};
+#endif
#endif /* NOTMUCH_PARSE_TIME_VRP_H */
diff --git a/test/T500-search-date.sh b/test/T500-search-date.sh
index f5cea42..198a2e6 100755
--- a/test/T500-search-date.sh
+++ b/test/T500-search-date.sh
@@ -12,6 +12,12 @@ test_begin_subtest "Absolute date range with 'same' operator"
output=$(notmuch search date:2010-12-16..! | notmuch_search_sanitize)
test_expect_equal "$output" "thread:XXX 2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)"
+if [ "${NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR}" = "1" ]; then
+ test_begin_subtest "Absolute date field"
+ output=$(notmuch search date:2010-12-16 | notmuch_search_sanitize)
+ test_expect_equal "$output" "thread:XXX 2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)"
+fi
+
test_begin_subtest "Absolute time range with TZ"
notmuch search date:18-Nov-2009_02:19:26-0800..2009-11-18_04:49:52-06:00 | notmuch_search_sanitize > OUTPUT
cat <<EOF >EXPECTED
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 03/12] lib/cli: add library API / CLI for compile time options
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
2016-05-08 0:04 ` [Patch v4 01/12] configure: detect Xapian:FieldProcessor David Bremner
2016-05-08 0:04 ` [Patch v4 02/12] lib: optionally support single argument date: queries David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 04/12] configure: check directly for xapian compaction API David Bremner
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
This is intentionally low tech; if we have more than two options it may
make sense to build up what infrastructure is provided.
---
doc/man1/notmuch-config.rst | 5 +++++
lib/Makefile.local | 1 +
lib/built-with.c | 33 +++++++++++++++++++++++++++++++++
lib/notmuch.h | 5 +++++
notmuch-config.c | 22 ++++++++++++++++++++++
test/T030-config.sh | 6 ++++--
test/T040-setup.sh | 6 ++++--
test/test-lib.sh | 6 ++++++
8 files changed, 80 insertions(+), 4 deletions(-)
create mode 100644 lib/built-with.c
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 40c1272..26a8eb1 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -132,6 +132,11 @@ The available configuration items are described below.
Default: ``gpg``.
+ **built_with.<name>**
+
+ Compile time feature <name>. Current possibilities include
+ "compact" (see **notmuch-compact(1)**)
+ and "field_processor" (see **notmuch-search-terms(7)**).
ENVIRONMENT
===========
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 3a07090..36c3924 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -39,6 +39,7 @@ libnotmuch_c_srcs = \
$(dir)/message-file.c \
$(dir)/messages.c \
$(dir)/sha1.c \
+ $(dir)/built-with.c \
$(dir)/tags.c
libnotmuch_cxx_srcs = \
diff --git a/lib/built-with.c b/lib/built-with.c
new file mode 100644
index 0000000..b619bed
--- /dev/null
+++ b/lib/built-with.c
@@ -0,0 +1,33 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * 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: David Bremner <david@tethera.net>
+ */
+
+#include "notmuch.h"
+#include "notmuch-private.h"
+
+notmuch_bool_t
+notmuch_built_with (const char *name) {
+ if (STRNCMP_LITERAL (name, "compact") == 0) {
+ return HAVE_XAPIAN_COMPACT;
+ } else if (STRNCMP_LITERAL (name, "field_processor") == 0) {
+ return HAVE_XAPIAN_FIELD_PROCESSOR;
+ } else {
+ return FALSE;
+ }
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index cb46fc0..3a092ef 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1838,6 +1838,11 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames);
void
notmuch_filenames_destroy (notmuch_filenames_t *filenames);
+/**
+ * interrogate the library for compile time features
+ */
+notmuch_bool_t
+notmuch_built_with (const char *name);
/* @} */
NOTMUCH_END_DECLS
diff --git a/notmuch-config.c b/notmuch-config.c
index d252bb2..97a46fa 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -750,6 +750,8 @@ _item_split (char *item, char **group, char **key)
return 0;
}
+#define BUILT_WITH_PREFIX "built_with."
+
static int
notmuch_config_command_get (notmuch_config_t *config, char *item)
{
@@ -773,6 +775,9 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
tags = notmuch_config_get_new_tags (config, &length);
for (i = 0; i < length; i++)
printf ("%s\n", tags[i]);
+ } else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
+ printf ("%s\n",
+ notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false");
} else {
char **value;
size_t i, length;
@@ -804,6 +809,11 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
{
char *group, *key;
+ if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
+ fprintf (stderr, "Error: read only option: %s\n", item);
+ return 1;
+ }
+
if (_item_split (item, &group, &key))
return 1;
@@ -830,6 +840,17 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
return notmuch_config_save (config);
}
+static
+void
+_notmuch_config_list_options () {
+ printf("%scompact=%s\n",
+ BUILT_WITH_PREFIX,
+ notmuch_built_with ("compact") ? "true" : "false");
+ printf("%sfield_processor=%s\n",
+ BUILT_WITH_PREFIX,
+ notmuch_built_with ("field_processor") ? "true" : "false");
+}
+
static int
notmuch_config_command_list (notmuch_config_t *config)
{
@@ -865,6 +886,7 @@ notmuch_config_command_list (notmuch_config_t *config)
g_strfreev (groups);
+ _notmuch_config_list_options ();
return 0;
}
diff --git a/test/T030-config.sh b/test/T030-config.sh
index f404908..437269f 100755
--- a/test/T030-config.sh
+++ b/test/T030-config.sh
@@ -44,7 +44,7 @@ test_expect_equal "$(notmuch config get foo.nonexistent)" ""
test_begin_subtest "List all items"
notmuch config set database.path "/canonical/path"
-output=$(notmuch config list)
+output=$(notmuch config list | notmuch_built_with_sanitize)
test_expect_equal "$output" "\
database.path=/canonical/path
user.name=Notmuch Test Suite
@@ -56,7 +56,9 @@ search.exclude_tags=
maildir.synchronize_flags=true
crypto.gpg_path=gpg
foo.string=this is another string value
-foo.list=this;is another;list value;"
+foo.list=this;is another;list value;
+built_with.compact=something
+built_with.field_processor=something"
test_begin_subtest "Top level --config=FILE option"
cp "${NOTMUCH_CONFIG}" alt-config
diff --git a/test/T040-setup.sh b/test/T040-setup.sh
index cf0c00b..be2f0db 100755
--- a/test/T040-setup.sh
+++ b/test/T040-setup.sh
@@ -19,7 +19,7 @@ another.suite@example.com
foo bar
baz
EOF
-output=$(notmuch --config=new-notmuch-config config list)
+output=$(notmuch --config=new-notmuch-config config list | notmuch_built_with_sanitize)
test_expect_equal "$output" "\
database.path=/path/to/maildir
user.name=Test Suite
@@ -29,6 +29,8 @@ new.tags=foo;bar;
new.ignore=
search.exclude_tags=baz;
maildir.synchronize_flags=true
-crypto.gpg_path=gpg"
+crypto.gpg_path=gpg
+built_with.compact=something
+built_with.field_processor=something"
test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index ac04b15..09f8731 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -733,6 +733,12 @@ notmuch_uuid_sanitize ()
{
sed 's/[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}/UUID/g'
}
+
+notmuch_built_with_sanitize ()
+{
+ sed 's/^built_with[.]\(.*\)=.*$/built_with.\1=something/'
+}
+
# End of notmuch helper functions
# Use test_set_prereq to tell that a particular prerequisite is available.
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 04/12] configure: check directly for xapian compaction API
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (2 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 03/12] lib/cli: add library API / CLI for compile time options David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 05/12] lib: provide config API David Bremner
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
This is consistent with the check for FieldProcessor, and probably a bit
more robust.
---
configure | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/configure b/configure
index 03e4318..f94f7c7 100755
--- a/configure
+++ b/configure
@@ -361,17 +361,18 @@ fi
have_xapian_compact=0
have_xapian_field_processor=0
if [ ${have_xapian} = "1" ]; then
- # Compaction is only supported on Xapian > 1.2.6
printf "Checking for Xapian compaction support... "
- case "${xapian_version}" in
- 0.*|1.[01].*|1.2.[0-5])
- printf "No (only available with Xapian > 1.2.6).\n" ;;
- [1-9]*.[0-9]*.[0-9]*)
- have_xapian_compact=1
- printf "Yes.\n" ;;
- *)
- printf "Unknown version.\n" ;;
- esac
+ cat>_compact.cc<<EOF
+#include <xapian.h>
+class TestCompactor : public Xapian::Compactor { };
+EOF
+ if ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} -c _compact.cc -o _compact.o > /dev/null 2>&1
+ then
+ have_xapian_compact=1
+ printf "Yes.\n"
+ else
+ printf "No.\n"
+ fi
printf "Checking for Xapian FieldProcessor API... "
cat>_field_processor.cc<<EOF
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 05/12] lib: provide config API
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (3 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 04/12] configure: check directly for xapian compaction API David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 06/12] lib: config list iterators David Bremner
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
This is a thin wrapper around the Xapian metadata API. The job of this
layer is to keep the config key value pairs from colliding with other
metadata by transparently prefixing the keys, along with the usual glue
to provide a C interface.
The split of _get_config into two functions is to allow returning of the
return value with different memory ownership semantics.
---
lib/Makefile.local | 1 +
lib/config.cc | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/notmuch.h | 20 +++++++++++
test/T590-libconfig.sh | 58 ++++++++++++++++++++++++++++++++
4 files changed, 170 insertions(+)
create mode 100644 lib/config.cc
create mode 100755 test/T590-libconfig.sh
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 36c3924..76b57cb 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
$(dir)/index.cc \
$(dir)/message.cc \
$(dir)/query.cc \
+ $(dir)/config.cc \
$(dir)/thread.cc
libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
diff --git a/lib/config.cc b/lib/config.cc
new file mode 100644
index 0000000..ce8fb31
--- /dev/null
+++ b/lib/config.cc
@@ -0,0 +1,91 @@
+/* config.cc - API for database metadata
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * 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: David Bremner <david@tethera.net>
+ */
+
+#include "notmuch.h"
+#include "notmuch-private.h"
+#include "database-private.h"
+
+static const std::string CONFIG_PREFIX = "C";
+
+notmuch_status_t
+notmuch_database_set_config (notmuch_database_t *notmuch,
+ const char *key,
+ const char *value)
+{
+ notmuch_status_t status;
+ Xapian::WritableDatabase *db;
+
+ status = _notmuch_database_ensure_writable (notmuch);
+ if (status)
+ return status;
+
+ try {
+ db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+ db->set_metadata (CONFIG_PREFIX + key, value);
+ } catch (const Xapian::Error &error) {
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ notmuch->exception_reported = TRUE;
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+static notmuch_status_t
+_metadata_value (notmuch_database_t *notmuch,
+ const char *key,
+ std::string &value)
+{
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+ try {
+ value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
+ } catch (const Xapian::Error &error) {
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ notmuch->exception_reported = TRUE;
+ if (! notmuch->exception_reported) {
+ _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
+ error.get_msg().c_str());
+ }
+ }
+ return status;
+}
+
+notmuch_status_t
+notmuch_database_get_config (notmuch_database_t *notmuch,
+ const char *key,
+ char **value)
+{
+ std::string strval;
+ notmuch_status_t status;
+
+ if (! value)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ status = _metadata_value (notmuch, key, strval);
+ if (status)
+ return status;
+
+ *value = strdup (strval.c_str ());
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 3a092ef..c827e02 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1838,6 +1838,26 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames);
void
notmuch_filenames_destroy (notmuch_filenames_t *filenames);
+
+/**
+ * set config 'key' to 'value'
+ *
+ */
+notmuch_status_t
+notmuch_database_set_config (notmuch_database_t *db, const char *key, const char *value);
+
+/**
+ * retrieve config item 'key', assign to 'value'
+ *
+ * keys which have not been previously set with n_d_set_config will
+ * return an empty string.
+ *
+ * return value is allocated by malloc and should be freed by the
+ * caller.
+ */
+notmuch_status_t
+notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value);
+
/**
* interrogate the library for compile time features
*/
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
new file mode 100755
index 0000000..85e4497
--- /dev/null
+++ b/test/T590-libconfig.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+test_description="library config API"
+
+. ./test-lib.sh || exit 1
+
+add_email_corpus
+
+cat <<EOF > c_head
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <notmuch.h>
+
+void run(int line, notmuch_status_t ret)
+{
+ if (ret) {
+ fprintf (stderr, "line %d: %s\n", line, ret);
+ exit (1);
+ }
+}
+
+#define RUN(v) run(__LINE__, v);
+
+int main (int argc, char** argv)
+{
+ notmuch_database_t *db;
+ char *val;
+ notmuch_status_t stat;
+
+ RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+
+EOF
+
+cat <<EOF > c_tail
+ RUN(notmuch_database_destroy(db));
+}
+EOF
+
+test_begin_subtest "notmuch_database_{set,get}_config"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ RUN(notmuch_database_set_config (db, "testkey1", "testvalue1"));
+ RUN(notmuch_database_set_config (db, "testkey2", "testvalue2"));
+ RUN(notmuch_database_get_config (db, "testkey1", &val));
+ printf("testkey1 = %s\n", val);
+ RUN(notmuch_database_get_config (db, "testkey2", &val));
+ printf("testkey2 = %s\n", val);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1 = testvalue1
+testkey2 = testvalue2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 06/12] lib: config list iterators
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (4 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 05/12] lib: provide config API David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 07/12] CLI: add optional config data to dump output David Bremner
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
Since xapian provides the ability to restrict the iterator to a given
prefix, we expose this ability to the user. Otherwise we mimic the other
iterator interfances in notmuch (e.g. tags.c).
---
lib/config.cc | 105 +++++++++++++++++++++++++++++++++++++++++++++++++
lib/notmuch.h | 44 +++++++++++++++++++++
test/T590-libconfig.sh | 60 ++++++++++++++++++++++++++++
3 files changed, 209 insertions(+)
diff --git a/lib/config.cc b/lib/config.cc
index ce8fb31..35c917b 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -24,6 +24,20 @@
static const std::string CONFIG_PREFIX = "C";
+struct _notmuch_config_list {
+ notmuch_database_t *notmuch;
+ Xapian::TermIterator *iterator;
+ char *current_key;
+ char *current_val;
+};
+
+static int
+_notmuch_config_list_destroy (notmuch_config_list_t *list)
+{
+ delete list->iterator;
+ return 0;
+}
+
notmuch_status_t
notmuch_database_set_config (notmuch_database_t *notmuch,
const char *key,
@@ -89,3 +103,94 @@ notmuch_database_get_config (notmuch_database_t *notmuch,
return NOTMUCH_STATUS_SUCCESS;
}
+
+notmuch_status_t
+notmuch_database_get_config_list (notmuch_database_t *notmuch,
+ const char *prefix,
+ notmuch_config_list_t **out)
+{
+ notmuch_config_list_t *list = NULL;
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+ list = talloc (notmuch, notmuch_config_list_t);
+ if (! list) {
+ status = NOTMUCH_STATUS_OUT_OF_MEMORY;
+ goto DONE;
+ }
+
+ talloc_set_destructor (list, _notmuch_config_list_destroy);
+ list->iterator = new Xapian::TermIterator;
+ list->notmuch = notmuch;
+ list->current_key = NULL;
+ list->current_val = NULL;
+
+ try {
+
+ *list->iterator = notmuch->xapian_db->metadata_keys_begin (CONFIG_PREFIX + (prefix ? prefix : ""));
+
+ } catch (const Xapian::Error &error) {
+ _notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n",
+ error.get_msg().c_str());
+ notmuch->exception_reported = TRUE;
+ status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+
+ *out = list;
+
+ DONE:
+ if (status && list)
+ talloc_free (list);
+
+ return status;
+}
+
+notmuch_bool_t
+notmuch_config_list_valid (notmuch_config_list_t *metadata)
+{
+ if (*(metadata->iterator) == metadata->notmuch->xapian_db->metadata_keys_end ())
+ return FALSE;
+
+ return TRUE;
+}
+
+const char *
+notmuch_config_list_key (notmuch_config_list_t *list)
+{
+ if (list->current_key)
+ talloc_free (list->current_key);
+
+ list->current_key = talloc_strdup (list, (**(list->iterator)).c_str () + CONFIG_PREFIX.length ());
+
+ return list->current_key;
+}
+
+const char *
+notmuch_config_list_value (notmuch_config_list_t *list)
+{
+ std::string strval;
+ notmuch_status_t status;
+ const char *key = notmuch_config_list_key (list);
+
+ /* TODO: better error reporting?? */
+ status = _metadata_value (list->notmuch, key, strval);
+ if (status)
+ return NULL;
+
+ if (list->current_val)
+ talloc_free (list->current_val);
+
+ list->current_val = talloc_strdup (list, strval.c_str ());
+ return list->current_val;
+}
+
+void
+notmuch_config_list_move_to_next (notmuch_config_list_t *list)
+{
+ (*(list->iterator))++;
+}
+
+void
+notmuch_config_list_destroy (notmuch_config_list_t *list)
+{
+ talloc_free (list);
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index c827e02..bd977c3 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -206,6 +206,7 @@ typedef struct _notmuch_message notmuch_message_t;
typedef struct _notmuch_tags notmuch_tags_t;
typedef struct _notmuch_directory notmuch_directory_t;
typedef struct _notmuch_filenames notmuch_filenames_t;
+typedef struct _notmuch_config_list notmuch_config_list_t;
#endif /* __DOXYGEN__ */
/**
@@ -1859,6 +1860,49 @@ notmuch_status_t
notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value);
/**
+ * Create an iterator for all config items with keys matching a given prefix
+ */
+notmuch_status_t
+notmuch_database_get_config_list (notmuch_database_t *db, const char *prefix, notmuch_config_list_t **out);
+
+/**
+ * Is 'config_list' iterator valid (i.e. _key, _value, _move_to_next can be called).
+ */
+notmuch_bool_t
+notmuch_config_list_valid (notmuch_config_list_t *config_list);
+
+/**
+ * return key for current config pair
+ *
+ * return value is owned by the iterator, and will be destroyed by the
+ * next call to notmuch_config_list_key or notmuch_config_list_destroy.
+ */
+const char *
+notmuch_config_list_key (notmuch_config_list_t *config_list);
+
+/**
+ * return 'value' for current config pair
+ *
+ * return value is owned by the iterator, and will be destroyed by the
+ * next call to notmuch_config_list_value or notmuch config_list_destroy
+ */
+const char *
+notmuch_config_list_value (notmuch_config_list_t *config_list);
+
+
+/**
+ * move 'config_list' iterator to the next pair
+ */
+void
+notmuch_config_list_move_to_next (notmuch_config_list_t *config_list);
+
+/**
+ * free any resources held by 'config_list'
+ */
+void
+notmuch_config_list_destroy (notmuch_config_list_t *config_list);
+
+/**
* interrogate the library for compile time features
*/
notmuch_bool_t
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
index 85e4497..8ca6883 100755
--- a/test/T590-libconfig.sh
+++ b/test/T590-libconfig.sh
@@ -55,4 +55,64 @@ testkey2 = testvalue2
EOF
test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "notmuch_database_get_config_list: empty list"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ notmuch_config_list_t *list;
+ RUN(notmuch_database_get_config_list (db, "nonexistent", &list));
+ printf("valid = %d\n", notmuch_config_list_valid (list));
+ notmuch_config_list_destroy (list);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+valid = 0
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+
+test_begin_subtest "notmuch_database_get_config_list: all pairs"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ notmuch_config_list_t *list;
+ RUN(notmuch_database_set_config (db, "zzzafter", "afterval"));
+ RUN(notmuch_database_set_config (db, "aaabefore", "beforeval"));
+ RUN(notmuch_database_get_config_list (db, "", &list));
+ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+ printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list));
+ }
+ notmuch_config_list_destroy (list);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+aaabefore beforeval
+testkey1 testvalue1
+testkey2 testvalue2
+zzzafter afterval
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "notmuch_database_get_config_list: one prefix"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ notmuch_config_list_t *list;
+ RUN(notmuch_database_get_config_list (db, "testkey", &list));
+ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+ printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list));
+ }
+ notmuch_config_list_destroy (list);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+testkey1 testvalue1
+testkey2 testvalue2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_done
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 07/12] CLI: add optional config data to dump output.
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (5 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 06/12] lib: config list iterators David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 08/12] CLI: optionally restore config data David Bremner
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
Note that it changes the default dump output format, but doesn't break
existing notmuch-restore. It might break user scripts though.
---
doc/man1/notmuch-dump.rst | 17 ++++++++++
notmuch-client.h | 8 +++++
notmuch-dump.c | 80 +++++++++++++++++++++++++++++++++++++++++++++--
notmuch-new.c | 2 +-
test/T150-tagging.sh | 8 ++---
test/T240-dump-restore.sh | 14 ++++-----
test/T590-libconfig.sh | 17 ++++++++++
test/test-lib.sh | 6 ++++
8 files changed, 137 insertions(+), 15 deletions(-)
diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst
index a37c337..eda9e07 100644
--- a/doc/man1/notmuch-dump.rst
+++ b/doc/man1/notmuch-dump.rst
@@ -71,6 +71,23 @@ Supported options for **dump** include
characters. Note also that tags with spaces will not be
correctly restored with this format.
+ ``--include=(config|tags)``
+
+ Control what kind of metadata is included in the output.
+
+ **config**
+
+ Output configuration data stored in the database. Each line
+ starts with "#@ ", followed by a space seperated key-value
+ pair. Both key and value are hex encoded if needed.
+
+ **tags**
+
+ Output per-message metadata, namely tags. See *format* above
+ for description of the output.
+
+ The default is to include both tags and configuration information
+
``--output=``\ <filename>
Write output to given file instead of stdout.
diff --git a/notmuch-client.h b/notmuch-client.h
index b3d0b66..ae6f124 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -446,11 +446,19 @@ typedef enum dump_formats {
DUMP_FORMAT_SUP
} dump_format_t;
+typedef enum dump_includes {
+ DUMP_INCLUDE_TAGS=1,
+ DUMP_INCLUDE_CONFIG=2,
+} dump_include_t;
+
+#define NOTMUCH_DUMP_VERSION 2
+
int
notmuch_database_dump (notmuch_database_t *notmuch,
const char *output_file_name,
const char *query_str,
dump_format_t output_format,
+ dump_include_t include,
notmuch_bool_t gzip_output);
/* If status is non-zero (i.e. error) print appropriate
diff --git a/notmuch-dump.c b/notmuch-dump.c
index 829781f..a6cf810 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -23,16 +23,82 @@
#include "string-util.h"
#include <zlib.h>
+static int
+database_dump_config (notmuch_database_t *notmuch, gzFile output)
+{
+ notmuch_config_list_t *list;
+ int ret = EXIT_FAILURE;
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+
+ if (print_status_database ("notmuch dump", notmuch,
+ notmuch_database_get_config_list (notmuch, NULL, &list)))
+ goto DONE;
+
+ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+ if (hex_encode (notmuch, notmuch_config_list_key (list),
+ &buffer, &buffer_size) != HEX_SUCCESS) {
+ fprintf (stderr, "Error: failed to hex-encode config key %s\n",
+ notmuch_config_list_key (list));
+ goto DONE;
+ }
+ gzprintf (output, "#@ %s", buffer);
+
+ if (hex_encode (notmuch, notmuch_config_list_value (list),
+ &buffer, &buffer_size) != HEX_SUCCESS) {
+ fprintf (stderr, "Error: failed to hex-encode config value %s\n",
+ notmuch_config_list_value (list) );
+ goto DONE;
+ }
+
+ gzprintf (output, " %s\n", buffer);
+ }
+
+ ret = EXIT_SUCCESS;
+
+ DONE:
+ if (list)
+ notmuch_config_list_destroy (list);
+
+ if (buffer)
+ talloc_free (buffer);
+
+ return ret;
+}
+
+static void
+print_dump_header (gzFile output, int output_format, int include)
+{
+ gzprintf (output, "#notmuch-dump %s:%d %s%s%s\n",
+ (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag",
+ NOTMUCH_DUMP_VERSION,
+ (include & DUMP_INCLUDE_CONFIG) ? "config" : "",
+ (include & DUMP_INCLUDE_TAGS) && (include & DUMP_INCLUDE_CONFIG) ? "," : "",
+ (include & DUMP_INCLUDE_TAGS) ? "tags" : "");
+
+
+}
static int
database_dump_file (notmuch_database_t *notmuch, gzFile output,
- const char *query_str, int output_format)
+ const char *query_str, int output_format, int include)
{
notmuch_query_t *query;
notmuch_messages_t *messages;
notmuch_message_t *message;
notmuch_tags_t *tags;
+ print_dump_header (output, output_format, include);
+
+ if (include & DUMP_INCLUDE_CONFIG) {
+ if (print_status_database ("notmuch dump", notmuch,
+ database_dump_config(notmuch,output)))
+ return EXIT_FAILURE;
+ }
+
+ if (! (include & DUMP_INCLUDE_TAGS))
+ return EXIT_SUCCESS;
+
if (! query_str)
query_str = "";
@@ -130,6 +196,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
const char *output_file_name,
const char *query_str,
dump_format_t output_format,
+ dump_include_t include,
notmuch_bool_t gzip_output)
{
gzFile output = NULL;
@@ -164,7 +231,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
goto DONE;
}
- ret = database_dump_file (notmuch, output, query_str, output_format);
+ ret = database_dump_file (notmuch, output, query_str, output_format, include);
if (ret) goto DONE;
ret = gzflush (output, Z_FINISH);
@@ -226,6 +293,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
int opt_index;
int output_format = DUMP_FORMAT_BATCH_TAG;
+ int include = 0;
notmuch_bool_t gzip_output = 0;
notmuch_opt_desc_t options[] = {
@@ -233,6 +301,9 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
(notmuch_keyword_t []){ { "sup", DUMP_FORMAT_SUP },
{ "batch-tag", DUMP_FORMAT_BATCH_TAG },
{ 0, 0 } } },
+ { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I',
+ (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG },
+ { "tags", DUMP_INCLUDE_TAGS} } },
{ NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 },
{ NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
{ NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 },
@@ -245,6 +316,9 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
notmuch_process_shared_options (argv[0]);
+ if (include == 0)
+ include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS;
+
if (opt_index < argc) {
query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
if (query_str == NULL) {
@@ -254,7 +328,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
}
ret = notmuch_database_dump (notmuch, output_file_name, query_str,
- output_format, gzip_output);
+ output_format, include, gzip_output);
notmuch_database_destroy (notmuch);
diff --git a/notmuch-new.c b/notmuch-new.c
index 2d975eb..8d1545e 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -1042,7 +1042,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
}
if (notmuch_database_dump (notmuch, backup_name, "",
- DUMP_FORMAT_BATCH_TAG, TRUE)) {
+ DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) {
fprintf (stderr, "Backup failed. Aborting upgrade.");
return EXIT_FAILURE;
}
diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh
index a451ffa..61d1311 100755
--- a/test/T150-tagging.sh
+++ b/test/T150-tagging.sh
@@ -188,7 +188,7 @@ cat <<EOF > EXPECTED
+%22%27%22%27%22%22%27%27 +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite
EOF
-notmuch dump --format=batch-tag | sort > OUTPUT
+NOTMUCH_DUMP_TAGS > OUTPUT
notmuch restore --format=batch-tag < BACKUP
test_expect_equal_file EXPECTED OUTPUT
@@ -209,7 +209,7 @@ cat <<EOF > EXPECTED
+%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
EOF
-notmuch dump --format=batch-tag | sort > OUTPUT
+NOTMUCH_DUMP_TAGS > OUTPUT
notmuch restore --format=batch-tag < BACKUP
test_expect_equal_file EXPECTED OUTPUT
@@ -235,7 +235,7 @@ cat <<EOF > EXPECTED
+%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 +=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d +A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 +P%c4%98%2f +R +inbox +tag5 +unread +%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d +%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 +%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-001@notmuch-test-suite
EOF
-notmuch dump --format=batch-tag | sort > OUTPUT
+NOTMUCH_DUMP_TAGS > OUTPUT
notmuch restore --format=batch-tag < BACKUP
test_expect_equal_file EXPECTED OUTPUT
@@ -260,7 +260,7 @@ cat <<EOF > EXPECTED
+foo%3a%3abar%25 +found%3a%3ait +inbox +tag5 +unread +winner -- id:msg-001@notmuch-test-suite
EOF
-notmuch dump --format=batch-tag | sort > OUTPUT
+NOTMUCH_DUMP_TAGS > OUTPUT
notmuch restore --format=batch-tag < BACKUP
test_expect_equal_file EXPECTED OUTPUT
diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh
index e6976ff..bbfb09b 100755
--- a/test/T240-dump-restore.sh
+++ b/test/T240-dump-restore.sh
@@ -97,7 +97,7 @@ test_expect_equal_file dump.expected dump.actual
# Note, we assume all messages from cworth have a message-id
# containing cworth.org
-grep 'cworth[.]org' dump.expected > dump-cworth.expected
+{ head -1 dump.expected ; grep 'cworth[.]org' dump.expected; } > dump-cworth.expected
test_begin_subtest "dump -- from:cworth"
notmuch dump -- from:cworth > dump-dash-cworth.actual
@@ -118,16 +118,16 @@ notmuch search --output=messages from:cworth | sed s/^id:// |\
test_expect_equal_file OUTPUT EXPECTED
test_begin_subtest "format=batch-tag, dump sanity check."
-notmuch dump --format=sup from:cworth | cut -f1 -d' ' | \
+NOTMUCH_DUMP_TAGS --format=sup from:cworth | cut -f1 -d' ' | \
sort > EXPECTED.$test_count
-notmuch dump --format=batch-tag from:cworth | sed 's/^.*-- id://' | \
+NOTMUCH_DUMP_TAGS --format=batch-tag from:cworth | sed 's/^.*-- id://' | \
sort > OUTPUT.$test_count
test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
test_begin_subtest "format=batch-tag, missing newline"
printf "+a_tag_without_newline -- id:20091117232137.GA7669@griffis1.net" > IN
notmuch restore --accumulate < IN
-notmuch dump id:20091117232137.GA7669@griffis1.net > OUT
+NOTMUCH_DUMP_TAGS id:20091117232137.GA7669@griffis1.net > OUT
cat <<EOF > EXPECTED
+a_tag_without_newline +inbox +unread -- id:20091117232137.GA7669@griffis1.net
EOF
@@ -155,7 +155,7 @@ cat <<EOF >EXPECTED.$test_count
+ -- id:20091117232137.GA7669@griffis1.net
EOF
notmuch restore --format=batch-tag < EXPECTED.$test_count
-notmuch dump --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count
+NOTMUCH_DUMP_TAGS --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count
test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
tag1='comic_swear=$&^%$^%\\//-+$^%$'
@@ -217,9 +217,9 @@ notmuch dump --format=batch-tag > OUTPUT.$test_count
test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
test_begin_subtest 'format=batch-tag, checking encoded output'
-notmuch dump --format=batch-tag -- from:cworth |\
+NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth |\
awk "{ print \"+$enc1 +$enc2 +$enc3 -- \" \$5 }" > EXPECTED.$test_count
-notmuch dump --format=batch-tag -- from:cworth > OUTPUT.$test_count
+NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth > OUTPUT.$test_count
test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
test_begin_subtest 'restoring sane tags'
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
index 8ca6883..5ea5300 100755
--- a/test/T590-libconfig.sh
+++ b/test/T590-libconfig.sh
@@ -115,4 +115,21 @@ testkey2 testvalue2
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "dump config"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ RUN(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!"));
+}
+EOF
+notmuch dump --include=config >OUTPUT
+cat <<'EOF' >EXPECTED
+#notmuch-dump batch-tag:2 config
+#@ aaabefore beforeval
+#@ key%20with%20spaces value,%20with,%20spaces%21
+#@ testkey1 testvalue1
+#@ testkey2 testvalue2
+#@ zzzafter afterval
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 09f8731..68a8774 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -673,6 +673,12 @@ NOTMUCH_NEW ()
notmuch new "${@}" | grep -v -E -e '^Processed [0-9]*( total)? file|Found [0-9]* total file'
}
+NOTMUCH_DUMP_TAGS ()
+{
+ # this relies on the default format being batch-tag, otherwise some tests will break
+ notmuch dump --include=tags "${@}" | sed '/^#/d' | sort
+}
+
notmuch_search_sanitize ()
{
perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 08/12] CLI: optionally restore config data.
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (6 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 07/12] CLI: add optional config data to dump output David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 09/12] CLI: add notmuch-config support for named queries David Bremner
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
The default to restore config data seems safest, especially since
currently we have no config data to mess up.
---
doc/man1/notmuch-restore.rst | 18 +++++++++++++++
notmuch-restore.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
test/T590-libconfig.sh | 11 +++++++++
3 files changed, 82 insertions(+)
diff --git a/doc/man1/notmuch-restore.rst b/doc/man1/notmuch-restore.rst
index 362e262..87fa22e 100644
--- a/doc/man1/notmuch-restore.rst
+++ b/doc/man1/notmuch-restore.rst
@@ -50,6 +50,24 @@ Supported options for **restore** include
format, this heuristic, based the fact that batch-tag format
contains no parentheses, should be accurate.
+ ``--include=(config|tags)``
+
+ Control what kind of metadata is restored.
+
+ **config**
+
+ Restore configuration data to the database. Each configuration line starts
+ with "#@ ", followed by a space seperated key-value pair.
+ Both key and value are hex encoded if needed.
+
+ **tags**
+
+ Output per-message metadata, namely tags. See *format* above
+ for more details.
+
+ The default is to restore both tags and configuration
+ information
+
``--input=``\ <filename>
Read input from given file instead of stdin.
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 9abc64f..e06fbde 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -24,6 +24,38 @@
#include "string-util.h"
#include "zlib-extra.h"
+static int
+process_config_line(notmuch_database_t *notmuch, const char* line){
+ const char *key_p, *val_p;
+ char *key, *val;
+ size_t key_len,val_len;
+ const char *delim=" \t\n";
+ int ret = EXIT_FAILURE;
+
+ void *local = talloc_new(NULL);
+
+ key_p = strtok_len_c(line, delim, &key_len);
+ val_p = strtok_len_c(key_p+key_len, delim, &val_len);
+
+ key = talloc_strndup(local, key_p, key_len);
+ val = talloc_strndup(local, val_p, val_len);
+ if (hex_decode_inplace (key) != HEX_SUCCESS ||
+ hex_decode_inplace (val) != HEX_SUCCESS ) {
+ fprintf (stderr, "hex decoding failure on line %s\n", line);
+ goto DONE;
+ }
+
+ if (print_status_database ("notmuch restore", notmuch,
+ notmuch_database_set_config (notmuch, key, val)))
+ goto DONE;
+
+ ret = EXIT_SUCCESS;
+
+ DONE:
+ talloc_free (local);
+ return ret;
+}
+
static regex_t regex;
/* Non-zero return indicates an error in retrieving the message,
@@ -137,6 +169,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
int ret = 0;
int opt_index;
+ int include=0;
int input_format = DUMP_FORMAT_AUTO;
if (notmuch_database_open (notmuch_config_get_database_path (config),
@@ -152,6 +185,10 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
{ "batch-tag", DUMP_FORMAT_BATCH_TAG },
{ "sup", DUMP_FORMAT_SUP },
{ 0, 0 } } },
+ { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I',
+ (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG },
+ { "tags", DUMP_INCLUDE_TAGS} } },
+
{ NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },
{ NOTMUCH_OPT_BOOLEAN, &accumulate, "accumulate", 'a', 0 },
{ NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 },
@@ -167,6 +204,10 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
notmuch_process_shared_options (argv[0]);
notmuch_exit_if_unmatched_db_uuid (notmuch);
+ if (include == 0) {
+ include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS;
+ }
+
name_for_error = input_file_name ? input_file_name : "stdin";
if (! accumulate)
@@ -225,11 +266,23 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[])
ret = EXIT_FAILURE;
goto DONE;
}
+
+ if ((include & DUMP_INCLUDE_CONFIG) && line_len >= 2 && line[0] == '#' && line[1] == '@') {
+ ret = process_config_line(notmuch, line+2);
+ if (ret)
+ goto DONE;
+ }
+
} while ((line_len == 0) ||
(line[0] == '#') ||
/* the cast is safe because we checked about for line_len < 0 */
(strspn (line, " \t\n") == (unsigned)line_len));
+ if (! (include & DUMP_INCLUDE_TAGS)) {
+ ret = EXIT_SUCCESS;
+ goto DONE;
+ }
+
char *p;
for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
if (*p == '(')
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
index 5ea5300..9c1e566 100755
--- a/test/T590-libconfig.sh
+++ b/test/T590-libconfig.sh
@@ -132,4 +132,15 @@ cat <<'EOF' >EXPECTED
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "restore config"
+notmuch dump --include=config >EXPECTED
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+{
+ RUN(notmuch_database_set_config (db, "testkey1", "mutatedvalue"));
+}
+EOF
+notmuch restore --include=config <EXPECTED
+notmuch dump --include=config >OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
test_done
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 09/12] CLI: add notmuch-config support for named queries
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (7 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 08/12] CLI: optionally restore config data David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 10/12] lib: make a global constant for query parser flags David Bremner
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
Most of the infrastructure here is general, only the validation/dispatch
is hardcoded to a particular prefix.
A notable change in behaviour is that notmuch-config now opens the
database e.g. on every call to list, which fails with an error message
if the database doesn't exit yet.
---
doc/man1/notmuch-config.rst | 6 ++++
notmuch-config.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-
test/Makefile.local | 2 +-
test/T030-config.sh | 12 ++++---
test/T600-named-queries.sh | 53 +++++++++++++++++++++++++++
test/test-lib.sh | 5 +++
6 files changed, 159 insertions(+), 7 deletions(-)
create mode 100755 test/T600-named-queries.sh
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 26a8eb1..5a517eb 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -138,6 +138,12 @@ The available configuration items are described below.
"compact" (see **notmuch-compact(1)**)
and "field_processor" (see **notmuch-search-terms(7)**).
+ **query.<name>**
+
+ Expansion for named query called <name>. See
+ **notmuch-search-terms(7)** for more information about named
+ queries.
+
ENVIRONMENT
===========
diff --git a/notmuch-config.c b/notmuch-config.c
index 97a46fa..e4f47e4 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -751,6 +751,28 @@ _item_split (char *item, char **group, char **key)
}
#define BUILT_WITH_PREFIX "built_with."
+#define QUERY_PREFIX "query."
+
+static int
+_print_db_config(notmuch_config_t *config, const char *name)
+{
+ notmuch_database_t *notmuch;
+ char *val;
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_get_config (notmuch, name, &val)))
+ return EXIT_FAILURE;
+
+ puts (val);
+
+ return EXIT_SUCCESS;
+}
static int
notmuch_config_command_get (notmuch_config_t *config, char *item)
@@ -778,6 +800,8 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
} else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
printf ("%s\n",
notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false");
+ } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+ return _print_db_config (config, item);
} else {
char **value;
size_t i, length;
@@ -805,6 +829,39 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
}
static int
+_set_db_config(notmuch_config_t *config, const char *key, int argc, char **argv)
+{
+ notmuch_database_t *notmuch;
+ const char *val = "";
+
+ if (argc > 1) {
+ /* XXX handle lists? */
+ fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key);
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 0) {
+ val = argv[0];
+ }
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_set_config (notmuch, key, val)))
+ return EXIT_FAILURE;
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_close (notmuch)))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static int
notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
{
char *group, *key;
@@ -814,6 +871,10 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
return 1;
}
+ if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+ return _set_db_config (config, item, argc, argv);
+ }
+
if (_item_split (item, &group, &key))
return 1;
@@ -852,6 +913,31 @@ _notmuch_config_list_options () {
}
static int
+_list_db_config (notmuch_config_t *config)
+{
+ notmuch_database_t *notmuch;
+ notmuch_config_list_t *list;
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_get_config_list (notmuch, "", &list)))
+ return EXIT_FAILURE;
+
+ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+ printf("%s=%s\n", notmuch_config_list_key (list), notmuch_config_list_value(list));
+ }
+ notmuch_config_list_destroy (list);
+
+ return EXIT_SUCCESS;
+}
+
+static int
notmuch_config_command_list (notmuch_config_t *config)
{
char **groups;
@@ -887,7 +973,7 @@ notmuch_config_command_list (notmuch_config_t *config)
g_strfreev (groups);
_notmuch_config_list_options ();
- return 0;
+ return _list_db_config (config);
}
int
diff --git a/test/Makefile.local b/test/Makefile.local
index 022f2cf..91b3693 100644
--- a/test/Makefile.local
+++ b/test/Makefile.local
@@ -19,7 +19,7 @@ $(dir)/hex-xcode: $(dir)/hex-xcode.o command-line-arguments.o util/libutil.a
$(call quiet,CC) $^ -o $@ $(LDFLAGS) $(TALLOC_LDFLAGS)
random_corpus_deps = $(dir)/random-corpus.o $(dir)/database-test.o \
- notmuch-config.o command-line-arguments.o \
+ notmuch-config.o status.o command-line-arguments.o \
lib/libnotmuch.a util/libutil.a \
parse-time-string/libparse-time-string.a
diff --git a/test/T030-config.sh b/test/T030-config.sh
index 437269f..b8d5a86 100755
--- a/test/T030-config.sh
+++ b/test/T030-config.sh
@@ -43,10 +43,10 @@ notmuch config set foo.nonexistent
test_expect_equal "$(notmuch config get foo.nonexistent)" ""
test_begin_subtest "List all items"
-notmuch config set database.path "/canonical/path"
-output=$(notmuch config list | notmuch_built_with_sanitize)
-test_expect_equal "$output" "\
-database.path=/canonical/path
+notmuch config list 2>&1 | notmuch_config_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+Error opening database at MAIL_DIR/.notmuch: No such file or directory
+database.path=MAIL_DIR
user.name=Notmuch Test Suite
user.primary_email=test_suite@notmuchmail.org
user.other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
@@ -58,7 +58,9 @@ crypto.gpg_path=gpg
foo.string=this is another string value
foo.list=this;is another;list value;
built_with.compact=something
-built_with.field_processor=something"
+built_with.field_processor=something
+EOF
+test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "Top level --config=FILE option"
cp "${NOTMUCH_CONFIG}" alt-config
diff --git a/test/T600-named-queries.sh b/test/T600-named-queries.sh
new file mode 100755
index 0000000..0922620
--- /dev/null
+++ b/test/T600-named-queries.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+test_description='named queries'
+. ./test-lib.sh || exit 1
+
+QUERYSTR="date:2009-11-18..2009-11-18 and tag:unread"
+
+test_expect_code 1 "error adding named query before initializing DB" \
+ "notmuch config set query.test \"$QUERYSTR\""
+
+add_email_corpus
+
+test_expect_success "adding named query" \
+ "notmuch config set query.test \"$QUERYSTR\""
+
+QUERYSTR2="query:test and subject:Maildir"
+test_expect_success "adding nested named query" \
+ "notmuch config set query.test2 \"$QUERYSTR2\""
+
+test_begin_subtest "retrieve named query"
+output=$(notmuch config get query.test)
+test_expect_equal "$QUERYSTR" "$output"
+
+test_begin_subtest "List all queries"
+notmuch config list | grep ^query | notmuch_config_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+query.test=date:2009-11-18..2009-11-18 and tag:unread
+query.test2=query:test and subject:Maildir
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "dump named queries"
+notmuch dump | grep '^#@' > OUTPUT
+cat<<EOF > QUERIES.BEFORE
+#@ query.test date%3a2009-11-18..2009-11-18%20and%20tag%3aunread
+#@ query.test2 query%3atest%20and%20subject%3aMaildir
+EOF
+test_expect_equal_file QUERIES.BEFORE OUTPUT
+
+test_begin_subtest "delete named queries"
+notmuch dump > BEFORE
+notmuch config set query.test
+notmuch dump | grep '^#@' > OUTPUT
+cat<<EOF > EXPECTED
+#@ query.test2 query%3atest%20and%20subject%3aMaildir
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "restore named queries"
+notmuch restore < BEFORE
+notmuch dump | grep '^#@' > OUTPUT
+test_expect_equal_file QUERIES.BEFORE OUTPUT
+
+test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 68a8774..8fef275 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -745,6 +745,11 @@ notmuch_built_with_sanitize ()
sed 's/^built_with[.]\(.*\)=.*$/built_with.\1=something/'
}
+notmuch_config_sanitize ()
+{
+ notmuch_dir_sanitize | notmuch_built_with_sanitize
+}
+
# End of notmuch helper functions
# Use test_set_prereq to tell that a particular prerequisite is available.
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 10/12] lib: make a global constant for query parser flags
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (8 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 09/12] CLI: add notmuch-config support for named queries David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 11/12] lib: add support for named queries David Bremner
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
It's already kindof gross that this is hardcoded in two different
places. We will also need these later in field processors calling back
into the query parser.
---
lib/database-private.h | 7 +++++++
lib/query.cc | 16 ++--------------
2 files changed, 9 insertions(+), 14 deletions(-)
diff --git a/lib/database-private.h b/lib/database-private.h
index e1962f4..d2990b6 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -144,6 +144,13 @@ operator&=(_notmuch_features &a, _notmuch_features b)
return a;
}
+#define NOTMUCH_QUERY_PARSER_FLAGS (Xapian::QueryParser::FLAG_BOOLEAN | \
+ Xapian::QueryParser::FLAG_PHRASE | \
+ Xapian::QueryParser::FLAG_LOVEHATE | \
+ Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE | \
+ Xapian::QueryParser::FLAG_WILDCARD | \
+ Xapian::QueryParser::FLAG_PURE_NOT)
+
struct _notmuch_database {
notmuch_bool_t exception_reported;
diff --git a/lib/query.cc b/lib/query.cc
index 77a7926..0875b0e 100644
--- a/lib/query.cc
+++ b/lib/query.cc
@@ -220,12 +220,6 @@ _notmuch_query_search_documents (notmuch_query_t *query,
Xapian::Query string_query, final_query, exclude_query;
Xapian::MSet mset;
Xapian::MSetIterator iterator;
- unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
- Xapian::QueryParser::FLAG_PHRASE |
- Xapian::QueryParser::FLAG_LOVEHATE |
- Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE |
- Xapian::QueryParser::FLAG_WILDCARD |
- Xapian::QueryParser::FLAG_PURE_NOT);
if (strcmp (query_string, "") == 0 ||
strcmp (query_string, "*") == 0)
@@ -233,7 +227,7 @@ _notmuch_query_search_documents (notmuch_query_t *query,
final_query = mail_query;
} else {
string_query = notmuch->query_parser->
- parse_query (query_string, flags);
+ parse_query (query_string, NOTMUCH_QUERY_PARSER_FLAGS);
final_query = Xapian::Query (Xapian::Query::OP_AND,
mail_query, string_query);
}
@@ -579,12 +573,6 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign
type));
Xapian::Query string_query, final_query, exclude_query;
Xapian::MSet mset;
- unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
- Xapian::QueryParser::FLAG_PHRASE |
- Xapian::QueryParser::FLAG_LOVEHATE |
- Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE |
- Xapian::QueryParser::FLAG_WILDCARD |
- Xapian::QueryParser::FLAG_PURE_NOT);
if (strcmp (query_string, "") == 0 ||
strcmp (query_string, "*") == 0)
@@ -592,7 +580,7 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign
final_query = mail_query;
} else {
string_query = notmuch->query_parser->
- parse_query (query_string, flags);
+ parse_query (query_string, NOTMUCH_QUERY_PARSER_FLAGS);
final_query = Xapian::Query (Xapian::Query::OP_AND,
mail_query, string_query);
}
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 11/12] lib: add support for named queries
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (9 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 10/12] lib: make a global constant for query parser flags David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 0:04 ` [Patch v4 12/12] fixup! lib/cli: add library API / CLI for compile time options David Bremner
2016-05-08 8:25 ` v4 of libconfig / single argument date / named query patches Tomi Ollila
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
This relies on the optional presense of xapian field processors, and the
library config API.
---
doc/man7/notmuch-search-terms.rst | 8 +++++++
lib/Makefile.local | 1 +
lib/database-private.h | 1 +
lib/database.cc | 3 +++
lib/query-fp.cc | 44 +++++++++++++++++++++++++++++++++++++++
lib/query-fp.h | 42 +++++++++++++++++++++++++++++++++++++
test/T600-named-queries.sh | 17 +++++++++++++++
7 files changed, 116 insertions(+)
create mode 100644 lib/query-fp.cc
create mode 100644 lib/query-fp.h
diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst
index adedf5a..223031b 100644
--- a/doc/man7/notmuch-search-terms.rst
+++ b/doc/man7/notmuch-search-terms.rst
@@ -56,6 +56,8 @@ indicate user-supplied values):
- lastmod:<initial-revision>..<final-revision>
+- query:<name>
+
The **from:** prefix is used to match the name or address of the sender
of an email message.
@@ -132,6 +134,11 @@ were added/removed or filenames changed). This is usually used in
conjunction with the **--uuid** argument to **notmuch search**
to find messages that have changed since an earlier query.
+The **query:** prefix allows queries to refer to previously saved
+queries added with **notmuch-config(1)**. Named queries are only
+available if notmuch is built with **Xapian Field Processors** (see
+below).
+
Operators
---------
@@ -385,6 +392,7 @@ notmuch was built against a sufficiently recent version of Xapian by running
Currently the following features require field processor support:
- non-range date queries, e.g. "date:today"
+- named queries e.g. "query:my_special_query"
SEE ALSO
========
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 76b57cb..beb9635 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
$(dir)/index.cc \
$(dir)/message.cc \
$(dir)/query.cc \
+ $(dir)/query-fp.cc \
$(dir)/config.cc \
$(dir)/thread.cc
diff --git a/lib/database-private.h b/lib/database-private.h
index d2990b6..1a78b60 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -185,6 +185,7 @@ struct _notmuch_database {
Xapian::ValueRangeProcessor *date_range_processor;
#if HAVE_XAPIAN_FIELD_PROCESSOR
Xapian::FieldProcessor *date_field_processor;
+ Xapian::FieldProcessor *query_field_processor;
#endif
Xapian::ValueRangeProcessor *last_mod_range_processor;
};
diff --git a/lib/database.cc b/lib/database.cc
index ebe019f..9630000 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -20,6 +20,7 @@
#include "database-private.h"
#include "parse-time-vrp.h"
+#include "query-fp.h"
#include "string-util.h"
#include <iostream>
@@ -1005,6 +1006,8 @@ notmuch_database_open_verbose (const char *path,
* with a .. to the range processor */
notmuch->date_field_processor = new DateFieldProcessor();
notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_processor);
+ notmuch->query_field_processor = new QueryFieldProcessor (*notmuch->query_parser, notmuch);
+ notmuch->query_parser->add_boolean_prefix("query", notmuch->query_field_processor);
#endif
notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
diff --git a/lib/query-fp.cc b/lib/query-fp.cc
new file mode 100644
index 0000000..dab78d2
--- /dev/null
+++ b/lib/query-fp.cc
@@ -0,0 +1,44 @@
+/* query-fp.cc - "query:" field processor glue glue
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * 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: David Bremner <david@tethera.net>
+ */
+
+#include "database-private.h"
+#include "query-fp.h"
+#include <iostream>
+
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+
+Xapian::Query
+QueryFieldProcessor::operator() (const std::string & name)
+{
+ std::string key = "query." + name;
+ char *expansion;
+ notmuch_status_t status;
+
+ status = notmuch_database_get_config (notmuch, key.c_str (), &expansion);
+ if (status) {
+ throw Xapian::QueryParserError ("error looking up key" + name);
+
+ }
+
+ return parser.parse_query (expansion, NOTMUCH_QUERY_PARSER_FLAGS);
+}
+#endif
diff --git a/lib/query-fp.h b/lib/query-fp.h
new file mode 100644
index 0000000..67f8705
--- /dev/null
+++ b/lib/query-fp.h
@@ -0,0 +1,42 @@
+/* query-fp.h - query field processor glue
+ *
+ * This file is part of notmuch.
+ *
+ * Copyright © 2016 David Bremner
+ *
+ * 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: David Bremner <david@tethera.net>
+ */
+
+#ifndef NOTMUCH_QUERY_FP_H
+#define NOTMUCH_QUERY_FP_H
+
+#include <xapian.h>
+#include "notmuch.h"
+
+#if HAVE_XAPIAN_FIELD_PROCESSOR
+class QueryFieldProcessor : public Xapian::FieldProcessor {
+ protected:
+ Xapian::QueryParser &parser;
+ notmuch_database_t *notmuch;
+
+ public:
+ QueryFieldProcessor (Xapian::QueryParser &parser_, notmuch_database_t *notmuch_)
+ : parser(parser_), notmuch(notmuch_) { };
+
+ Xapian::Query operator()(const std::string & str);
+};
+#endif
+#endif /* NOTMUCH_QUERY_FP_H */
diff --git a/test/T600-named-queries.sh b/test/T600-named-queries.sh
index 0922620..f0ae24f 100755
--- a/test/T600-named-queries.sh
+++ b/test/T600-named-queries.sh
@@ -50,4 +50,21 @@ notmuch restore < BEFORE
notmuch dump | grep '^#@' > OUTPUT
test_expect_equal_file QUERIES.BEFORE OUTPUT
+if [ $NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR -eq 1 ]; then
+ test_begin_subtest "search named query"
+ notmuch search query:test > OUTPUT
+ notmuch search $QUERYSTR > EXPECTED
+ test_expect_equal_file EXPECTED OUTPUT
+
+ test_begin_subtest "search named query with other terms"
+ notmuch search query:test and subject:Maildir > OUTPUT
+ notmuch search $QUERYSTR and subject:Maildir > EXPECTED
+ test_expect_equal_file EXPECTED OUTPUT
+
+ test_begin_subtest "search nested named query"
+ notmuch search query:test2 > OUTPUT
+ notmuch search $QUERYSTR2 > EXPECTED
+ test_expect_equal_file EXPECTED OUTPUT
+fi
+
test_done
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Patch v4 12/12] fixup! lib/cli: add library API / CLI for compile time options
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (10 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 11/12] lib: add support for named queries David Bremner
@ 2016-05-08 0:04 ` David Bremner
2016-05-08 8:25 ` v4 of libconfig / single argument date / named query patches Tomi Ollila
12 siblings, 0 replies; 14+ messages in thread
From: David Bremner @ 2016-05-08 0:04 UTC (permalink / raw)
To: notmuch
---
notmuch-config.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/notmuch-config.c b/notmuch-config.c
index e4f47e4..f8636d5 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -903,7 +903,7 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
static
void
-_notmuch_config_list_options () {
+_notmuch_config_list_built_with () {
printf("%scompact=%s\n",
BUILT_WITH_PREFIX,
notmuch_built_with ("compact") ? "true" : "false");
@@ -972,7 +972,7 @@ notmuch_config_command_list (notmuch_config_t *config)
g_strfreev (groups);
- _notmuch_config_list_options ();
+ _notmuch_config_list_built_with ();
return _list_db_config (config);
}
--
2.8.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: v4 of libconfig / single argument date / named query patches
2016-05-08 0:04 v4 of libconfig / single argument date / named query patches David Bremner
` (11 preceding siblings ...)
2016-05-08 0:04 ` [Patch v4 12/12] fixup! lib/cli: add library API / CLI for compile time options David Bremner
@ 2016-05-08 8:25 ` Tomi Ollila
12 siblings, 0 replies; 14+ messages in thread
From: Tomi Ollila @ 2016-05-08 8:25 UTC (permalink / raw)
To: David Bremner, notmuch
On Sun, May 08 2016, David Bremner <david@tethera.net> wrote:
> This obsoletes
>
> id:1462065879-29860-1-git-send-email-david@tethera.net
looks pretty good & tests pass. one problem though, rest are nits:
;; This buffer is for notes you don't want to save, and for Lisp evaluation.
;; If you want to create a file, visit that file with C-x C-f,
;; then enter the text in that file's own buffer.
*** in id:1462665889-17121-2-git-send-email-david@tethera.net
+ -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_PROCESSOR) \\
build gives compile /warning/ there, unfortunately this is not fatal and
therefore can go unnoticed. -Wundef would do the same, complain about
undefined macro but would not be fatal.
then, some more nits:
*** id:1462665889-17121-4-git-send-email-david@tethera.net
+_notmuch_config_list_options () {
*** id:1462665889-17121-8-git-send-email-david@tethera.net
+typedef enum dump_includes {
+ DUMP_INCLUDE_TAGS=1,
+ DUMP_INCLUDE_CONFIG=2,
+} dump_include_t;
print_dump_header () has some empty lines before closing '}'
*** id:1462665889-17121-9-git-send-email-david@tethera.net
+process_config_line(notmuch_database_t *notmuch, const char* line){
+ val_p = strtok_len_c(key_p+key_len, delim, &val_len);
+ int include=0;
*** id:1462665889-17121-12-git-send-email-david@tethera.net
+/* query-fp.cc - "query:" field processor glue glue
id:1462665889-17121-13-git-send-email-david@tethera.net
+_notmuch_config_list_built_with () {
^ permalink raw reply [flat|nested] 14+ messages in thread