aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Bremner <david@tethera.net>2022-02-15 20:53:12 -0400
committerDavid Bremner <david@tethera.net>2022-02-15 20:54:56 -0400
commitf1b2ab70c39cacb53c0b3c1d49358260a8d1818d (patch)
tree8b06d207475c256081a10ec5eb1fbd7c959610e0 /lib
parentcf342d7302544532a1f66fd7a1cc42df99fcd228 (diff)
parent7b5921877e748338359a25dae578771f768183af (diff)
Merge tag '0.35' into debian/bullseye-backportsdebian/0.35-1_bpo11+1archive/debian/0.35-1_bpo11+1
notmuch 0.35 release
Diffstat (limited to 'lib')
-rw-r--r--lib/built-with.c2
-rw-r--r--lib/config.cc23
-rw-r--r--lib/database-private.h15
-rw-r--r--lib/database.cc14
-rw-r--r--lib/indexopts.c4
-rw-r--r--lib/notmuch-private.h8
-rw-r--r--lib/notmuch.h17
-rw-r--r--lib/open.cc63
-rw-r--r--lib/parse-sexp.cc97
-rw-r--r--lib/parse-time-vrp.cc51
-rw-r--r--lib/prefix.cc4
-rw-r--r--lib/regexp-fields.cc10
12 files changed, 222 insertions, 86 deletions
diff --git a/lib/built-with.c b/lib/built-with.c
index 89958e12..275e72b8 100644
--- a/lib/built-with.c
+++ b/lib/built-with.c
@@ -32,7 +32,7 @@ notmuch_built_with (const char *name)
return HAVE_XAPIAN_DB_RETRY_LOCK;
} else if (STRNCMP_LITERAL (name, "session_key") == 0) {
return true;
- } else if (STRNCMP_LITERAL (name, "sexpr_query") == 0) {
+ } else if (STRNCMP_LITERAL (name, "sexp_queries") == 0) {
return HAVE_SFSEXP;
} else {
return false;
diff --git a/lib/config.cc b/lib/config.cc
index e502858d..503a0c8b 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -435,11 +435,6 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
for (gchar **keys_p = keys; *keys_p; keys_p++) {
char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *grp, *keys_p);
char *normalized_val;
- val = g_key_file_get_value (file, *grp, *keys_p, NULL);
- if (! val) {
- status = NOTMUCH_STATUS_FILE_ERROR;
- goto DONE;
- }
/* If we opened from a given path, do not overwrite it */
if (strcmp (absolute_key, "database.path") == 0 &&
@@ -447,6 +442,12 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
notmuch->xapian_db)
continue;
+ val = g_key_file_get_string (file, *grp, *keys_p, NULL);
+ if (! val) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
normalized_val = _expand_path (notmuch, absolute_key, val);
_notmuch_string_map_set (notmuch->config, absolute_key, normalized_val);
g_free (val);
@@ -596,6 +597,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key)
return "user.name";
case NOTMUCH_CONFIG_AUTOCOMMIT:
return "database.autocommit";
+ case NOTMUCH_CONFIG_EXTRA_HEADERS:
+ return "show.extra_headers";
default:
return NULL;
}
@@ -643,6 +646,7 @@ _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
return "";
case NOTMUCH_CONFIG_AUTOCOMMIT:
return "8000";
+ case NOTMUCH_CONFIG_EXTRA_HEADERS:
case NOTMUCH_CONFIG_HOOK_DIR:
case NOTMUCH_CONFIG_BACKUP_DIR:
case NOTMUCH_CONFIG_OTHER_EMAIL:
@@ -657,6 +661,10 @@ notmuch_status_t
_notmuch_config_load_defaults (notmuch_database_t *notmuch)
{
notmuch_config_key_t key;
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+ if (notmuch->config == NULL)
+ notmuch->config = _notmuch_string_map_create (notmuch);
for (key = NOTMUCH_CONFIG_FIRST;
key < NOTMUCH_CONFIG_LAST;
@@ -666,11 +674,14 @@ _notmuch_config_load_defaults (notmuch_database_t *notmuch)
val = _notmuch_string_map_get (notmuch->config, key_string);
if (! val) {
+ if (key == NOTMUCH_CONFIG_MAIL_ROOT && (notmuch->params & NOTMUCH_PARAM_SPLIT))
+ status = NOTMUCH_STATUS_NO_MAIL_ROOT;
+
_notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch,
key));
}
}
- return NOTMUCH_STATUS_SUCCESS;
+ return status;
}
const char *
diff --git a/lib/database-private.h b/lib/database-private.h
index 8dd77281..657b1aa1 100644
--- a/lib/database-private.h
+++ b/lib/database-private.h
@@ -160,11 +160,12 @@ operator&= (_notmuch_features &a, _notmuch_features b)
/*
* Configuration options for xapian database fields */
-typedef enum notmuch_field_flags {
+typedef enum {
NOTMUCH_FIELD_NO_FLAGS = 0,
NOTMUCH_FIELD_EXTERNAL = 1 << 0,
NOTMUCH_FIELD_PROBABILISTIC = 1 << 1,
NOTMUCH_FIELD_PROCESSOR = 1 << 2,
+ NOTMUCH_FIELD_STRIP_TRAILING_SLASH = 1 << 3,
} notmuch_field_flag_t;
/*
@@ -191,12 +192,17 @@ operator& (notmuch_field_flag_t a, notmuch_field_flag_t b)
Xapian::QueryParser::FLAG_PURE_NOT)
/*
- * Which parameters were explicit when the database was opened */
+ * explicit and implied parameters to open */
typedef enum {
NOTMUCH_PARAM_NONE = 0,
+ /* database passed explicitely */
NOTMUCH_PARAM_DATABASE = 1 << 0,
+ /* config file passed explicitely */
NOTMUCH_PARAM_CONFIG = 1 << 1,
+ /* profile name passed explicitely */
NOTMUCH_PARAM_PROFILE = 1 << 2,
+ /* split (e.g. XDG) configuration */
+ NOTMUCH_PARAM_SPLIT = 1 << 3,
} notmuch_open_param_t;
/*
@@ -374,5 +380,10 @@ notmuch_status_t
_notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
Xapian::Query &output);
#endif
+
+/* parse-time-vrp.h */
+notmuch_status_t
+_notmuch_date_strings_to_query (Xapian::valueno slot, const std::string &from, const std::string &to,
+ Xapian::Query &output, std::string &msg);
#endif
#endif
diff --git a/lib/database.cc b/lib/database.cc
index 7eb0de79..df83e204 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -311,6 +311,8 @@ notmuch_status_to_string (notmuch_status_t status)
return "Database exists, not recreated";
case NOTMUCH_STATUS_BAD_QUERY_SYNTAX:
return "Syntax error in query";
+ case NOTMUCH_STATUS_NO_MAIL_ROOT:
+ return "No mail root found";
default:
case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
@@ -590,10 +592,12 @@ notmuch_database_compact (const char *path,
notmuch_database_t *notmuch = NULL;
char *message = NULL;
- ret = notmuch_database_open_verbose (path,
- NOTMUCH_DATABASE_MODE_READ_WRITE,
- &notmuch,
- &message);
+ ret = notmuch_database_open_with_config (path,
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "",
+ NULL,
+ &notmuch,
+ &message);
if (ret) {
if (status_cb) status_cb (message, closure);
return ret;
@@ -751,6 +755,8 @@ notmuch_database_destroy (notmuch_database_t *notmuch)
notmuch->date_range_processor = NULL;
delete notmuch->last_mod_range_processor;
notmuch->last_mod_range_processor = NULL;
+ delete notmuch->stemmer;
+ notmuch->stemmer = NULL;
talloc_free (notmuch);
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 4a860858..2ffd1942 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -20,6 +20,10 @@
#include "notmuch-private.h"
+struct _notmuch_indexopts {
+ _notmuch_crypto_t crypto;
+};
+
notmuch_indexopts_t *
notmuch_database_get_default_indexopts (notmuch_database_t *db)
{
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 093c29b1..3cc79bc4 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -121,7 +121,7 @@ typedef enum {
*/
#define NOTMUCH_MESSAGE_ID_MAX (200 - sizeof (NOTMUCH_METADATA_THREAD_ID_PREFIX))
-typedef enum _notmuch_private_status {
+typedef enum {
/* First, copy all the public status values. */
NOTMUCH_PRIVATE_STATUS_SUCCESS = NOTMUCH_STATUS_SUCCESS,
NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY = NOTMUCH_STATUS_OUT_OF_MEMORY,
@@ -173,7 +173,7 @@ typedef enum _notmuch_private_status {
(notmuch_status_t) private_status)
/* Flags shared by various lookup functions. */
-typedef enum _notmuch_find_flags {
+typedef enum {
/* Lookup without creating any documents. This is the default
* behavior. */
NOTMUCH_FIND_LOOKUP = 0,
@@ -711,9 +711,7 @@ _notmuch_thread_create (void *ctx,
/* indexopts.c */
-struct _notmuch_indexopts {
- _notmuch_crypto_t crypto;
-};
+struct _notmuch_indexopts;
#define CONFIG_HEADER_PREFIX "index.header."
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 5c5a024e..2e6ec2af 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -58,7 +58,7 @@ NOTMUCH_BEGIN_DECLS
* version in Makefile.local.
*/
#define LIBNOTMUCH_MAJOR_VERSION 5
-#define LIBNOTMUCH_MINOR_VERSION 5
+#define LIBNOTMUCH_MINOR_VERSION 6
#define LIBNOTMUCH_MICRO_VERSION 0
@@ -112,7 +112,7 @@ typedef int notmuch_bool_t;
* A zero value (NOTMUCH_STATUS_SUCCESS) indicates that the function
* completed without error. Any other value indicates an error.
*/
-typedef enum _notmuch_status {
+typedef enum {
/**
* No error occurred.
*/
@@ -225,6 +225,10 @@ typedef enum _notmuch_status {
*/
NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
/**
+ * No mail root could be deduced from parameters and environment
+ */
+ NOTMUCH_STATUS_NO_MAIL_ROOT,
+ /**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
*/
@@ -323,7 +327,7 @@ typedef enum {
* config_path="" and error_message=NULL
* @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32)
*/
-/* NOTMUCH_DEPRECATED(5, 4) */
+NOTMUCH_DEPRECATED(5, 4)
notmuch_status_t
notmuch_database_open (const char *path,
notmuch_database_mode_t mode,
@@ -335,7 +339,7 @@ notmuch_database_open (const char *path,
* @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32)
*
*/
-/* NOTMUCH_DEPRECATED(5, 4) */
+NOTMUCH_DEPRECATED(5, 4)
notmuch_status_t
notmuch_database_open_verbose (const char *path,
notmuch_database_mode_t mode,
@@ -1686,7 +1690,7 @@ notmuch_message_reindex (notmuch_message_t *message,
/**
* Message flags.
*/
-typedef enum _notmuch_message_flag {
+typedef enum {
NOTMUCH_MESSAGE_FLAG_MATCH,
NOTMUCH_MESSAGE_FLAG_EXCLUDED,
@@ -2532,7 +2536,7 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list);
/**
* Configuration keys known to libnotmuch
*/
-typedef enum _notmuch_config_key {
+typedef enum {
NOTMUCH_CONFIG_FIRST,
NOTMUCH_CONFIG_DATABASE_PATH = NOTMUCH_CONFIG_FIRST,
NOTMUCH_CONFIG_MAIL_ROOT,
@@ -2546,6 +2550,7 @@ typedef enum _notmuch_config_key {
NOTMUCH_CONFIG_OTHER_EMAIL,
NOTMUCH_CONFIG_USER_NAME,
NOTMUCH_CONFIG_AUTOCOMMIT,
+ NOTMUCH_CONFIG_EXTRA_HEADERS,
NOTMUCH_CONFIG_LAST
} notmuch_config_key_t;
diff --git a/lib/open.cc b/lib/open.cc
index a942383b..30cfcf9e 100644
--- a/lib/open.cc
+++ b/lib/open.cc
@@ -19,9 +19,8 @@ notmuch_database_open (const char *path,
char *status_string = NULL;
notmuch_status_t status;
- status = notmuch_database_open_verbose (path, mode, database,
- &status_string);
-
+ status = notmuch_database_open_with_config (path, mode, "", NULL,
+ database, &status_string);
if (status_string) {
fputs (status_string, stderr);
free (status_string);
@@ -187,11 +186,10 @@ _db_dir_exists (const char *database_path, char **message)
}
static notmuch_status_t
-_choose_database_path (void *ctx,
+_choose_database_path (notmuch_database_t *notmuch,
const char *profile,
GKeyFile *key_file,
const char **database_path,
- bool *split,
char **message)
{
if (! *database_path) {
@@ -199,24 +197,24 @@ _choose_database_path (void *ctx,
}
if (! *database_path && key_file) {
- char *path = g_key_file_get_value (key_file, "database", "path", NULL);
+ char *path = g_key_file_get_string (key_file, "database", "path", NULL);
if (path) {
if (path[0] == '/')
- *database_path = talloc_strdup (ctx, path);
+ *database_path = talloc_strdup (notmuch, path);
else
- *database_path = talloc_asprintf (ctx, "%s/%s", getenv ("HOME"), path);
+ *database_path = talloc_asprintf (notmuch, "%s/%s", getenv ("HOME"), path);
g_free (path);
}
}
if (! *database_path) {
notmuch_status_t status;
- *database_path = _xdg_dir (ctx, "XDG_DATA_HOME", ".local/share", profile);
+ *database_path = _xdg_dir (notmuch, "XDG_DATA_HOME", ".local/share", profile);
status = _db_dir_exists (*database_path, message);
if (status) {
*database_path = NULL;
} else {
- *split = true;
+ notmuch->params |= NOTMUCH_PARAM_SPLIT;
}
}
@@ -227,7 +225,7 @@ _choose_database_path (void *ctx,
if (! *database_path) {
notmuch_status_t status;
- *database_path = talloc_asprintf (ctx, "%s/mail", getenv ("HOME"));
+ *database_path = talloc_asprintf (notmuch, "%s/mail", getenv ("HOME"));
status = _db_dir_exists (*database_path, message);
if (status) {
*database_path = NULL;
@@ -511,11 +509,9 @@ notmuch_database_open_with_config (const char *database_path,
char **status_string)
{
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
- void *local = talloc_new (NULL);
notmuch_database_t *notmuch = NULL;
char *message = NULL;
GKeyFile *key_file = NULL;
- bool split = false;
_notmuch_init ();
@@ -531,8 +527,8 @@ notmuch_database_open_with_config (const char *database_path,
goto DONE;
}
- if ((status = _choose_database_path (local, profile, key_file,
- &database_path, &split,
+ if ((status = _choose_database_path (notmuch, profile, key_file,
+ &database_path,
&message)))
goto DONE;
@@ -550,8 +546,6 @@ notmuch_database_open_with_config (const char *database_path,
status = _finish_open (notmuch, profile, mode, key_file, &message);
DONE:
- talloc_free (local);
-
if (key_file)
g_key_file_free (key_file);
@@ -613,9 +607,7 @@ notmuch_database_create_with_config (const char *database_path,
const char *notmuch_path = NULL;
char *message = NULL;
GKeyFile *key_file = NULL;
- void *local = talloc_new (NULL);
int err;
- bool split = false;
_notmuch_init ();
@@ -631,8 +623,8 @@ notmuch_database_create_with_config (const char *database_path,
goto DONE;
}
- if ((status = _choose_database_path (local, profile, key_file,
- &database_path, &split, &message)))
+ if ((status = _choose_database_path (notmuch, profile, key_file,
+ &database_path, &message)))
goto DONE;
status = _db_dir_exists (database_path, &message);
@@ -641,37 +633,34 @@ notmuch_database_create_with_config (const char *database_path,
_set_database_path (notmuch, database_path);
- if (key_file && ! split) {
+ if (key_file && ! (notmuch->params & NOTMUCH_PARAM_SPLIT)) {
char *mail_root = notmuch_canonicalize_file_name (
- g_key_file_get_value (key_file, "database", "mail_root", NULL));
+ g_key_file_get_string (key_file, "database", "mail_root", NULL));
char *db_path = notmuch_canonicalize_file_name (database_path);
- split = (mail_root && (0 != strcmp (mail_root, db_path)));
+ if (mail_root && (0 != strcmp (mail_root, db_path)))
+ notmuch->params |= NOTMUCH_PARAM_SPLIT;
free (mail_root);
free (db_path);
}
- if (split) {
+ if (notmuch->params & NOTMUCH_PARAM_SPLIT) {
notmuch_path = database_path;
} else {
- if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) {
+ if (! (notmuch_path = talloc_asprintf (notmuch, "%s/%s", database_path, ".notmuch"))) {
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
goto DONE;
}
err = mkdir (notmuch_path, 0755);
if (err) {
- if (errno == EEXIST) {
- status = NOTMUCH_STATUS_DATABASE_EXISTS;
- talloc_free (notmuch);
- notmuch = NULL;
- } else {
+ if (errno != EEXIST) {
IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n",
notmuch_path, strerror (errno)));
status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
}
- goto DONE;
}
}
@@ -712,8 +701,6 @@ notmuch_database_create_with_config (const char *database_path,
}
DONE:
- talloc_free (local);
-
if (key_file)
g_key_file_free (key_file);
@@ -813,11 +800,9 @@ notmuch_database_load_config (const char *database_path,
char **status_string)
{
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS, warning = NOTMUCH_STATUS_SUCCESS;
- void *local = talloc_new (NULL);
notmuch_database_t *notmuch = NULL;
char *message = NULL;
GKeyFile *key_file = NULL;
- bool split = false;
_notmuch_init ();
@@ -839,8 +824,8 @@ notmuch_database_load_config (const char *database_path,
goto DONE;
}
- status = _choose_database_path (local, profile, key_file,
- &database_path, &split, &message);
+ status = _choose_database_path (notmuch, profile, key_file,
+ &database_path, &message);
switch (status) {
case NOTMUCH_STATUS_NO_DATABASE:
case NOTMUCH_STATUS_SUCCESS:
@@ -875,8 +860,6 @@ notmuch_database_load_config (const char *database_path,
goto DONE;
DONE:
- talloc_free (local);
-
if (status_string)
*status_string = message;
diff --git a/lib/parse-sexp.cc b/lib/parse-sexp.cc
index 356c32ea..08fd7037 100644
--- a/lib/parse-sexp.cc
+++ b/lib/parse-sexp.cc
@@ -32,6 +32,8 @@ typedef enum {
SEXP_FLAG_EXPAND = 1 << 6,
SEXP_FLAG_DO_EXPAND = 1 << 7,
SEXP_FLAG_ORPHAN = 1 << 8,
+ SEXP_FLAG_RANGE = 1 << 9,
+ SEXP_FLAG_PATHNAME = 1 << 10,
} _sexp_flag_t;
/*
@@ -66,16 +68,21 @@ static _sexp_prefix_t prefixes[] =
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_EXPAND },
{ "body", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
SEXP_FLAG_FIELD },
+ { "date", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
+ SEXP_FLAG_RANGE },
{ "from", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
{ "folder", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
- SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
+ SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND |
+ SEXP_FLAG_PATHNAME },
{ "id", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
{ "infix", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
SEXP_FLAG_SINGLE | SEXP_FLAG_ORPHAN },
{ "is", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
+ { "lastmod", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
+ SEXP_FLAG_RANGE },
{ "matching", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
SEXP_FLAG_DO_EXPAND },
{ "mid", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
@@ -89,7 +96,8 @@ static _sexp_prefix_t prefixes[] =
{ "or", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
SEXP_FLAG_NONE },
{ "path", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
- SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
+ SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX |
+ SEXP_FLAG_PATHNAME },
{ "property", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
{ "query", Xapian::Query::OP_INVALID, Xapian::Query::MatchNothing,
@@ -446,6 +454,79 @@ _sexp_expand_param (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
}
+static notmuch_status_t
+_sexp_parse_range (notmuch_database_t *notmuch, const _sexp_prefix_t *prefix,
+ const sexp_t *sx, Xapian::Query &output)
+{
+ const char *from, *to;
+ std::string msg;
+
+ /* empty range matches everything */
+ if (! sx) {
+ output = Xapian::Query::MatchAll;
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+
+ if (sx->ty == SEXP_LIST) {
+ _notmuch_database_log (notmuch, "expected atom as first argument of '%s'\n", prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ from = sx->val;
+ to = from;
+
+ if (sx->next) {
+ if (sx->next->ty == SEXP_LIST) {
+ _notmuch_database_log (notmuch, "expected atom as second argument of '%s'\n",
+ prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ if (sx->next->next) {
+ _notmuch_database_log (notmuch, "'%s' expects maximum of two arguments\n", prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ to = sx->next->val;
+ }
+
+ if (strcmp (prefix->name, "date") == 0) {
+ notmuch_status_t status;
+ status = _notmuch_date_strings_to_query (NOTMUCH_VALUE_TIMESTAMP, from, to, output, msg);
+ if (status) {
+ if (! msg.empty ())
+ _notmuch_database_log (notmuch, "%s\n", msg.c_str ());
+ }
+ return status;
+ }
+
+ if (strcmp (prefix->name, "lastmod") == 0) {
+ long from_idx, to_idx;
+
+ try {
+ from_idx = std::stol (from);
+ } catch (std::logic_error &e) {
+ _notmuch_database_log (notmuch, "bad 'from' revision: '%s'\n", from);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ try {
+ to_idx = std::stol (to);
+ } catch (std::logic_error &e) {
+ _notmuch_database_log (notmuch, "bad 'to' revision: '%s'\n", to);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ output = Xapian::Query (Xapian::Query::OP_VALUE_RANGE, NOTMUCH_VALUE_LAST_MOD,
+ Xapian::sortable_serialise (from_idx),
+ Xapian::sortable_serialise (to_idx));
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+
+ _notmuch_database_log (notmuch, "unimplimented range prefix: '%s'\n", prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+}
+
/* Here we expect the s-expression to be a proper list, with first
* element defining and operation, or as a special case the empty
* list */
@@ -467,8 +548,13 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
return _sexp_parse_wildcard (notmuch, parent, env, "", output);
}
+ char *atom = sx->val;
+
+ if (parent && parent->flags & SEXP_FLAG_PATHNAME)
+ strip_trailing (atom, '/');
+
if (parent && (parent->flags & SEXP_FLAG_BOOLEAN)) {
- output = Xapian::Query (term_prefix + sx->val);
+ output = Xapian::Query (term_prefix + atom);
return NOTMUCH_STATUS_SUCCESS;
}
@@ -519,7 +605,7 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
for (_sexp_prefix_t *prefix = prefixes; prefix && prefix->name; prefix++) {
if (strcmp (prefix->name, sx->list->val) == 0) {
- if (prefix->flags & SEXP_FLAG_FIELD) {
+ if (prefix->flags & (SEXP_FLAG_FIELD | SEXP_FLAG_RANGE)) {
if (parent) {
_notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
prefix->name, parent->name);
@@ -541,6 +627,9 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
}
+ if (prefix->flags & SEXP_FLAG_RANGE)
+ return _sexp_parse_range (notmuch, prefix, sx->list->next, output);
+
if (strcmp (prefix->name, "infix") == 0) {
return _sexp_parse_infix (notmuch, sx->list->next, output);
}
diff --git a/lib/parse-time-vrp.cc b/lib/parse-time-vrp.cc
index 22bf2ab5..6b07970b 100644
--- a/lib/parse-time-vrp.cc
+++ b/lib/parse-time-vrp.cc
@@ -24,22 +24,28 @@
#include "parse-time-vrp.h"
#include "parse-time-string.h"
-Xapian::Query
-ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string &end)
+notmuch_status_t
+_notmuch_date_strings_to_query (Xapian::valueno slot,
+ const std::string &begin, const std::string &end,
+ Xapian::Query &output, std::string &msg)
{
double from = DBL_MIN, to = DBL_MAX;
time_t parsed_time, now;
std::string str;
/* Use the same 'now' for begin and end. */
- if (time (&now) == (time_t) -1)
- throw Xapian::QueryParserError ("unable to get current time");
+ if (time (&now) == (time_t) -1) {
+ msg = "unable to get current time";
+ return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+ }
if (! begin.empty ()) {
- if (parse_time_string (begin.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_DOWN))
- throw Xapian::QueryParserError ("Didn't understand date specification '" + begin + "'");
- else
- from = (double) parsed_time;
+ if (parse_time_string (begin.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_DOWN)) {
+ msg = "Didn't understand date specification '" + begin + "'";
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ from = (double) parsed_time;
}
if (! end.empty ()) {
@@ -48,15 +54,30 @@ ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string
else
str = end;
- if (parse_time_string (str.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_UP_INCLUSIVE))
- throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'");
- else
- to = (double) parsed_time;
+ if (parse_time_string (str.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_UP_INCLUSIVE)) {
+ msg = "Didn't understand date specification '" + str + "'";
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+ to = (double) parsed_time;
}
- return Xapian::Query (Xapian::Query::OP_VALUE_RANGE, slot,
- Xapian::sortable_serialise (from),
- Xapian::sortable_serialise (to));
+ output = Xapian::Query (Xapian::Query::OP_VALUE_RANGE, slot,
+ Xapian::sortable_serialise (from),
+ Xapian::sortable_serialise (to));
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+Xapian::Query
+ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string &end)
+{
+
+ Xapian::Query output;
+ std::string msg;
+
+ if (_notmuch_date_strings_to_query (slot, begin, end, output, msg))
+ throw Xapian::QueryParserError (msg);
+
+ return output;
}
/* XXX TODO: is throwing an exception the right thing to do here? */
diff --git a/lib/prefix.cc b/lib/prefix.cc
index 0d92bdd7..857c05b9 100644
--- a/lib/prefix.cc
+++ b/lib/prefix.cc
@@ -46,7 +46,7 @@ prefix_t prefix_table[] = {
{ "mid", "Q", NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
{ "path", "P", NOTMUCH_FIELD_EXTERNAL |
- NOTMUCH_FIELD_PROCESSOR },
+ NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
{ "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL },
/*
* Unconditionally add ':' to reduce potential ambiguity with
@@ -55,7 +55,7 @@ prefix_t prefix_table[] = {
* discussion.
*/
{ "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL |
- NOTMUCH_FIELD_PROCESSOR },
+ NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
{ "date", NULL, NOTMUCH_FIELD_EXTERNAL |
NOTMUCH_FIELD_PROCESSOR },
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
diff --git a/lib/regexp-fields.cc b/lib/regexp-fields.cc
index c6d9d94f..7e9d959c 100644
--- a/lib/regexp-fields.cc
+++ b/lib/regexp-fields.cc
@@ -235,7 +235,15 @@ RegexpFieldProcessor::operator() (const std::string & str)
return parser.parse_query (query_str, NOTMUCH_QUERY_PARSER_FLAGS, term_prefix);
} else {
/* Boolean prefix */
- std::string term = term_prefix + str;
+ std::string query_str;
+ std::string term;
+
+ if (str.length () > 1 && str.at (str.size () - 1) == '/')
+ query_str = str.substr (0, str.size () - 1);
+ else
+ query_str = str;
+
+ term = term_prefix + query_str;
return Xapian::Query (term);
}
}