]> git.notmuchmail.org Git - notmuch/blob - lib/prefix.cc
emacs: Add new option notmuch-search-hide-excluded
[notmuch] / lib / prefix.cc
1 #include "database-private.h"
2 #include "query-fp.h"
3 #include "thread-fp.h"
4 #include "regexp-fields.h"
5 #include "parse-time-vrp.h"
6 #include "sexp-fp.h"
7
8 typedef struct {
9     const char *name;
10     const char *prefix;
11     notmuch_field_flag_t flags;
12 } prefix_t;
13
14 /* With these prefix values we follow the conventions published here:
15  *
16  * https://xapian.org/docs/omega/termprefixes.html
17  *
18  * as much as makes sense. Note that I took some liberty in matching
19  * the reserved prefix values to notmuch concepts, (for example, 'G'
20  * is documented as "newsGroup (or similar entity - e.g. a web forum
21  * name)", for which I think the thread is the closest analogue in
22  * notmuch. This in spite of the fact that we will eventually be
23  * storing mailing-list messages where 'G' for "mailing list name"
24  * might be even a closer analogue. I'm treating the single-character
25  * prefixes preferentially for core notmuch concepts (which will be
26  * nearly universal to all mail messages).
27  */
28
29 static const
30 prefix_t prefix_table[] = {
31     /* name                     term prefix     flags */
32     { "type",                   "T",            NOTMUCH_FIELD_NO_FLAGS },
33     { "reference",              "XREFERENCE",   NOTMUCH_FIELD_NO_FLAGS },
34     { "replyto",                "XREPLYTO",     NOTMUCH_FIELD_NO_FLAGS },
35     { "directory",              "XDIRECTORY",   NOTMUCH_FIELD_NO_FLAGS },
36     { "file-direntry",          "XFDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
37     { "directory-direntry",     "XDDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
38     { "body",                   "",             NOTMUCH_FIELD_EXTERNAL |
39       NOTMUCH_FIELD_PROBABILISTIC },
40     { "thread",                 "G",            NOTMUCH_FIELD_EXTERNAL |
41       NOTMUCH_FIELD_PROCESSOR },
42     { "tag",                    "K",            NOTMUCH_FIELD_EXTERNAL |
43       NOTMUCH_FIELD_PROCESSOR },
44     { "is",                     "K",            NOTMUCH_FIELD_EXTERNAL |
45       NOTMUCH_FIELD_PROCESSOR },
46     { "id",                     "Q",            NOTMUCH_FIELD_EXTERNAL },
47     { "mid",                    "Q",            NOTMUCH_FIELD_EXTERNAL |
48       NOTMUCH_FIELD_PROCESSOR },
49     { "path",                   "P",            NOTMUCH_FIELD_EXTERNAL |
50       NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
51     { "property",               "XPROPERTY",    NOTMUCH_FIELD_EXTERNAL },
52     /*
53      * Unconditionally add ':' to reduce potential ambiguity with
54      * overlapping prefixes and/or terms that start with capital
55      * letters. See Xapian document termprefixes.html for related
56      * discussion.
57      */
58     { "folder",                 "XFOLDER:",     NOTMUCH_FIELD_EXTERNAL |
59       NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
60     { "date",                   NULL,           NOTMUCH_FIELD_EXTERNAL |
61       NOTMUCH_FIELD_PROCESSOR },
62     { "query",                  NULL,           NOTMUCH_FIELD_EXTERNAL |
63       NOTMUCH_FIELD_PROCESSOR },
64     { "sexp",                  NULL,            NOTMUCH_FIELD_EXTERNAL |
65       NOTMUCH_FIELD_PROCESSOR },
66     { "from",                   "XFROM",        NOTMUCH_FIELD_EXTERNAL |
67       NOTMUCH_FIELD_PROBABILISTIC |
68       NOTMUCH_FIELD_PROCESSOR },
69     { "to",                     "XTO",          NOTMUCH_FIELD_EXTERNAL |
70       NOTMUCH_FIELD_PROBABILISTIC },
71     { "attachment",             "XATTACHMENT",  NOTMUCH_FIELD_EXTERNAL |
72       NOTMUCH_FIELD_PROBABILISTIC },
73     { "mimetype",               "XMIMETYPE",    NOTMUCH_FIELD_EXTERNAL |
74       NOTMUCH_FIELD_PROBABILISTIC },
75     { "subject",                "XSUBJECT",     NOTMUCH_FIELD_EXTERNAL |
76       NOTMUCH_FIELD_PROBABILISTIC |
77       NOTMUCH_FIELD_PROCESSOR },
78 };
79
80 static const char *
81 _user_prefix (void *ctx, const char *name)
82 {
83     return talloc_asprintf (ctx, "XU%s:", name);
84 }
85
86 const char *
87 _find_prefix (const char *name)
88 {
89     unsigned int i;
90
91     for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
92         if (strcmp (name, prefix_table[i].name) == 0)
93             return prefix_table[i].prefix;
94     }
95
96     INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
97
98     return "";
99 }
100
101 /* Like find prefix, but include the possibility of user defined
102  * prefixes specific to this database */
103
104 const char *
105 _notmuch_database_prefix (notmuch_database_t *notmuch, const char *name)
106 {
107     unsigned int i;
108
109     /*XXX TODO: reduce code duplication */
110     for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
111         if (strcmp (name, prefix_table[i].name) == 0)
112             return prefix_table[i].prefix;
113     }
114
115     if (notmuch->user_prefix)
116         return _notmuch_string_map_get (notmuch->user_prefix, name);
117
118     return NULL;
119 }
120
121 static void
122 _setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch)
123 {
124     if (prefix->prefix)
125         notmuch->query_parser->add_prefix ("", prefix->prefix);
126     if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC)
127         notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
128     else
129         notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix);
130 }
131
132 static void
133 _setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
134 {
135     if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) {
136         Xapian::FieldProcessor *fp;
137
138         if (STRNCMP_LITERAL (prefix->name, "date") == 0)
139             fp = (new DateFieldProcessor (NOTMUCH_VALUE_TIMESTAMP))->release ();
140         else if (STRNCMP_LITERAL (prefix->name, "query") == 0)
141             fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
142         else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
143             fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
144         else if (STRNCMP_LITERAL (prefix->name, "sexp") == 0)
145             fp = (new SexpFieldProcessor (notmuch))->release ();
146         else
147             fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
148                                             *notmuch->query_parser, notmuch))->release ();
149
150         /* we treat all field-processor fields as boolean in order to get the raw input */
151         if (prefix->prefix)
152             notmuch->query_parser->add_prefix ("", prefix->prefix);
153         notmuch->query_parser->add_boolean_prefix (prefix->name, fp);
154     } else {
155         _setup_query_field_default (prefix, notmuch);
156     }
157 }
158
159 notmuch_status_t
160 _notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch)
161 {
162     for (unsigned int i = 0; i < ARRAY_SIZE (prefix_table); i++) {
163         const prefix_t *prefix = &prefix_table[i];
164         if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
165             _setup_query_field (prefix, notmuch);
166         }
167     }
168     return NOTMUCH_STATUS_SUCCESS;
169 }
170
171 notmuch_status_t
172 _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
173 {
174     notmuch_string_map_iterator_t *list;
175
176     notmuch->user_prefix = _notmuch_string_map_create (notmuch);
177     if (notmuch->user_prefix == NULL)
178         return NOTMUCH_STATUS_OUT_OF_MEMORY;
179
180     notmuch->user_header = _notmuch_string_map_create (notmuch);
181     if (notmuch->user_header == NULL)
182         return NOTMUCH_STATUS_OUT_OF_MEMORY;
183
184     list = _notmuch_string_map_iterator_create (notmuch->config, CONFIG_HEADER_PREFIX, FALSE);
185     if (! list)
186         INTERNAL_ERROR ("unable to read headers from configuration");
187
188     for (; _notmuch_string_map_iterator_valid (list);
189          _notmuch_string_map_iterator_move_to_next (list)) {
190
191         prefix_t query_field;
192
193         const char *key = _notmuch_string_map_iterator_key (list)
194                           + sizeof (CONFIG_HEADER_PREFIX) - 1;
195
196         _notmuch_string_map_append (notmuch->user_prefix,
197                                     key,
198                                     _user_prefix (notmuch, key));
199
200         _notmuch_string_map_append (notmuch->user_header,
201                                     key,
202                                     _notmuch_string_map_iterator_value (list));
203
204         query_field.name = talloc_strdup (notmuch, key);
205         query_field.prefix = _user_prefix (notmuch, key);
206         query_field.flags = NOTMUCH_FIELD_PROBABILISTIC
207                             | NOTMUCH_FIELD_EXTERNAL;
208
209         _setup_query_field_default (&query_field, notmuch);
210     }
211
212     _notmuch_string_map_iterator_destroy (list);
213
214     return NOTMUCH_STATUS_SUCCESS;
215 }