mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-25 12:28:09 +01:00
Merge branch 'release'
merge 0.13.1 bugfix patches back to master, fixes for emacs reply and spurious directory document creation.
This commit is contained in:
commit
6d44c5af65
10 changed files with 120 additions and 103 deletions
45
NEWS
45
NEWS
|
@ -1,24 +1,17 @@
|
||||||
Notmuch 0.13 (2012-xx-xx)
|
Notmuch 0.13 (2012-05-15)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
Command-Line Interface
|
Command-Line Interface
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Reply to sender
|
|
||||||
|
|
||||||
"notmuch reply" has gained the ability to create a reply template
|
|
||||||
for replying just to the sender of the message, in addition to reply
|
|
||||||
to all. The feature is available through the new command line option
|
|
||||||
--reply-to=(all|sender).
|
|
||||||
|
|
||||||
JSON reply format
|
JSON reply format
|
||||||
|
|
||||||
"notmuch reply" can now produce JSON output that contains the headers
|
`notmuch reply` can now produce JSON output that contains the headers
|
||||||
for a reply message and full information about the original message
|
for a reply message and full information about the original message
|
||||||
begin replied to. This allows MUAs to create replies intelligently.
|
begin replied to. This allows MUAs to create replies intelligently.
|
||||||
For example, an MUA that can parse HTML might quote HTML parts.
|
For example, an MUA that can parse HTML might quote HTML parts.
|
||||||
|
|
||||||
Calling notmuch reply with --format=json imposes the restriction that
|
Calling notmuch reply with `--format=json` imposes the restriction that
|
||||||
only a single message is returned by the search, as replying to
|
only a single message is returned by the search, as replying to
|
||||||
multiple messages does not have a well-defined behavior. The default
|
multiple messages does not have a well-defined behavior. The default
|
||||||
retains its current behavior for multiple message replies.
|
retains its current behavior for multiple message replies.
|
||||||
|
@ -26,24 +19,24 @@ JSON reply format
|
||||||
Tag exclusion
|
Tag exclusion
|
||||||
|
|
||||||
Tags can be automatically excluded from search results by adding them
|
Tags can be automatically excluded from search results by adding them
|
||||||
to the new 'search.exclude_tags' option in the Notmuch config file.
|
to the new `search.exclude_tags` option in the Notmuch config file.
|
||||||
|
|
||||||
This behaviour can be overridden by explicitly including an excluded
|
This behaviour can be overridden by explicitly including an excluded
|
||||||
tag in your query, for example:
|
tag in your query, for example:
|
||||||
|
|
||||||
notmuch search $your_query and tag:$excluded_tag
|
notmuch search $your_query and tag:$excluded_tag
|
||||||
|
|
||||||
Existing users will probably want to run "notmuch setup" again to add
|
Existing users will probably want to run `notmuch setup` again to add
|
||||||
the new well-commented [search] section to the configuration file.
|
the new well-commented [search] section to the configuration file.
|
||||||
|
|
||||||
For new configurations, accepting the default setting will cause the
|
For new configurations, accepting the default setting will cause the
|
||||||
tags "deleted" and "spam" to be excluded, equivalent to running:
|
tags "deleted" and "spam" to be excluded, equivalent to running:
|
||||||
|
|
||||||
notmuch config set search.exclude_tags deleted spam
|
notmuch config set search.exclude_tags deleted spam
|
||||||
|
|
||||||
Raw show format changes
|
Raw show format changes
|
||||||
|
|
||||||
The output of show --format=raw has changed for multipart and
|
The output of show `--format=raw` has changed for multipart and
|
||||||
message parts. Previously, the output was a mash of somewhat-parsed
|
message parts. Previously, the output was a mash of somewhat-parsed
|
||||||
headers and transfer-decoded bodies. Now, such parts are reproduced
|
headers and transfer-decoded bodies. Now, such parts are reproduced
|
||||||
faithfully from the original source. Message parts (which includes
|
faithfully from the original source. Message parts (which includes
|
||||||
|
@ -54,7 +47,7 @@ Raw show format changes
|
||||||
|
|
||||||
Listing configuration items
|
Listing configuration items
|
||||||
|
|
||||||
The new "config list" command prints out all configuration items and
|
The new `config list` command prints out all configuration items and
|
||||||
their values.
|
their values.
|
||||||
|
|
||||||
Emacs Interface
|
Emacs Interface
|
||||||
|
@ -64,7 +57,7 @@ Changes to tagging interface
|
||||||
|
|
||||||
The user-facing tagging functions in the Emacs interface have been
|
The user-facing tagging functions in the Emacs interface have been
|
||||||
normalized across all notmuch modes. The tagging functions are now
|
normalized across all notmuch modes. The tagging functions are now
|
||||||
'notmuch-search-tag' in search-mode, and 'notmuch-show-tag' in
|
notmuch-search-tag in search-mode, and notmuch-show-tag in
|
||||||
show-mode. They accept a string representing a single tag change,
|
show-mode. They accept a string representing a single tag change,
|
||||||
or a list of tag changes. See 'M-x describe-function notmuch-tag'
|
or a list of tag changes. See 'M-x describe-function notmuch-tag'
|
||||||
for more information.
|
for more information.
|
||||||
|
@ -83,11 +76,11 @@ Reply improvement using the JSON format
|
||||||
New add-on tool: notmuch-mutt
|
New add-on tool: notmuch-mutt
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
The new contrib/ tool "notmuch-mutt" provides Notmuch integration for
|
The new contrib/ tool `notmuch-mutt` provides Notmuch integration for
|
||||||
the Mutt mail user agent. Using it, Mutt users can perform mail
|
the Mutt mail user agent. Using it, Mutt users can perform mail
|
||||||
search, thread reconstruction, and mail tagging/untagging without
|
search, thread reconstruction, and mail tagging/untagging without
|
||||||
leaving Mutt. notmuch-mutt, formerly distributed under the name
|
leaving Mutt. notmuch-mutt, formerly distributed under the name
|
||||||
"mutt-notmuch" by Stefano Zacchiroli, will be maintained as a notmuch
|
`mutt-notmuch` by Stefano Zacchiroli, will be maintained as a notmuch
|
||||||
contrib/ from now on.
|
contrib/ from now on.
|
||||||
|
|
||||||
Library changes
|
Library changes
|
||||||
|
@ -96,26 +89,26 @@ Library changes
|
||||||
The API changes detailed below break binary and source compatibility,
|
The API changes detailed below break binary and source compatibility,
|
||||||
so libnotmuch has been bumped to version 3.0.0.
|
so libnotmuch has been bumped to version 3.0.0.
|
||||||
|
|
||||||
The function notmuch_database_close has been split into
|
The function `notmuch_database_close` has been split into
|
||||||
notmuch_database_close and notmuch_database_destroy
|
`notmuch_database_close` and `notmuch_database_destroy`
|
||||||
|
|
||||||
This makes it possible for long running programs to close the xapian
|
This makes it possible for long running programs to close the xapian
|
||||||
database and thus release the lock associated with it without
|
database and thus release the lock associated with it without
|
||||||
destroying the data structures obtained from it.
|
destroying the data structures obtained from it.
|
||||||
|
|
||||||
notmuch_database_open, notmuch_database_create, and
|
`notmuch_database_open`, `notmuch_database_create`, and
|
||||||
notmuch_database_get_directory now return errors
|
`notmuch_database_get_directory` now return errors
|
||||||
|
|
||||||
The type signatures of these functions have changed so that the
|
The type signatures of these functions have changed so that the
|
||||||
functions now return a notmuch_status_t and take an out-argument for
|
functions now return a `notmuch_status_t` and take an out-argument for
|
||||||
returning the new database object or directory object.
|
returning the new database object or directory object.
|
||||||
|
|
||||||
go bindings changes
|
Go bindings changes
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Go 1 compatibility
|
Go 1 compatibility
|
||||||
|
|
||||||
The go bindings and the notmuch-addrlookup utility are now
|
The go bindings and the `notmuch-addrlookup` utility are now
|
||||||
compatible with go 1.
|
compatible with go 1.
|
||||||
|
|
||||||
Notmuch 0.12 (2012-03-20)
|
Notmuch 0.12 (2012-03-20)
|
||||||
|
|
|
@ -347,7 +347,6 @@ class Database(object):
|
||||||
|
|
||||||
def get_directory(self, path):
|
def get_directory(self, path):
|
||||||
"""Returns a :class:`Directory` of path,
|
"""Returns a :class:`Directory` of path,
|
||||||
(creating it if it does not exist(?))
|
|
||||||
|
|
||||||
:param path: An unicode string containing the path relative to the path
|
:param path: An unicode string containing the path relative to the path
|
||||||
of database (see :meth:`get_path`), or else should be an absolute
|
of database (see :meth:`get_path`), or else should be an absolute
|
||||||
|
@ -355,8 +354,6 @@ class Database(object):
|
||||||
:returns: :class:`Directory` or raises an exception.
|
:returns: :class:`Directory` or raises an exception.
|
||||||
:raises: :exc:`FileError` if path is not relative database or absolute
|
:raises: :exc:`FileError` if path is not relative database or absolute
|
||||||
with initial components same as database.
|
with initial components same as database.
|
||||||
:raises: :exc:`ReadOnlyDatabaseError` if the database has not been
|
|
||||||
opened in read-write mode
|
|
||||||
"""
|
"""
|
||||||
self._assert_db_is_initialized()
|
self._assert_db_is_initialized()
|
||||||
|
|
||||||
|
@ -530,19 +527,10 @@ class Database(object):
|
||||||
retry.
|
retry.
|
||||||
:raises: :exc:`NotInitializedError` if the database was not
|
:raises: :exc:`NotInitializedError` if the database was not
|
||||||
intitialized.
|
intitialized.
|
||||||
:raises: :exc:`ReadOnlyDatabaseError` if the database has not been
|
|
||||||
opened in read-write mode
|
|
||||||
|
|
||||||
*Added in notmuch 0.9*"""
|
*Added in notmuch 0.9*"""
|
||||||
self._assert_db_is_initialized()
|
self._assert_db_is_initialized()
|
||||||
|
|
||||||
# work around libnotmuch calling exit(3), see
|
|
||||||
# id:20120221002921.8534.57091@thinkbox.jade-hamburg.de
|
|
||||||
# TODO: remove once this issue is resolved
|
|
||||||
if self.mode != Database.MODE.READ_WRITE:
|
|
||||||
raise ReadOnlyDatabaseError('The database has to be opened in '
|
|
||||||
'read-write mode for get_directory')
|
|
||||||
|
|
||||||
msg_p = NotmuchMessageP()
|
msg_p = NotmuchMessageP()
|
||||||
status = Database._find_message_by_filename(self._db, _str(filename),
|
status = Database._find_message_by_filename(self._db, _str(filename),
|
||||||
byref(msg_p))
|
byref(msg_p))
|
||||||
|
|
|
@ -244,7 +244,12 @@ the given type."
|
||||||
current buffer, if possible."
|
current buffer, if possible."
|
||||||
(let ((display-buffer (current-buffer)))
|
(let ((display-buffer (current-buffer)))
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(let* ((charset (plist-get part :content-charset))
|
;; In case there is :content, the content string is already converted
|
||||||
|
;; into emacs internal format. `gnus-decoded' is a fake charset,
|
||||||
|
;; which means no further decoding (to be done by mm- functions).
|
||||||
|
(let* ((charset (if (plist-member part :content)
|
||||||
|
'gnus-decoded
|
||||||
|
(plist-get part :content-charset)))
|
||||||
(handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset)))))
|
(handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset)))))
|
||||||
;; If the user wants the part inlined, insert the content and
|
;; If the user wants the part inlined, insert the content and
|
||||||
;; test whether we are able to inline it (which includes both
|
;; test whether we are able to inline it (which includes both
|
||||||
|
|
|
@ -956,7 +956,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
|
||||||
document.get_value (NOTMUCH_VALUE_TIMESTAMP));
|
document.get_value (NOTMUCH_VALUE_TIMESTAMP));
|
||||||
|
|
||||||
directory = _notmuch_directory_create (notmuch, term.c_str() + 10,
|
directory = _notmuch_directory_create (notmuch, term.c_str() + 10,
|
||||||
&status);
|
NOTMUCH_FIND_CREATE, &status);
|
||||||
notmuch_directory_set_mtime (directory, mtime);
|
notmuch_directory_set_mtime (directory, mtime);
|
||||||
notmuch_directory_destroy (directory);
|
notmuch_directory_destroy (directory);
|
||||||
}
|
}
|
||||||
|
@ -1197,9 +1197,17 @@ _notmuch_database_split_path (void *ctx,
|
||||||
return NOTMUCH_STATUS_SUCCESS;
|
return NOTMUCH_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find the document ID of the specified directory.
|
||||||
|
*
|
||||||
|
* If (flags & NOTMUCH_FIND_CREATE), a new directory document will be
|
||||||
|
* created if one does not exist for 'path'. Otherwise, if the
|
||||||
|
* directory document does not exist, this sets *directory_id to
|
||||||
|
* ((unsigned int)-1) and returns NOTMUCH_STATUS_SUCCESS.
|
||||||
|
*/
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
_notmuch_database_find_directory_id (notmuch_database_t *notmuch,
|
_notmuch_database_find_directory_id (notmuch_database_t *notmuch,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
unsigned int *directory_id)
|
unsigned int *directory_id)
|
||||||
{
|
{
|
||||||
notmuch_directory_t *directory;
|
notmuch_directory_t *directory;
|
||||||
|
@ -1210,8 +1218,8 @@ _notmuch_database_find_directory_id (notmuch_database_t *notmuch,
|
||||||
return NOTMUCH_STATUS_SUCCESS;
|
return NOTMUCH_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
directory = _notmuch_directory_create (notmuch, path, &status);
|
directory = _notmuch_directory_create (notmuch, path, flags, &status);
|
||||||
if (status) {
|
if (status || !directory) {
|
||||||
*directory_id = -1;
|
*directory_id = -1;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1240,13 +1248,16 @@ _notmuch_database_get_directory_path (void *ctx,
|
||||||
* database path), return a new string (with 'ctx' as the talloc
|
* database path), return a new string (with 'ctx' as the talloc
|
||||||
* owner) suitable for use as a direntry term value.
|
* owner) suitable for use as a direntry term value.
|
||||||
*
|
*
|
||||||
* The necessary directory documents will be created in the database
|
* If (flags & NOTMUCH_FIND_CREATE), the necessary directory documents
|
||||||
* as needed.
|
* will be created in the database as needed. Otherwise, if the
|
||||||
|
* necessary directory documents do not exist, this sets
|
||||||
|
* *direntry to NULL and returns NOTMUCH_STATUS_SUCCESS.
|
||||||
*/
|
*/
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
_notmuch_database_filename_to_direntry (void *ctx,
|
_notmuch_database_filename_to_direntry (void *ctx,
|
||||||
notmuch_database_t *notmuch,
|
notmuch_database_t *notmuch,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
char **direntry)
|
char **direntry)
|
||||||
{
|
{
|
||||||
const char *relative, *directory, *basename;
|
const char *relative, *directory, *basename;
|
||||||
|
@ -1260,10 +1271,12 @@ _notmuch_database_filename_to_direntry (void *ctx,
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = _notmuch_database_find_directory_id (notmuch, directory,
|
status = _notmuch_database_find_directory_id (notmuch, directory, flags,
|
||||||
&directory_id);
|
&directory_id);
|
||||||
if (status)
|
if (status || directory_id == (unsigned int)-1) {
|
||||||
|
*direntry = NULL;
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
*direntry = talloc_asprintf (ctx, "%u:%s", directory_id, basename);
|
*direntry = talloc_asprintf (ctx, "%u:%s", directory_id, basename);
|
||||||
|
|
||||||
|
@ -1315,12 +1328,9 @@ notmuch_database_get_directory (notmuch_database_t *notmuch,
|
||||||
return NOTMUCH_STATUS_NULL_POINTER;
|
return NOTMUCH_STATUS_NULL_POINTER;
|
||||||
*directory = NULL;
|
*directory = NULL;
|
||||||
|
|
||||||
/* XXX Handle read-only databases properly */
|
|
||||||
if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
|
|
||||||
return NOTMUCH_STATUS_READ_ONLY_DATABASE;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
*directory = _notmuch_directory_create (notmuch, path, &status);
|
*directory = _notmuch_directory_create (notmuch, path,
|
||||||
|
NOTMUCH_FIND_LOOKUP, &status);
|
||||||
} catch (const Xapian::Error &error) {
|
} catch (const Xapian::Error &error) {
|
||||||
fprintf (stderr, "A Xapian exception occurred getting directory: %s.\n",
|
fprintf (stderr, "A Xapian exception occurred getting directory: %s.\n",
|
||||||
error.get_msg().c_str());
|
error.get_msg().c_str());
|
||||||
|
@ -1884,9 +1894,9 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
|
||||||
local = talloc_new (notmuch);
|
local = talloc_new (notmuch);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
status = _notmuch_database_filename_to_direntry (local, notmuch,
|
status = _notmuch_database_filename_to_direntry (
|
||||||
filename, &direntry);
|
local, notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
|
||||||
if (status)
|
if (status || !direntry)
|
||||||
goto DONE;
|
goto DONE;
|
||||||
|
|
||||||
term = talloc_asprintf (local, "%s%s", prefix, direntry);
|
term = talloc_asprintf (local, "%s%s", prefix, direntry);
|
||||||
|
|
|
@ -82,28 +82,41 @@ find_directory_document (notmuch_database_t *notmuch,
|
||||||
return NOTMUCH_PRIVATE_STATUS_SUCCESS;
|
return NOTMUCH_PRIVATE_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find or create a directory document.
|
||||||
|
*
|
||||||
|
* 'path' should be a path relative to the path of 'database', or else
|
||||||
|
* should be an absolute path with initial components that match the
|
||||||
|
* path of 'database'.
|
||||||
|
*
|
||||||
|
* If (flags & NOTMUCH_FIND_CREATE), then the directory document will
|
||||||
|
* be created if it does not exist. Otherwise, if the directory
|
||||||
|
* document does not exist, *status_ret is set to
|
||||||
|
* NOTMUCH_STATUS_SUCCESS and this returns NULL.
|
||||||
|
*/
|
||||||
notmuch_directory_t *
|
notmuch_directory_t *
|
||||||
_notmuch_directory_create (notmuch_database_t *notmuch,
|
_notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
notmuch_status_t *status_ret)
|
notmuch_status_t *status_ret)
|
||||||
{
|
{
|
||||||
Xapian::WritableDatabase *db;
|
Xapian::WritableDatabase *db;
|
||||||
notmuch_directory_t *directory;
|
notmuch_directory_t *directory;
|
||||||
notmuch_private_status_t private_status;
|
notmuch_private_status_t private_status;
|
||||||
const char *db_path;
|
const char *db_path;
|
||||||
|
notmuch_bool_t create = (flags & NOTMUCH_FIND_CREATE);
|
||||||
|
|
||||||
*status_ret = NOTMUCH_STATUS_SUCCESS;
|
*status_ret = NOTMUCH_STATUS_SUCCESS;
|
||||||
|
|
||||||
path = _notmuch_database_relative_path (notmuch, path);
|
path = _notmuch_database_relative_path (notmuch, path);
|
||||||
|
|
||||||
if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
|
if (create && notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
|
||||||
INTERNAL_ERROR ("Failure to ensure database is writable");
|
INTERNAL_ERROR ("Failure to ensure database is writable");
|
||||||
|
|
||||||
db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
|
|
||||||
|
|
||||||
directory = talloc (notmuch, notmuch_directory_t);
|
directory = talloc (notmuch, notmuch_directory_t);
|
||||||
if (unlikely (directory == NULL))
|
if (unlikely (directory == NULL)) {
|
||||||
|
*status_ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
directory->notmuch = notmuch;
|
directory->notmuch = notmuch;
|
||||||
|
|
||||||
|
@ -122,6 +135,13 @@ _notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
directory->document_id = directory->doc.get_docid ();
|
directory->document_id = directory->doc.get_docid ();
|
||||||
|
|
||||||
if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
|
if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
|
||||||
|
if (!create) {
|
||||||
|
notmuch_directory_destroy (directory);
|
||||||
|
directory = NULL;
|
||||||
|
*status_ret = NOTMUCH_STATUS_SUCCESS;
|
||||||
|
goto DONE;
|
||||||
|
}
|
||||||
|
|
||||||
void *local = talloc_new (directory);
|
void *local = talloc_new (directory);
|
||||||
const char *parent, *basename;
|
const char *parent, *basename;
|
||||||
Xapian::docid parent_id;
|
Xapian::docid parent_id;
|
||||||
|
@ -133,7 +153,13 @@ _notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
|
|
||||||
_notmuch_database_split_path (local, path, &parent, &basename);
|
_notmuch_database_split_path (local, path, &parent, &basename);
|
||||||
|
|
||||||
_notmuch_database_find_directory_id (notmuch, parent, &parent_id);
|
*status_ret = _notmuch_database_find_directory_id (
|
||||||
|
notmuch, parent, NOTMUCH_FIND_CREATE, &parent_id);
|
||||||
|
if (*status_ret) {
|
||||||
|
notmuch_directory_destroy (directory);
|
||||||
|
directory = NULL;
|
||||||
|
goto DONE;
|
||||||
|
}
|
||||||
|
|
||||||
if (basename) {
|
if (basename) {
|
||||||
term = talloc_asprintf (local, "%s%u:%s",
|
term = talloc_asprintf (local, "%s%u:%s",
|
||||||
|
@ -145,6 +171,8 @@ _notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
|
directory->doc.add_value (NOTMUCH_VALUE_TIMESTAMP,
|
||||||
Xapian::sortable_serialise (0));
|
Xapian::sortable_serialise (0));
|
||||||
|
|
||||||
|
db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
|
||||||
|
|
||||||
directory->document_id = _notmuch_database_generate_doc_id (notmuch);
|
directory->document_id = _notmuch_database_generate_doc_id (notmuch);
|
||||||
db->replace_document (directory->document_id, directory->doc);
|
db->replace_document (directory->document_id, directory->doc);
|
||||||
talloc_free (local);
|
talloc_free (local);
|
||||||
|
@ -158,10 +186,11 @@ _notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
error.get_msg().c_str());
|
error.get_msg().c_str());
|
||||||
notmuch->exception_reported = TRUE;
|
notmuch->exception_reported = TRUE;
|
||||||
notmuch_directory_destroy (directory);
|
notmuch_directory_destroy (directory);
|
||||||
|
directory = NULL;
|
||||||
*status_ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
*status_ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DONE:
|
||||||
if (db_path != path)
|
if (db_path != path)
|
||||||
free ((char *) db_path);
|
free ((char *) db_path);
|
||||||
|
|
||||||
|
|
|
@ -495,9 +495,8 @@ _notmuch_message_add_filename (notmuch_message_t *message,
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = _notmuch_database_filename_to_direntry (local,
|
status = _notmuch_database_filename_to_direntry (
|
||||||
message->notmuch,
|
local, message->notmuch, filename, NOTMUCH_FIND_CREATE, &direntry);
|
||||||
filename, &direntry);
|
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
@ -541,9 +540,9 @@ _notmuch_message_remove_filename (notmuch_message_t *message,
|
||||||
notmuch_status_t status;
|
notmuch_status_t status;
|
||||||
Xapian::TermIterator i, last;
|
Xapian::TermIterator i, last;
|
||||||
|
|
||||||
status = _notmuch_database_filename_to_direntry (local, message->notmuch,
|
status = _notmuch_database_filename_to_direntry (
|
||||||
filename, &direntry);
|
local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
|
||||||
if (status)
|
if (status || !direntry)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
/* Unlink this file from its parent directory. */
|
/* Unlink this file from its parent directory. */
|
||||||
|
|
|
@ -146,6 +146,16 @@ typedef enum _notmuch_private_status {
|
||||||
: \
|
: \
|
||||||
(notmuch_status_t) private_status)
|
(notmuch_status_t) private_status)
|
||||||
|
|
||||||
|
/* Flags shared by various lookup functions. */
|
||||||
|
typedef enum _notmuch_find_flags {
|
||||||
|
/* Lookup without creating any documents. This is the default
|
||||||
|
* behavior. */
|
||||||
|
NOTMUCH_FIND_LOOKUP = 0,
|
||||||
|
/* If set, create the necessary document (or documents) if they
|
||||||
|
* are missing. Requires a read/write database. */
|
||||||
|
NOTMUCH_FIND_CREATE = 1<<0,
|
||||||
|
} notmuch_find_flags_t;
|
||||||
|
|
||||||
typedef struct _notmuch_doc_id_set notmuch_doc_id_set_t;
|
typedef struct _notmuch_doc_id_set notmuch_doc_id_set_t;
|
||||||
|
|
||||||
typedef struct _notmuch_string_list notmuch_string_list_t;
|
typedef struct _notmuch_string_list notmuch_string_list_t;
|
||||||
|
@ -188,6 +198,7 @@ _notmuch_database_find_unique_doc_id (notmuch_database_t *notmuch,
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
_notmuch_database_find_directory_id (notmuch_database_t *database,
|
_notmuch_database_find_directory_id (notmuch_database_t *database,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
unsigned int *directory_id);
|
unsigned int *directory_id);
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
|
@ -199,6 +210,7 @@ notmuch_status_t
|
||||||
_notmuch_database_filename_to_direntry (void *ctx,
|
_notmuch_database_filename_to_direntry (void *ctx,
|
||||||
notmuch_database_t *notmuch,
|
notmuch_database_t *notmuch,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
char **direntry);
|
char **direntry);
|
||||||
|
|
||||||
/* directory.cc */
|
/* directory.cc */
|
||||||
|
@ -206,6 +218,7 @@ _notmuch_database_filename_to_direntry (void *ctx,
|
||||||
notmuch_directory_t *
|
notmuch_directory_t *
|
||||||
_notmuch_directory_create (notmuch_database_t *notmuch,
|
_notmuch_directory_create (notmuch_database_t *notmuch,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
notmuch_find_flags_t flags,
|
||||||
notmuch_status_t *status_ret);
|
notmuch_status_t *status_ret);
|
||||||
|
|
||||||
unsigned int
|
unsigned int
|
||||||
|
|
|
@ -300,10 +300,8 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch);
|
||||||
* (see notmuch_database_get_path), or else should be an absolute path
|
* (see notmuch_database_get_path), or else should be an absolute path
|
||||||
* with initial components that match the path of 'database'.
|
* with initial components that match the path of 'database'.
|
||||||
*
|
*
|
||||||
* Note: Currently this will create the directory object if it doesn't
|
* If this directory object does not exist in the database, this
|
||||||
* exist. In the future, when a directory object does not exist this
|
* returns NOTMUCH_STATUS_SUCCESS and sets *directory to NULL.
|
||||||
* will return NOTMUCH_STATUS_SUCCESS and set *directory to NULL.
|
|
||||||
* Callers should be prepared for this.
|
|
||||||
*
|
*
|
||||||
* Return value:
|
* Return value:
|
||||||
*
|
*
|
||||||
|
@ -313,10 +311,6 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch);
|
||||||
*
|
*
|
||||||
* NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred;
|
* NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred;
|
||||||
* directory not retrieved.
|
* directory not retrieved.
|
||||||
*
|
|
||||||
* NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
|
|
||||||
* mode so the directory cannot be created (this case will be
|
|
||||||
* removed in the future).
|
|
||||||
*/
|
*/
|
||||||
notmuch_status_t
|
notmuch_status_t
|
||||||
notmuch_database_get_directory (notmuch_database_t *database,
|
notmuch_database_get_directory (notmuch_database_t *database,
|
||||||
|
|
|
@ -256,7 +256,7 @@ add_files_recursive (notmuch_database_t *notmuch,
|
||||||
notmuch_filenames_t *db_subdirs = NULL;
|
notmuch_filenames_t *db_subdirs = NULL;
|
||||||
time_t stat_time;
|
time_t stat_time;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
notmuch_bool_t is_maildir, new_directory;
|
notmuch_bool_t is_maildir;
|
||||||
const char **tag;
|
const char **tag;
|
||||||
|
|
||||||
if (stat (path, &st)) {
|
if (stat (path, &st)) {
|
||||||
|
@ -281,33 +281,12 @@ add_files_recursive (notmuch_database_t *notmuch,
|
||||||
}
|
}
|
||||||
db_mtime = directory ? notmuch_directory_get_mtime (directory) : 0;
|
db_mtime = directory ? notmuch_directory_get_mtime (directory) : 0;
|
||||||
|
|
||||||
new_directory = db_mtime ? FALSE : TRUE;
|
|
||||||
|
|
||||||
/* XXX This is a temporary workaround. If we don't update the
|
|
||||||
* database mtime until after processing messages in this
|
|
||||||
* directory, then a 0 mtime is *not* sufficient to indicate that
|
|
||||||
* this directory has no messages or subdirs in the database (for
|
|
||||||
* example, if an earlier run skipped the mtime update because
|
|
||||||
* fs_mtime == stat_time, or was interrupted before updating the
|
|
||||||
* mtime at the end). To address this, we record a (bogus)
|
|
||||||
* non-zero value before processing any child messages so that a
|
|
||||||
* later run won't mistake this for a new directory (and, for
|
|
||||||
* example, fail to detect removed files and subdirs).
|
|
||||||
*
|
|
||||||
* A better solution would be for notmuch_database_get_directory
|
|
||||||
* to indicate if it really created a new directory or not, either
|
|
||||||
* by a new out-argument, or by recording this information and
|
|
||||||
* providing an accessor.
|
|
||||||
*/
|
|
||||||
if (new_directory && directory)
|
|
||||||
notmuch_directory_set_mtime (directory, -1);
|
|
||||||
|
|
||||||
/* If the database knows about this directory, then we sort based
|
/* If the database knows about this directory, then we sort based
|
||||||
* on strcmp to match the database sorting. Otherwise, we can do
|
* on strcmp to match the database sorting. Otherwise, we can do
|
||||||
* inode-based sorting for faster filesystem operation. */
|
* inode-based sorting for faster filesystem operation. */
|
||||||
num_fs_entries = scandir (path, &fs_entries, 0,
|
num_fs_entries = scandir (path, &fs_entries, 0,
|
||||||
new_directory ?
|
directory ?
|
||||||
dirent_sort_inode : dirent_sort_strcmp_name);
|
dirent_sort_strcmp_name : dirent_sort_inode);
|
||||||
|
|
||||||
if (num_fs_entries == -1) {
|
if (num_fs_entries == -1) {
|
||||||
fprintf (stderr, "Error opening directory %s: %s\n",
|
fprintf (stderr, "Error opening directory %s: %s\n",
|
||||||
|
@ -376,13 +355,12 @@ add_files_recursive (notmuch_database_t *notmuch,
|
||||||
* being discovered until the clock catches up and the directory
|
* being discovered until the clock catches up and the directory
|
||||||
* is modified again).
|
* is modified again).
|
||||||
*/
|
*/
|
||||||
if (fs_mtime == db_mtime)
|
if (directory && fs_mtime == db_mtime)
|
||||||
goto DONE;
|
goto DONE;
|
||||||
|
|
||||||
/* new_directory means a directory that the database has never
|
/* If the database has never seen this directory before, we can
|
||||||
* seen before. In that case, we can simply leave db_files and
|
* simply leave db_files and db_subdirs NULL. */
|
||||||
* db_subdirs NULL. */
|
if (directory) {
|
||||||
if (!new_directory) {
|
|
||||||
db_files = notmuch_directory_get_child_files (directory);
|
db_files = notmuch_directory_get_child_files (directory);
|
||||||
db_subdirs = notmuch_directory_get_child_directories (directory);
|
db_subdirs = notmuch_directory_get_child_directories (directory);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,4 +28,12 @@ EOF
|
||||||
notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED
|
notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED
|
||||||
test_expect_equal_file OUTPUT EXPECTED
|
test_expect_equal_file OUTPUT EXPECTED
|
||||||
|
|
||||||
|
test_begin_subtest "get non-existent file"
|
||||||
|
test_python <<EOF
|
||||||
|
import notmuch
|
||||||
|
db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
|
||||||
|
print db.find_message_by_filename("i-dont-exist")
|
||||||
|
EOF
|
||||||
|
test_expect_equal "$(cat OUTPUT)" "None"
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in a new issue