lib: Eliminate some redundant includes of xapian.h
[notmuch] / lib / directory.cc
1 /* directory.cc - Results of directory-based searches from a notmuch database
2  *
3  * Copyright © 2009 Carl Worth
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/ .
17  *
18  * Author: Carl Worth <cworth@cworth.org>
19  */
20
21 #include "notmuch-private.h"
22 #include "database-private.h"
23
24 struct _notmuch_filenames {
25     Xapian::TermIterator iterator;
26     Xapian::TermIterator end;
27     int prefix_len;
28     char *filename;
29 };
30
31 /* We end up having to call the destructors explicitly because we had
32  * to use "placement new" in order to initialize C++ objects within a
33  * block that we allocated with talloc. So C++ is making talloc
34  * slightly less simple to use, (we wouldn't need
35  * talloc_set_destructor at all otherwise).
36  */
37 static int
38 _notmuch_filenames_destructor (notmuch_filenames_t *filenames)
39 {
40     filenames->iterator.~TermIterator ();
41     filenames->end.~TermIterator ();
42
43     return 0;
44 }
45
46 /* Create an iterator to iterate over the basenames of files (or
47  * directories) that all share a common parent directory.
48  *
49  * The code here is general enough to be reused for any case of
50  * iterating over the non-prefixed portion of terms sharing a common
51  * prefix.
52  */
53 static notmuch_filenames_t *
54 _notmuch_filenames_create (void *ctx,
55                            notmuch_database_t *notmuch,
56                            const char *prefix)
57 {
58     notmuch_filenames_t *filenames;
59
60     filenames = talloc (ctx, notmuch_filenames_t);
61     if (unlikely (filenames == NULL))
62         return NULL;
63
64     new (&filenames->iterator) Xapian::TermIterator ();
65     new (&filenames->end) Xapian::TermIterator ();
66
67     talloc_set_destructor (filenames, _notmuch_filenames_destructor);
68
69     filenames->iterator = notmuch->xapian_db->allterms_begin (prefix);
70     filenames->end = notmuch->xapian_db->allterms_end (prefix);
71
72     filenames->prefix_len = strlen (prefix);
73
74     filenames->filename = NULL;
75
76     return filenames;
77 }
78
79 notmuch_bool_t
80 notmuch_filenames_valid (notmuch_filenames_t *filenames)
81 {
82     if (filenames == NULL)
83         return NULL;
84
85     return (filenames->iterator != filenames->end);
86 }
87
88 const char *
89 notmuch_filenames_get (notmuch_filenames_t *filenames)
90 {
91     if (filenames == NULL || filenames->iterator == filenames->end)
92         return NULL;
93
94     if (filenames->filename == NULL) {
95         std::string term = *filenames->iterator;
96
97         filenames->filename = talloc_strdup (filenames,
98                                              term.c_str () +
99                                              filenames->prefix_len);
100     }
101
102     return filenames->filename;
103 }
104
105 void
106 notmuch_filenames_move_to_next (notmuch_filenames_t *filenames)
107 {
108     if (filenames == NULL)
109         return;
110
111     if (filenames->filename) {
112         talloc_free (filenames->filename);
113         filenames->filename = NULL;
114     }
115
116     if (filenames->iterator != filenames->end)
117         filenames->iterator++;
118 }
119
120 void
121 notmuch_filenames_destroy (notmuch_filenames_t *filenames)
122 {
123     if (filenames == NULL)
124         return;
125
126     talloc_free (filenames);
127 }
128
129 struct _notmuch_directory {
130     notmuch_database_t *notmuch;
131     Xapian::docid document_id;
132     Xapian::Document doc;
133     time_t mtime;
134 };
135
136 /* We end up having to call the destructor explicitly because we had
137  * to use "placement new" in order to initialize C++ objects within a
138  * block that we allocated with talloc. So C++ is making talloc
139  * slightly less simple to use, (we wouldn't need
140  * talloc_set_destructor at all otherwise).
141  */
142 static int
143 _notmuch_directory_destructor (notmuch_directory_t *directory)
144 {
145     directory->doc.~Document ();
146
147     return 0;
148 }
149
150 static notmuch_private_status_t
151 find_directory_document (notmuch_database_t *notmuch,
152                          const char *db_path,
153                          Xapian::Document *document)
154 {
155     notmuch_private_status_t status;
156     Xapian::docid doc_id;
157
158     status = _notmuch_database_find_unique_doc_id (notmuch, "directory",
159                                                    db_path, &doc_id);
160     if (status) {
161         *document = Xapian::Document ();
162         return status;
163     }
164
165     *document = notmuch->xapian_db->get_document (doc_id);
166     return NOTMUCH_PRIVATE_STATUS_SUCCESS;
167 }
168
169 notmuch_directory_t *
170 _notmuch_directory_create (notmuch_database_t *notmuch,
171                            const char *path,
172                            notmuch_status_t *status_ret)
173 {
174     Xapian::WritableDatabase *db;
175     notmuch_directory_t *directory;
176     notmuch_private_status_t private_status;
177     const char *db_path;
178
179     *status_ret = NOTMUCH_STATUS_SUCCESS;
180
181     path = _notmuch_database_relative_path (notmuch, path);
182
183     if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
184         INTERNAL_ERROR ("Failure to ensure database is writable");
185
186     db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
187
188     directory = talloc (notmuch, notmuch_directory_t);
189     if (unlikely (directory == NULL))
190         return NULL;
191
192     directory->notmuch = notmuch;
193
194     /* "placement new"---not actually allocating memory */
195     new (&directory->doc) Xapian::Document;
196
197     talloc_set_destructor (directory, _notmuch_directory_destructor);
198
199     db_path = _notmuch_database_get_directory_db_path (path);
200
201     try {
202         Xapian::TermIterator i, end;
203
204         private_status = find_directory_document (notmuch, db_path,
205                                                   &directory->doc);
206         directory->document_id = directory->doc.get_docid ();
207
208         if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
209             void *local = talloc_new (directory);
210             const char *parent, *basename;
211             Xapian::docid parent_id;
212             char *term = talloc_asprintf (local, "%s%s",
213                                           _find_prefix ("directory"), db_path);
214             directory->doc.add_term (term, 0);
215
216             directory->doc.set_data (path);
217
218             _notmuch_database_split_path (local, path, &parent, &basename);
219
220             _notmuch_database_find_directory_id (notmuch, parent, &parent_id);
221
222             if (basename) {
223                 term = talloc_asprintf (local, "%s%u:%s",
224                                         _find_prefix ("directory-direntry"),
225                                         parent_id, basename);
226                 directory->doc.add_term (term, 0);
227             }
228
229             directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
230                                       Xapian::sortable_serialise (0));
231
232             directory->document_id = _notmuch_database_generate_doc_id (notmuch);
233             db->replace_document (directory->document_id, directory->doc);
234             talloc_free (local);
235         }
236
237         directory->mtime = Xapian::sortable_unserialise (
238             directory->doc.get_value (NOTMUCH_VALUE_TIMESTAMP));
239     } catch (const Xapian::Error &error) {
240         fprintf (stderr,
241                  "A Xapian exception occurred creating a directory: %s.\n",
242                  error.get_msg().c_str());
243         notmuch->exception_reported = TRUE;
244         notmuch_directory_destroy (directory);
245         *status_ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
246         return NULL;
247     }
248
249     if (db_path != path)
250         free ((char *) db_path);
251
252     return directory;
253 }
254
255 unsigned int
256 _notmuch_directory_get_document_id (notmuch_directory_t *directory)
257 {
258     return directory->document_id;
259 }
260
261 notmuch_status_t
262 notmuch_directory_set_mtime (notmuch_directory_t *directory,
263                              time_t mtime)
264 {
265     notmuch_database_t *notmuch = directory->notmuch;
266     Xapian::WritableDatabase *db;
267     notmuch_status_t status;
268
269     status = _notmuch_database_ensure_writable (notmuch);
270     if (status)
271         return status;
272
273     db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
274
275     try {
276         directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
277                                    Xapian::sortable_serialise (mtime));
278
279         db->replace_document (directory->document_id, directory->doc);
280     } catch (const Xapian::Error &error) {
281         fprintf (stderr,
282                  "A Xapian exception occurred setting directory mtime: %s.\n",
283                  error.get_msg().c_str());
284         notmuch->exception_reported = TRUE;
285         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
286     }
287
288     return NOTMUCH_STATUS_SUCCESS;
289 }
290
291 time_t
292 notmuch_directory_get_mtime (notmuch_directory_t *directory)
293 {
294     return directory->mtime;
295 }
296
297 notmuch_filenames_t *
298 notmuch_directory_get_child_files (notmuch_directory_t *directory)
299 {
300     char *term;
301     notmuch_filenames_t *child_files;
302
303     term = talloc_asprintf (directory, "%s%u:",
304                             _find_prefix ("file-direntry"),
305                             directory->document_id);
306
307     child_files = _notmuch_filenames_create (directory,
308                                              directory->notmuch, term);
309
310     talloc_free (term);
311
312     return child_files;
313 }
314
315 notmuch_filenames_t *
316 notmuch_directory_get_child_directories (notmuch_directory_t *directory)
317 {
318     char *term;
319     notmuch_filenames_t *child_directories;
320
321     term = talloc_asprintf (directory, "%s%u:",
322                             _find_prefix ("directory-direntry"),
323                             directory->document_id);
324
325     child_directories = _notmuch_filenames_create (directory,
326                                                    directory->notmuch, term);
327
328     talloc_free (term);
329
330     return child_directories;
331 }
332
333 void
334 notmuch_directory_destroy (notmuch_directory_t *directory)
335 {
336     talloc_free (directory);
337 }