unsigned long flags;
Xapian::Document doc;
+ Xapian::termcount termpos;
};
#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
talloc_set_destructor (message, _notmuch_message_destructor);
message->doc = doc;
+ message->termpos = 0;
return message;
}
*
* Here, 'talloc owner' is an optional talloc context to which the new
* message will belong. This allows for the caller to not bother
- * calling notmuch_message_destroy on the message, and no that all
- * memory will be reclaimed with 'talloc_owner' is free. The caller
+ * calling notmuch_message_destroy on the message, and know that all
+ * memory will be reclaimed when 'talloc_owner' is freed. The caller
* still can call notmuch_message_destroy when finished with the
* message if desired.
*
return message;
}
+unsigned int
+_notmuch_message_get_doc_id (notmuch_message_t *message)
+{
+ return message->doc_id;
+}
+
const char *
notmuch_message_get_message_id (notmuch_message_t *message)
{
_notmuch_message_add_filename (notmuch_message_t *message,
const char *filename)
{
+ const char *relative, *directory;
notmuch_status_t status;
void *local = talloc_new (message);
char *direntry;
+ if (filename == NULL)
+ INTERNAL_ERROR ("Message filename cannot be NULL.");
+
if (message->filename_list) {
_notmuch_filename_list_destroy (message->filename_list);
message->filename_list = NULL;
}
- if (filename == NULL)
- INTERNAL_ERROR ("Message filename cannot be NULL.");
+ relative = _notmuch_database_relative_path (message->notmuch, filename);
+
+ status = _notmuch_database_split_path (local, relative, &directory, NULL);
+ if (status)
+ return status;
status = _notmuch_database_filename_to_direntry (local,
message->notmuch,
if (status)
return status;
+ /* New file-direntry allows navigating to this message with
+ * notmuch_directory_get_child_files() . */
_notmuch_message_add_term (message, "file-direntry", direntry);
+ /* New terms allow user to search with folder: specification. */
+ _notmuch_message_gen_terms (message, "folder", directory);
+
talloc_free (local);
return NOTMUCH_STATUS_SUCCESS;
}
-/* Change a particular filename for 'message' from 'old_filename' to
- * 'new_filename'
+/* Remove a particular 'filename' from 'message'.
*
* This change will not be reflected in the database until the next
* call to _notmuch_message_sync.
- */
-static notmuch_status_t
-_notmuch_message_rename (notmuch_message_t *message,
- const char *old_filename,
- const char *new_filename)
+ *
+ * Note: This function does not remove a document from the database,
+ * even if the specified filename is the only filename for this
+ * message. For that functionality, see
+ * _notmuch_database_remove_message. */
+notmuch_status_t
+_notmuch_message_remove_filename (notmuch_message_t *message,
+ const char *filename)
{
+ const char *direntry_prefix = _find_prefix ("file-direntry");
+ int direntry_prefix_len = strlen (direntry_prefix);
+ const char *folder_prefix = _find_prefix ("folder");
+ int folder_prefix_len = strlen (folder_prefix);
void *local = talloc_new (message);
char *direntry;
notmuch_private_status_t private_status;
notmuch_status_t status;
+ Xapian::TermIterator i, last;
- status = _notmuch_message_add_filename (message, new_filename);
- if (status)
- return status;
+ if (message->filename_list) {
+ _notmuch_filename_list_destroy (message->filename_list);
+ message->filename_list = NULL;
+ }
status = _notmuch_database_filename_to_direntry (local, message->notmuch,
- old_filename, &direntry);
+ filename, &direntry);
if (status)
return status;
+ /* Unlink this file from its parent directory. */
private_status = _notmuch_message_remove_term (message,
"file-direntry", direntry);
status = COERCE_STATUS (private_status,
"Unexpected error from _notmuch_message_remove_term");
+ /* Re-synchronize "folder:" terms for this message. This requires
+ * first removing all "folder:" terms, then adding back terms for
+ * all remaining filenames of the message. */
+ while (1) {
+ i = message->doc.termlist_begin ();
+ i.skip_to (folder_prefix);
+
+ /* Terminate loop when no terms remain with desired prefix. */
+ if (i == message->doc.termlist_end () ||
+ strncmp ((*i).c_str (), folder_prefix, folder_prefix_len))
+ {
+ break;
+ }
+
+ try {
+ message->doc.remove_term ((*i));
+ } catch (const Xapian::InvalidArgumentError) {
+ /* Ignore failure to remove non-existent term. */
+ }
+ }
+
+ i = message->doc.termlist_begin ();
+ i.skip_to (direntry_prefix);
+
+ for (; i != message->doc.termlist_end (); i++) {
+ unsigned int directory_id;
+ const char *direntry, *directory;
+ char *colon;
+
+ /* Terminate loop at first term without desired prefix. */
+ if (strncmp ((*i).c_str (), direntry_prefix, direntry_prefix_len))
+ break;
+
+ direntry = (*i).c_str ();
+ direntry += direntry_prefix_len;
+
+ directory_id = strtol (direntry, &colon, 10);
+
+ if (colon == NULL || *colon != ':')
+ INTERNAL_ERROR ("malformed direntry");
+
+ directory = _notmuch_database_get_directory_path (local,
+ message->notmuch,
+ directory_id);
+ if (strlen (directory))
+ _notmuch_message_gen_terms (message, "folder", directory);
+ }
+
talloc_free (local);
return status;
return NOTMUCH_PRIVATE_STATUS_NULL_POINTER;
term_gen->set_document (message->doc);
+ term_gen->set_termpos (message->termpos);
if (prefix_name) {
const char *prefix = _find_prefix (prefix_name);
term_gen->index_text (text, 1, prefix);
+ message->termpos = term_gen->get_termpos ();
}
term_gen->index_text (text);
const char *filename;
char *filename_new;
char *to_set, *to_clear;
- notmuch_status_t status;
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
_get_maildir_flag_actions (message, &to_set, &to_clear);
if (err)
continue;
- new_status = _notmuch_message_rename (message,
- filename, filename_new);
+ new_status = _notmuch_message_remove_filename (message,
+ filename);
+ /* Hold on to only the first error. */
+ if (! status && new_status) {
+ status = new_status;
+ continue;
+ }
+
+ new_status = _notmuch_message_add_filename (message,
+ filename_new);
/* Hold on to only the first error. */
if (! status && new_status) {
status = new_status;