* library prefix / field processor cleanup
@ 2017-01-28 13:15 David Bremner
2017-01-28 13:15 ` [RFC Patch 1/2] lib: merge internal prefix tables David Bremner
2017-01-28 13:15 ` [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors David Bremner
0 siblings, 2 replies; 5+ messages in thread
From: David Bremner @ 2017-01-28 13:15 UTC (permalink / raw)
To: notmuch
I started trying to generalize the regexp stuff to include
message-ids [1], but didn't get that far. I think both of these patches
are defensible on their own, particularly the second. I'll probably
include something like them in a revised series for regexp searching.
Probably we should use more of the the "release()" style memory
management with Xapian; I didn't follow that up yet as I didn't want
to get _too_ distracted from current goal.
[1]: the complication is that message-ids are boolean fields, while
from and subject are probabilistic. It's not insurmountable, but it
did inspire re-thinking the way query fields are initialized.
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC Patch 1/2] lib: merge internal prefix tables
2017-01-28 13:15 library prefix / field processor cleanup David Bremner
@ 2017-01-28 13:15 ` David Bremner
2017-01-29 10:14 ` Jani Nikula
2017-01-28 13:15 ` [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors David Bremner
1 sibling, 1 reply; 5+ messages in thread
From: David Bremner @ 2017-01-28 13:15 UTC (permalink / raw)
To: notmuch
Replace multiple tables with some flags in a single table. This makes
the code a bit shorter, and it should also make it easier to add
other options to fields, e.g. regexp searching.
---
lib/database-private.h | 7 ++++
lib/database.cc | 86 +++++++++++++++++++++++---------------------------
2 files changed, 46 insertions(+), 47 deletions(-)
diff --git a/lib/database-private.h b/lib/database-private.h
index ccc1e9a1..32357ce0 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -148,6 +148,13 @@ operator&=(_notmuch_features &a, _notmuch_features b)
return a;
}
+/*
+ * Configuration options for xapian database fields */
+typedef enum notmuch_field_flags {
+ NOTMUCH_FIELD_EXTERNAL = 1,
+ NOTMUCH_FIELD_PROBABILISTIC = 2
+} notmuch_field_flag_t;
+
#define NOTMUCH_QUERY_PARSER_FLAGS (Xapian::QueryParser::FLAG_BOOLEAN | \
Xapian::QueryParser::FLAG_PHRASE | \
Xapian::QueryParser::FLAG_LOVEHATE | \
diff --git a/lib/database.cc b/lib/database.cc
index 2d19f20c..b98468a6 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -42,6 +42,7 @@ using namespace std;
typedef struct {
const char *name;
const char *prefix;
+ int flags;
} prefix_t;
#define NOTMUCH_DATABASE_VERSION 3
@@ -247,37 +248,37 @@ typedef struct {
* nearly universal to all mail messages).
*/
-static prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
- { "type", "T" },
- { "reference", "XREFERENCE" },
- { "replyto", "XREPLYTO" },
- { "directory", "XDIRECTORY" },
- { "file-direntry", "XFDIRENTRY" },
- { "directory-direntry", "XDDIRENTRY" },
-};
-
-static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
- { "thread", "G" },
- { "tag", "K" },
- { "is", "K" },
- { "id", "Q" },
- { "path", "P" },
- { "property", "XPROPERTY" },
+static prefix_t PREFIX[] = {
+ /* name term prefix flags */
+ { "type", "T", 0 },
+ { "reference", "XREFERENCE", 0 },
+ { "replyto", "XREPLYTO", 0 },
+ { "directory", "XDIRECTORY", 0 },
+ { "file-direntry", "XFDIRENTRY", 0 },
+ { "directory-direntry", "XDDIRENTRY", 0 },
+ { "thread", "G", NOTMUCH_FIELD_EXTERNAL },
+ { "tag", "K", NOTMUCH_FIELD_EXTERNAL },
+ { "is", "K", NOTMUCH_FIELD_EXTERNAL },
+ { "id", "Q", NOTMUCH_FIELD_EXTERNAL },
+ { "path", "P", NOTMUCH_FIELD_EXTERNAL },
+ { "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL },
/*
* Unconditionally add ':' to reduce potential ambiguity with
* overlapping prefixes and/or terms that start with capital
* letters. See Xapian document termprefixes.html for related
* discussion.
*/
- { "folder", "XFOLDER:" },
-};
-
-static prefix_t PROBABILISTIC_PREFIX[]= {
- { "from", "XFROM" },
- { "to", "XTO" },
- { "attachment", "XATTACHMENT" },
- { "mimetype", "XMIMETYPE"},
- { "subject", "XSUBJECT"},
+ { "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL },
+ { "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROBABILISTIC },
+ { "to", "XTO", NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROBABILISTIC },
+ { "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROBABILISTIC },
+ { "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROBABILISTIC },
+ { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL |
+ NOTMUCH_FIELD_PROBABILISTIC },
};
const char *
@@ -285,19 +286,9 @@ _find_prefix (const char *name)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_INTERNAL); i++) {
- if (strcmp (name, BOOLEAN_PREFIX_INTERNAL[i].name) == 0)
- return BOOLEAN_PREFIX_INTERNAL[i].prefix;
- }
-
- for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
- if (strcmp (name, BOOLEAN_PREFIX_EXTERNAL[i].name) == 0)
- return BOOLEAN_PREFIX_EXTERNAL[i].prefix;
- }
-
- for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
- if (strcmp (name, PROBABILISTIC_PREFIX[i].name) == 0)
- return PROBABILISTIC_PREFIX[i].prefix;
+ for (i = 0; i < ARRAY_SIZE (PREFIX); i++) {
+ if (strcmp (name, PREFIX[i].name) == 0)
+ return PREFIX[i].prefix;
}
INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
@@ -1053,15 +1044,16 @@ notmuch_database_open_verbose (const char *path,
notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
- for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
- prefix_t *prefix = &BOOLEAN_PREFIX_EXTERNAL[i];
- notmuch->query_parser->add_boolean_prefix (prefix->name,
- prefix->prefix);
- }
-
- for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
- prefix_t *prefix = &PROBABILISTIC_PREFIX[i];
- notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
+ for (i = 0; i < ARRAY_SIZE (PREFIX); i++) {
+ prefix_t *prefix = &PREFIX[i];
+ if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
+ if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC) {
+ notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
+ } else {
+ notmuch->query_parser->add_boolean_prefix (prefix->name,
+ prefix->prefix);
+ }
+ }
}
} catch (const Xapian::Error &error) {
IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
--
2.11.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors
2017-01-28 13:15 library prefix / field processor cleanup David Bremner
2017-01-28 13:15 ` [RFC Patch 1/2] lib: merge internal prefix tables David Bremner
@ 2017-01-28 13:15 ` David Bremner
2017-01-29 10:46 ` Jani Nikula
1 sibling, 1 reply; 5+ messages in thread
From: David Bremner @ 2017-01-28 13:15 UTC (permalink / raw)
To: notmuch
It turns out this is exactly what release() is for; Xapian will
deallocate the objects when it's done with them.
---
lib/database-private.h | 4 ----
lib/database.cc | 19 ++++++++-----------
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/lib/database-private.h b/lib/database-private.h
index 32357ce0..3eac3c7e 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -194,10 +194,6 @@ 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;
- Xapian::FieldProcessor *query_field_processor;
-#endif
Xapian::ValueRangeProcessor *last_mod_range_processor;
};
diff --git a/lib/database.cc b/lib/database.cc
index b98468a6..c1563ca7 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -1029,10 +1029,14 @@ notmuch_database_open_verbose (const char *path,
#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);
- notmuch->query_field_processor = new QueryFieldProcessor (*notmuch->query_parser, notmuch);
- notmuch->query_parser->add_boolean_prefix("query", notmuch->query_field_processor);
+ {
+ Xapian::FieldProcessor * date_fp = new DateFieldProcessor();
+ Xapian::FieldProcessor * query_fp =
+ new QueryFieldProcessor (*notmuch->query_parser, notmuch);
+
+ notmuch->query_parser->add_boolean_prefix("date", date_fp->release ());
+ notmuch->query_parser->add_boolean_prefix("query", query_fp->release ());
+ }
#endif
notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
@@ -1125,13 +1129,6 @@ notmuch_database_close (notmuch_database_t *notmuch)
delete notmuch->last_mod_range_processor;
notmuch->last_mod_range_processor = NULL;
-#if HAVE_XAPIAN_FIELD_PROCESSOR
- delete notmuch->date_field_processor;
- notmuch->date_field_processor = NULL;
- delete notmuch->query_field_processor;
- notmuch->query_field_processor = NULL;
-#endif
-
return status;
}
--
2.11.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC Patch 1/2] lib: merge internal prefix tables
2017-01-28 13:15 ` [RFC Patch 1/2] lib: merge internal prefix tables David Bremner
@ 2017-01-29 10:14 ` Jani Nikula
0 siblings, 0 replies; 5+ messages in thread
From: Jani Nikula @ 2017-01-29 10:14 UTC (permalink / raw)
To: David Bremner, notmuch
On Sat, 28 Jan 2017, David Bremner <david@tethera.net> wrote:
> Replace multiple tables with some flags in a single table. This makes
> the code a bit shorter, and it should also make it easier to add
> other options to fields, e.g. regexp searching.
> ---
> lib/database-private.h | 7 ++++
> lib/database.cc | 86 +++++++++++++++++++++++---------------------------
> 2 files changed, 46 insertions(+), 47 deletions(-)
>
> diff --git a/lib/database-private.h b/lib/database-private.h
> index ccc1e9a1..32357ce0 100644
> --- a/lib/database-private.h
> +++ b/lib/database-private.h
> @@ -148,6 +148,13 @@ operator&=(_notmuch_features &a, _notmuch_features b)
> return a;
> }
>
> +/*
> + * Configuration options for xapian database fields */
> +typedef enum notmuch_field_flags {
> + NOTMUCH_FIELD_EXTERNAL = 1,
> + NOTMUCH_FIELD_PROBABILISTIC = 2
Since these are used as bit flags, I think it would more clear if they
were defined as (1 << 0) and (1 << 1).
I'm not sure if the enum buys us anything over defining them as macros,
but I could go either way.
> +} notmuch_field_flag_t;
> +
> #define NOTMUCH_QUERY_PARSER_FLAGS (Xapian::QueryParser::FLAG_BOOLEAN | \
> Xapian::QueryParser::FLAG_PHRASE | \
> Xapian::QueryParser::FLAG_LOVEHATE | \
> diff --git a/lib/database.cc b/lib/database.cc
> index 2d19f20c..b98468a6 100644
> --- a/lib/database.cc
> +++ b/lib/database.cc
> @@ -42,6 +42,7 @@ using namespace std;
> typedef struct {
> const char *name;
> const char *prefix;
> + int flags;
> } prefix_t;
>
> #define NOTMUCH_DATABASE_VERSION 3
> @@ -247,37 +248,37 @@ typedef struct {
> * nearly universal to all mail messages).
> */
>
> -static prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
> - { "type", "T" },
> - { "reference", "XREFERENCE" },
> - { "replyto", "XREPLYTO" },
> - { "directory", "XDIRECTORY" },
> - { "file-direntry", "XFDIRENTRY" },
> - { "directory-direntry", "XDDIRENTRY" },
> -};
> -
> -static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
> - { "thread", "G" },
> - { "tag", "K" },
> - { "is", "K" },
> - { "id", "Q" },
> - { "path", "P" },
> - { "property", "XPROPERTY" },
> +static prefix_t PREFIX[] = {
Not really specific to this patch, but while at it this could be made
const, and perhaps renamed lower case. The upper case names always
seemed a bit odd. Can be left for follow-up too.
Otherwise, LGTM, a nice cleanup.
BR,
Jani.
> + /* name term prefix flags */
> + { "type", "T", 0 },
> + { "reference", "XREFERENCE", 0 },
> + { "replyto", "XREPLYTO", 0 },
> + { "directory", "XDIRECTORY", 0 },
> + { "file-direntry", "XFDIRENTRY", 0 },
> + { "directory-direntry", "XDDIRENTRY", 0 },
> + { "thread", "G", NOTMUCH_FIELD_EXTERNAL },
> + { "tag", "K", NOTMUCH_FIELD_EXTERNAL },
> + { "is", "K", NOTMUCH_FIELD_EXTERNAL },
> + { "id", "Q", NOTMUCH_FIELD_EXTERNAL },
> + { "path", "P", NOTMUCH_FIELD_EXTERNAL },
> + { "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL },
> /*
> * Unconditionally add ':' to reduce potential ambiguity with
> * overlapping prefixes and/or terms that start with capital
> * letters. See Xapian document termprefixes.html for related
> * discussion.
> */
> - { "folder", "XFOLDER:" },
> -};
> -
> -static prefix_t PROBABILISTIC_PREFIX[]= {
> - { "from", "XFROM" },
> - { "to", "XTO" },
> - { "attachment", "XATTACHMENT" },
> - { "mimetype", "XMIMETYPE"},
> - { "subject", "XSUBJECT"},
> + { "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL },
> + { "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
> + NOTMUCH_FIELD_PROBABILISTIC },
> + { "to", "XTO", NOTMUCH_FIELD_EXTERNAL |
> + NOTMUCH_FIELD_PROBABILISTIC },
> + { "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL |
> + NOTMUCH_FIELD_PROBABILISTIC },
> + { "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL |
> + NOTMUCH_FIELD_PROBABILISTIC },
> + { "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL |
> + NOTMUCH_FIELD_PROBABILISTIC },
> };
>
> const char *
> @@ -285,19 +286,9 @@ _find_prefix (const char *name)
> {
> unsigned int i;
>
> - for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_INTERNAL); i++) {
> - if (strcmp (name, BOOLEAN_PREFIX_INTERNAL[i].name) == 0)
> - return BOOLEAN_PREFIX_INTERNAL[i].prefix;
> - }
> -
> - for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
> - if (strcmp (name, BOOLEAN_PREFIX_EXTERNAL[i].name) == 0)
> - return BOOLEAN_PREFIX_EXTERNAL[i].prefix;
> - }
> -
> - for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
> - if (strcmp (name, PROBABILISTIC_PREFIX[i].name) == 0)
> - return PROBABILISTIC_PREFIX[i].prefix;
> + for (i = 0; i < ARRAY_SIZE (PREFIX); i++) {
> + if (strcmp (name, PREFIX[i].name) == 0)
> + return PREFIX[i].prefix;
> }
>
> INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
> @@ -1053,15 +1044,16 @@ notmuch_database_open_verbose (const char *path,
> notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
> notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
>
> - for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX_EXTERNAL); i++) {
> - prefix_t *prefix = &BOOLEAN_PREFIX_EXTERNAL[i];
> - notmuch->query_parser->add_boolean_prefix (prefix->name,
> - prefix->prefix);
> - }
> -
> - for (i = 0; i < ARRAY_SIZE (PROBABILISTIC_PREFIX); i++) {
> - prefix_t *prefix = &PROBABILISTIC_PREFIX[i];
> - notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
> + for (i = 0; i < ARRAY_SIZE (PREFIX); i++) {
> + prefix_t *prefix = &PREFIX[i];
> + if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
> + if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC) {
> + notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
> + } else {
> + notmuch->query_parser->add_boolean_prefix (prefix->name,
> + prefix->prefix);
> + }
> + }
> }
> } catch (const Xapian::Error &error) {
> IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors
2017-01-28 13:15 ` [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors David Bremner
@ 2017-01-29 10:46 ` Jani Nikula
0 siblings, 0 replies; 5+ messages in thread
From: Jani Nikula @ 2017-01-29 10:46 UTC (permalink / raw)
To: David Bremner, notmuch
On Sat, 28 Jan 2017, David Bremner <david@tethera.net> wrote:
> It turns out this is exactly what release() is for; Xapian will
> deallocate the objects when it's done with them.
release() fares 2 on Rusty scale [1][2]... after digging through source,
LGTM.
BR,
Jani.
[1] http://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html
[2] http://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
> ---
> lib/database-private.h | 4 ----
> lib/database.cc | 19 ++++++++-----------
> 2 files changed, 8 insertions(+), 15 deletions(-)
>
> diff --git a/lib/database-private.h b/lib/database-private.h
> index 32357ce0..3eac3c7e 100644
> --- a/lib/database-private.h
> +++ b/lib/database-private.h
> @@ -194,10 +194,6 @@ 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;
> - Xapian::FieldProcessor *query_field_processor;
> -#endif
> Xapian::ValueRangeProcessor *last_mod_range_processor;
> };
>
> diff --git a/lib/database.cc b/lib/database.cc
> index b98468a6..c1563ca7 100644
> --- a/lib/database.cc
> +++ b/lib/database.cc
> @@ -1029,10 +1029,14 @@ notmuch_database_open_verbose (const char *path,
> #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);
> - notmuch->query_field_processor = new QueryFieldProcessor (*notmuch->query_parser, notmuch);
> - notmuch->query_parser->add_boolean_prefix("query", notmuch->query_field_processor);
> + {
> + Xapian::FieldProcessor * date_fp = new DateFieldProcessor();
> + Xapian::FieldProcessor * query_fp =
> + new QueryFieldProcessor (*notmuch->query_parser, notmuch);
> +
> + notmuch->query_parser->add_boolean_prefix("date", date_fp->release ());
> + notmuch->query_parser->add_boolean_prefix("query", query_fp->release ());
> + }
> #endif
> notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
>
> @@ -1125,13 +1129,6 @@ notmuch_database_close (notmuch_database_t *notmuch)
> delete notmuch->last_mod_range_processor;
> notmuch->last_mod_range_processor = NULL;
>
> -#if HAVE_XAPIAN_FIELD_PROCESSOR
> - delete notmuch->date_field_processor;
> - notmuch->date_field_processor = NULL;
> - delete notmuch->query_field_processor;
> - notmuch->query_field_processor = NULL;
> -#endif
> -
> return status;
> }
>
> --
> 2.11.0
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2017-01-29 10:46 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-28 13:15 library prefix / field processor cleanup David Bremner
2017-01-28 13:15 ` [RFC Patch 1/2] lib: merge internal prefix tables David Bremner
2017-01-29 10:14 ` Jani Nikula
2017-01-28 13:15 ` [RFC Patch 2/2] lib: Let Xapian manage the memory for FieldProcessors David Bremner
2017-01-29 10:46 ` Jani Nikula
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).