1 /* directory.cc - Results of directory-based searches from a notmuch database
3 * Copyright © 2009 Carl Worth
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.
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.
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/ .
18 * Author: Carl Worth <cworth@cworth.org>
21 #include "notmuch-private.h"
22 #include "database-private.h"
24 struct _notmuch_filenames {
25 Xapian::TermIterator iterator;
26 Xapian::TermIterator end;
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).
38 _notmuch_filenames_destructor (notmuch_filenames_t *filenames)
40 filenames->iterator.~TermIterator ();
41 filenames->end.~TermIterator ();
46 /* Create an iterator to iterate over the basenames of files (or
47 * directories) that all share a common parent directory.
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
53 static notmuch_filenames_t *
54 _notmuch_filenames_create (void *ctx,
55 notmuch_database_t *notmuch,
58 notmuch_filenames_t *filenames;
60 filenames = talloc (ctx, notmuch_filenames_t);
61 if (unlikely (filenames == NULL))
64 new (&filenames->iterator) Xapian::TermIterator ();
65 new (&filenames->end) Xapian::TermIterator ();
67 talloc_set_destructor (filenames, _notmuch_filenames_destructor);
69 filenames->iterator = notmuch->xapian_db->allterms_begin (prefix);
70 filenames->end = notmuch->xapian_db->allterms_end (prefix);
72 filenames->prefix_len = strlen (prefix);
74 filenames->filename = NULL;
80 notmuch_filenames_valid (notmuch_filenames_t *filenames)
82 if (filenames == NULL)
85 return (filenames->iterator != filenames->end);
89 notmuch_filenames_get (notmuch_filenames_t *filenames)
91 if (filenames == NULL || filenames->iterator == filenames->end)
94 if (filenames->filename == NULL) {
95 std::string term = *filenames->iterator;
97 filenames->filename = talloc_strdup (filenames,
99 filenames->prefix_len);
102 return filenames->filename;
106 notmuch_filenames_move_to_next (notmuch_filenames_t *filenames)
108 if (filenames == NULL)
111 if (filenames->filename) {
112 talloc_free (filenames->filename);
113 filenames->filename = NULL;
116 if (filenames->iterator != filenames->end)
117 filenames->iterator++;
121 notmuch_filenames_destroy (notmuch_filenames_t *filenames)
123 if (filenames == NULL)
126 talloc_free (filenames);
129 struct _notmuch_directory {
130 notmuch_database_t *notmuch;
131 Xapian::docid document_id;
132 Xapian::Document doc;
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).
143 _notmuch_directory_destructor (notmuch_directory_t *directory)
145 directory->doc.~Document ();
150 static notmuch_private_status_t
151 find_directory_document (notmuch_database_t *notmuch,
153 Xapian::Document *document)
155 notmuch_private_status_t status;
156 Xapian::docid doc_id;
158 status = _notmuch_database_find_unique_doc_id (notmuch, "directory",
161 *document = Xapian::Document ();
165 *document = notmuch->xapian_db->get_document (doc_id);
166 return NOTMUCH_PRIVATE_STATUS_SUCCESS;
169 notmuch_directory_t *
170 _notmuch_directory_create (notmuch_database_t *notmuch,
172 notmuch_status_t *status_ret)
174 Xapian::WritableDatabase *db;
175 notmuch_directory_t *directory;
176 notmuch_private_status_t private_status;
179 *status_ret = NOTMUCH_STATUS_SUCCESS;
181 path = _notmuch_database_relative_path (notmuch, path);
183 if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
184 INTERNAL_ERROR ("Failure to ensure database is writable");
186 db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
188 directory = talloc (notmuch, notmuch_directory_t);
189 if (unlikely (directory == NULL))
192 directory->notmuch = notmuch;
194 /* "placement new"---not actually allocating memory */
195 new (&directory->doc) Xapian::Document;
197 talloc_set_destructor (directory, _notmuch_directory_destructor);
199 db_path = _notmuch_database_get_directory_db_path (path);
202 Xapian::TermIterator i, end;
204 private_status = find_directory_document (notmuch, db_path,
206 directory->document_id = directory->doc.get_docid ();
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);
216 directory->doc.set_data (path);
218 _notmuch_database_split_path (local, path, &parent, &basename);
220 _notmuch_database_find_directory_id (notmuch, parent, &parent_id);
223 term = talloc_asprintf (local, "%s%u:%s",
224 _find_prefix ("directory-direntry"),
225 parent_id, basename);
226 directory->doc.add_term (term, 0);
229 directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
230 Xapian::sortable_serialise (0));
232 directory->document_id = _notmuch_database_generate_doc_id (notmuch);
233 db->replace_document (directory->document_id, directory->doc);
237 directory->mtime = Xapian::sortable_unserialise (
238 directory->doc.get_value (NOTMUCH_VALUE_TIMESTAMP));
239 } catch (const Xapian::Error &error) {
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;
250 free ((char *) db_path);
256 _notmuch_directory_get_document_id (notmuch_directory_t *directory)
258 return directory->document_id;
262 notmuch_directory_set_mtime (notmuch_directory_t *directory,
265 notmuch_database_t *notmuch = directory->notmuch;
266 Xapian::WritableDatabase *db;
267 notmuch_status_t status;
269 status = _notmuch_database_ensure_writable (notmuch);
273 db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
276 directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
277 Xapian::sortable_serialise (mtime));
279 db->replace_document (directory->document_id, directory->doc);
280 } catch (const Xapian::Error &error) {
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;
288 return NOTMUCH_STATUS_SUCCESS;
292 notmuch_directory_get_mtime (notmuch_directory_t *directory)
294 return directory->mtime;
297 notmuch_filenames_t *
298 notmuch_directory_get_child_files (notmuch_directory_t *directory)
301 notmuch_filenames_t *child_files;
303 term = talloc_asprintf (directory, "%s%u:",
304 _find_prefix ("file-direntry"),
305 directory->document_id);
307 child_files = _notmuch_filenames_create (directory,
308 directory->notmuch, term);
315 notmuch_filenames_t *
316 notmuch_directory_get_child_directories (notmuch_directory_t *directory)
319 notmuch_filenames_t *child_directories;
321 term = talloc_asprintf (directory, "%s%u:",
322 _find_prefix ("directory-direntry"),
323 directory->document_id);
325 child_directories = _notmuch_filenames_create (directory,
326 directory->notmuch, term);
330 return child_directories;
334 notmuch_directory_destroy (notmuch_directory_t *directory)
336 talloc_free (directory);