mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-22 19:08:09 +01:00
a1d139de4d
This is analogous to the "infix" prefix provided by the s-expression based query parser.
215 lines
7.6 KiB
C++
215 lines
7.6 KiB
C++
#include "database-private.h"
|
|
#include "query-fp.h"
|
|
#include "thread-fp.h"
|
|
#include "regexp-fields.h"
|
|
#include "parse-time-vrp.h"
|
|
#include "sexp-fp.h"
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const char *prefix;
|
|
notmuch_field_flag_t flags;
|
|
} prefix_t;
|
|
|
|
/* With these prefix values we follow the conventions published here:
|
|
*
|
|
* https://xapian.org/docs/omega/termprefixes.html
|
|
*
|
|
* as much as makes sense. Note that I took some liberty in matching
|
|
* the reserved prefix values to notmuch concepts, (for example, 'G'
|
|
* is documented as "newsGroup (or similar entity - e.g. a web forum
|
|
* name)", for which I think the thread is the closest analogue in
|
|
* notmuch. This in spite of the fact that we will eventually be
|
|
* storing mailing-list messages where 'G' for "mailing list name"
|
|
* might be even a closer analogue. I'm treating the single-character
|
|
* prefixes preferentially for core notmuch concepts (which will be
|
|
* nearly universal to all mail messages).
|
|
*/
|
|
|
|
static const
|
|
prefix_t prefix_table[] = {
|
|
/* name term prefix flags */
|
|
{ "type", "T", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "reference", "XREFERENCE", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "replyto", "XREPLYTO", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "directory", "XDIRECTORY", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "file-direntry", "XFDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "directory-direntry", "XDDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
|
{ "body", "", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROBABILISTIC },
|
|
{ "thread", "G", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "tag", "K", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "is", "K", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "id", "Q", NOTMUCH_FIELD_EXTERNAL },
|
|
{ "mid", "Q", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "path", "P", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
|
|
{ "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:", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
|
|
{ "date", NULL, NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "sexp", NULL, NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
|
|
NOTMUCH_FIELD_PROBABILISTIC |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
{ "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 |
|
|
NOTMUCH_FIELD_PROCESSOR },
|
|
};
|
|
|
|
static const char *
|
|
_user_prefix (void *ctx, const char *name)
|
|
{
|
|
return talloc_asprintf (ctx, "XU%s:", name);
|
|
}
|
|
|
|
const char *
|
|
_find_prefix (const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
|
if (strcmp (name, prefix_table[i].name) == 0)
|
|
return prefix_table[i].prefix;
|
|
}
|
|
|
|
INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
|
|
|
|
return "";
|
|
}
|
|
|
|
/* Like find prefix, but include the possibility of user defined
|
|
* prefixes specific to this database */
|
|
|
|
const char *
|
|
_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
/*XXX TODO: reduce code duplication */
|
|
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
|
if (strcmp (name, prefix_table[i].name) == 0)
|
|
return prefix_table[i].prefix;
|
|
}
|
|
|
|
if (notmuch->user_prefix)
|
|
return _notmuch_string_map_get (notmuch->user_prefix, name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch)
|
|
{
|
|
if (prefix->prefix)
|
|
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
|
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);
|
|
}
|
|
|
|
static void
|
|
_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
|
|
{
|
|
if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) {
|
|
Xapian::FieldProcessor *fp;
|
|
|
|
if (STRNCMP_LITERAL (prefix->name, "date") == 0)
|
|
fp = (new DateFieldProcessor (NOTMUCH_VALUE_TIMESTAMP))->release ();
|
|
else if (STRNCMP_LITERAL (prefix->name, "query") == 0)
|
|
fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
|
else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
|
|
fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
|
else if (STRNCMP_LITERAL (prefix->name, "sexp") == 0)
|
|
fp = (new SexpFieldProcessor (notmuch))->release ();
|
|
else
|
|
fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
|
|
*notmuch->query_parser, notmuch))->release ();
|
|
|
|
/* we treat all field-processor fields as boolean in order to get the raw input */
|
|
if (prefix->prefix)
|
|
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
|
notmuch->query_parser->add_boolean_prefix (prefix->name, fp);
|
|
} else {
|
|
_setup_query_field_default (prefix, notmuch);
|
|
}
|
|
}
|
|
|
|
notmuch_status_t
|
|
_notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch)
|
|
{
|
|
for (unsigned int i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
|
const prefix_t *prefix = &prefix_table[i];
|
|
if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
|
|
_setup_query_field (prefix, notmuch);
|
|
}
|
|
}
|
|
return NOTMUCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
notmuch_status_t
|
|
_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
|
|
{
|
|
notmuch_string_map_iterator_t *list;
|
|
|
|
notmuch->user_prefix = _notmuch_string_map_create (notmuch);
|
|
if (notmuch->user_prefix == NULL)
|
|
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
|
|
|
notmuch->user_header = _notmuch_string_map_create (notmuch);
|
|
if (notmuch->user_header == NULL)
|
|
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
|
|
|
list = _notmuch_string_map_iterator_create (notmuch->config, CONFIG_HEADER_PREFIX, FALSE);
|
|
if (! list)
|
|
INTERNAL_ERROR ("unable to read headers from configuration");
|
|
|
|
for (; _notmuch_string_map_iterator_valid (list);
|
|
_notmuch_string_map_iterator_move_to_next (list)) {
|
|
|
|
prefix_t query_field;
|
|
|
|
const char *key = _notmuch_string_map_iterator_key (list)
|
|
+ sizeof (CONFIG_HEADER_PREFIX) - 1;
|
|
|
|
_notmuch_string_map_append (notmuch->user_prefix,
|
|
key,
|
|
_user_prefix (notmuch, key));
|
|
|
|
_notmuch_string_map_append (notmuch->user_header,
|
|
key,
|
|
_notmuch_string_map_iterator_value (list));
|
|
|
|
query_field.name = talloc_strdup (notmuch, key);
|
|
query_field.prefix = _user_prefix (notmuch, key);
|
|
query_field.flags = NOTMUCH_FIELD_PROBABILISTIC
|
|
| NOTMUCH_FIELD_EXTERNAL;
|
|
|
|
_setup_query_field_default (&query_field, notmuch);
|
|
}
|
|
|
|
_notmuch_string_map_iterator_destroy (list);
|
|
|
|
return NOTMUCH_STATUS_SUCCESS;
|
|
}
|