SEXP_FLAG_DO_EXPAND = 1 << 7,
SEXP_FLAG_ORPHAN = 1 << 8,
SEXP_FLAG_RANGE = 1 << 9,
+ SEXP_FLAG_PATHNAME = 1 << 10,
} _sexp_flag_t;
/*
{ "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,
{ "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,
return NOTMUCH_STATUS_SUCCESS;
}
+static notmuch_status_t
+resolve_binding (notmuch_database_t *notmuch, const _sexp_binding_t *env, const char *name,
+ const _sexp_binding_t **out)
+{
+ for (; env; env = env->next) {
+ if (strcmp (name, env->name) == 0) {
+ *out = env;
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+ }
+
+ _notmuch_database_log (notmuch, "undefined parameter '%s'\n", name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+}
+
+static notmuch_status_t
+_sexp_expand_term (notmuch_database_t *notmuch,
+ const _sexp_prefix_t *prefix,
+ const _sexp_binding_t *env,
+ const sexp_t *sx,
+ const char **out)
+{
+ notmuch_status_t status;
+
+ if (! out)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ while (sx->ty == SEXP_VALUE && sx->aty == SEXP_BASIC && sx->val[0] == ',') {
+ const char *name = sx->val + 1;
+ const _sexp_binding_t *binding;
+
+ status = resolve_binding (notmuch, env, name, &binding);
+ if (status)
+ return status;
+
+ sx = binding->sx;
+ env = binding->context;
+ }
+
+ if (sx->ty != SEXP_VALUE) {
+ _notmuch_database_log (notmuch, "'%s' expects single atom as argument\n",
+ prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ *out = sx->val;
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
static notmuch_status_t
_sexp_parse_wildcard (notmuch_database_t *notmuch,
const _sexp_prefix_t *parent,
notmuch_status_t
_sexp_parse_regex (notmuch_database_t *notmuch,
const _sexp_prefix_t *prefix, const _sexp_prefix_t *parent,
- unused(const _sexp_binding_t *env),
- std::string val, Xapian::Query &output)
+ const _sexp_binding_t *env,
+ const sexp_t *term, Xapian::Query &output)
{
if (! parent) {
_notmuch_database_log (notmuch, "illegal '%s' outside field\n",
}
std::string msg; /* ignored */
+ const char *str;
+ notmuch_status_t status;
+
+ status = _sexp_expand_term (notmuch, prefix, env, term, &str);
+ if (status)
+ return status;
return _notmuch_regexp_to_query (notmuch, Xapian::BAD_VALUENO, parent->name,
- val, output, msg);
+ str, output, msg);
}
const _sexp_binding_t *env, const char *name,
Xapian::Query &output)
{
- for (; env; env = env->next) {
- if (strcmp (name, env->name) == 0) {
- return _sexp_to_xapian_query (notmuch, parent, env->context, env->sx,
- output);
- }
- }
- _notmuch_database_log (notmuch, "undefined parameter %s\n", name);
- return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ notmuch_status_t status;
+
+ const _sexp_binding_t *binding;
+
+ status = resolve_binding (notmuch, env, name, &binding);
+ if (status)
+ return status;
+
+ return _sexp_to_xapian_query (notmuch, parent, binding->context, binding->sx,
+ output);
}
static notmuch_status_t
-_sexp_parse_date (notmuch_database_t *notmuch, const sexp_t *sx, Xapian::Query &output)
+_sexp_parse_range (notmuch_database_t *notmuch, const _sexp_prefix_t *prefix,
+ const sexp_t *sx, Xapian::Query &output)
{
- /* empty date matches everything */
+ const char *from, *to;
+ std::string msg;
+
+ /* empty range matches everything */
if (! sx) {
output = Xapian::Query::MatchAll;
return NOTMUCH_STATUS_SUCCESS;
}
- _notmuch_database_log (notmuch, "unimplemented date query\n");
+ 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;
+ if (strcmp (from, "*") == 0)
+ from = "";
+
+ 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 (to, "*") == 0)
+ to = "";
+ }
+
+ 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 {
+ if (EMPTY_STRING (from))
+ from_idx = 0L;
+ else
+ 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 {
+ if (EMPTY_STRING (to))
+ to_idx = LONG_MAX;
+ else
+ 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;
}
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;
}
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
}
- if (strcmp (prefix->name, "date") == 0) {
- return _sexp_parse_date (notmuch, sx->list->next, output);
- }
+ 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);
return _notmuch_query_name_to_query (notmuch, sx->list->next->val, output);
}
- if (prefix->xapian_op == Xapian::Query::OP_WILDCARD)
- return _sexp_parse_wildcard (notmuch, parent, env, sx->list->next->val, output);
+ if (prefix->xapian_op == Xapian::Query::OP_WILDCARD) {
+ const char *str;
+ status = _sexp_expand_term (notmuch, prefix, env, sx->list->next, &str);
+ if (status)
+ return status;
+
+ return _sexp_parse_wildcard (notmuch, parent, env, str, output);
+ }
if (prefix->flags & SEXP_FLAG_DO_REGEX) {
- return _sexp_parse_regex (notmuch, prefix, parent, env, sx->list->next->val, output);
+ return _sexp_parse_regex (notmuch, prefix, parent, env, sx->list->next, output);
}
if (prefix->flags & SEXP_FLAG_DO_EXPAND) {