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