]> git.notmuchmail.org Git - notmuch/blob - lib/directory.cc
lib: Add new implementation of notmuch_filenames_t
[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 /* Create an iterator to iterate over the basenames of files (or
25  * directories) that all share a common parent directory.
26  *
27  * The code here is general enough to be reused for any case of
28  * iterating over the non-prefixed portion of terms sharing a common
29  * prefix.
30  */
31 static notmuch_filenames_t *
32 _create_filenames_for_terms_with_prefix (void *ctx,
33                                          notmuch_database_t *notmuch,
34                                          const char *prefix)
35 {
36     notmuch_filenames_t *filenames;
37     Xapian::TermIterator i, end;
38     int prefix_len = strlen (prefix);
39
40     filenames = _notmuch_filenames_create (ctx);
41     if (unlikely (filenames == NULL))
42         return NULL;
43
44     end = notmuch->xapian_db->allterms_end (prefix);
45
46     for (i = notmuch->xapian_db->allterms_begin (prefix); i != end; i++)
47     {
48         std::string term = *i;
49
50         _notmuch_filenames_add_filename (filenames, term.c_str () +
51                                          prefix_len);
52     }
53
54     _notmuch_filenames_move_to_first (filenames);
55
56     return filenames;
57 }
58
59 struct _notmuch_directory {
60     notmuch_database_t *notmuch;
61     Xapian::docid document_id;
62     Xapian::Document doc;
63     time_t mtime;
64 };
65
66 /* We end up having to call the destructor explicitly because we had
67  * to use "placement new" in order to initialize C++ objects within a
68  * block that we allocated with talloc. So C++ is making talloc
69  * slightly less simple to use, (we wouldn't need
70  * talloc_set_destructor at all otherwise).
71  */
72 static int
73 _notmuch_directory_destructor (notmuch_directory_t *directory)
74 {
75     directory->doc.~Document ();
76
77     return 0;
78 }
79
80 static notmuch_private_status_t
81 find_directory_document (notmuch_database_t *notmuch,
82                          const char *db_path,
83                          Xapian::Document *document)
84 {
85     notmuch_private_status_t status;
86     Xapian::docid doc_id;
87
88     status = _notmuch_database_find_unique_doc_id (notmuch, "directory",
89                                                    db_path, &doc_id);
90     if (status) {
91         *document = Xapian::Document ();
92         return status;
93     }
94
95     *document = notmuch->xapian_db->get_document (doc_id);
96     return NOTMUCH_PRIVATE_STATUS_SUCCESS;
97 }
98
99 notmuch_directory_t *
100 _notmuch_directory_create (notmuch_database_t *notmuch,
101                            const char *path,
102                            notmuch_status_t *status_ret)
103 {
104     Xapian::WritableDatabase *db;
105     notmuch_directory_t *directory;
106     notmuch_private_status_t private_status;
107     const char *db_path;
108
109     *status_ret = NOTMUCH_STATUS_SUCCESS;
110
111     path = _notmuch_database_relative_path (notmuch, path);
112
113     if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
114         INTERNAL_ERROR ("Failure to ensure database is writable");
115
116     db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
117
118     directory = talloc (notmuch, notmuch_directory_t);
119     if (unlikely (directory == NULL))
120         return NULL;
121
122     directory->notmuch = notmuch;
123
124     /* "placement new"---not actually allocating memory */
125     new (&directory->doc) Xapian::Document;
126
127     talloc_set_destructor (directory, _notmuch_directory_destructor);
128
129     db_path = _notmuch_database_get_directory_db_path (path);
130
131     try {
132         Xapian::TermIterator i, end;
133
134         private_status = find_directory_document (notmuch, db_path,
135                                                   &directory->doc);
136         directory->document_id = directory->doc.get_docid ();
137
138         if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
139             void *local = talloc_new (directory);
140             const char *parent, *basename;
141             Xapian::docid parent_id;
142             char *term = talloc_asprintf (local, "%s%s",
143                                           _find_prefix ("directory"), db_path);
144             directory->doc.add_term (term, 0);
145
146             directory->doc.set_data (path);
147
148             _notmuch_database_split_path (local, path, &parent, &basename);
149
150             _notmuch_database_find_directory_id (notmuch, parent, &parent_id);
151
152             if (basename) {
153                 term = talloc_asprintf (local, "%s%u:%s",
154                                         _find_prefix ("directory-direntry"),
155                                         parent_id, basename);
156                 directory->doc.add_term (term, 0);
157             }
158
159             directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
160                                       Xapian::sortable_serialise (0));
161
162             directory->document_id = _notmuch_database_generate_doc_id (notmuch);
163             db->replace_document (directory->document_id, directory->doc);
164             talloc_free (local);
165         }
166
167         directory->mtime = Xapian::sortable_unserialise (
168             directory->doc.get_value (NOTMUCH_VALUE_TIMESTAMP));
169     } catch (const Xapian::Error &error) {
170         fprintf (stderr,
171                  "A Xapian exception occurred creating a directory: %s.\n",
172                  error.get_msg().c_str());
173         notmuch->exception_reported = TRUE;
174         notmuch_directory_destroy (directory);
175         *status_ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
176         return NULL;
177     }
178
179     if (db_path != path)
180         free ((char *) db_path);
181
182     return directory;
183 }
184
185 unsigned int
186 _notmuch_directory_get_document_id (notmuch_directory_t *directory)
187 {
188     return directory->document_id;
189 }
190
191 notmuch_status_t
192 notmuch_directory_set_mtime (notmuch_directory_t *directory,
193                              time_t mtime)
194 {
195     notmuch_database_t *notmuch = directory->notmuch;
196     Xapian::WritableDatabase *db;
197     notmuch_status_t status;
198
199     status = _notmuch_database_ensure_writable (notmuch);
200     if (status)
201         return status;
202
203     db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
204
205     try {
206         directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
207                                    Xapian::sortable_serialise (mtime));
208
209         db->replace_document (directory->document_id, directory->doc);
210     } catch (const Xapian::Error &error) {
211         fprintf (stderr,
212                  "A Xapian exception occurred setting directory mtime: %s.\n",
213                  error.get_msg().c_str());
214         notmuch->exception_reported = TRUE;
215         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
216     }
217
218     return NOTMUCH_STATUS_SUCCESS;
219 }
220
221 time_t
222 notmuch_directory_get_mtime (notmuch_directory_t *directory)
223 {
224     return directory->mtime;
225 }
226
227 notmuch_filenames_t *
228 notmuch_directory_get_child_files (notmuch_directory_t *directory)
229 {
230     char *term;
231     notmuch_filenames_t *child_files;
232
233     term = talloc_asprintf (directory, "%s%u:",
234                             _find_prefix ("file-direntry"),
235                             directory->document_id);
236
237     child_files = _create_filenames_for_terms_with_prefix (directory,
238                                                            directory->notmuch,
239                                                            term);
240
241     talloc_free (term);
242
243     return child_files;
244 }
245
246 notmuch_filenames_t *
247 notmuch_directory_get_child_directories (notmuch_directory_t *directory)
248 {
249     char *term;
250     notmuch_filenames_t *child_directories;
251
252     term = talloc_asprintf (directory, "%s%u:",
253                             _find_prefix ("directory-direntry"),
254                             directory->document_id);
255
256     child_directories = _create_filenames_for_terms_with_prefix (directory,
257                                                  directory->notmuch, term);
258
259     talloc_free (term);
260
261     return child_directories;
262 }
263
264 void
265 notmuch_directory_destroy (notmuch_directory_t *directory)
266 {
267     talloc_free (directory);
268 }