[PATCH v2 3/3] reply: Move reply citation printing to the recursive MIME walk
This makes more logical sense, since it makes the recursive printer responsible for the entire reply body and lets it start at the root of the MIME tree instead of the first child. (We could move reply header creation in there, too, but if we ever support proper reply to multiple messages, we'll want just one set of reply headers computed from the entire message set and many bodies.) --- notmuch-reply.c | 12 ++-- 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 84a1220..0949d9f 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -47,7 +47,11 @@ format_part_reply (mime_node_t *node) { int i; -if (GMIME_IS_MESSAGE (node->part)) { +if (node->envelope_file) { + printf ("On %s, %s wrote:\n", + notmuch_message_get_header (node->envelope_file, "date"), + notmuch_message_get_header (node->envelope_file, "from")); +} else if (GMIME_IS_MESSAGE (node->part)) { GMimeMessage *message = GMIME_MESSAGE (node->part); InternetAddressList *recipients; const char *recipients_string; @@ -540,13 +544,9 @@ notmuch_reply_format_default(void *ctx, g_object_unref (G_OBJECT (reply)); reply = NULL; - printf ("On %s, %s wrote:\n", - notmuch_message_get_header (message, "date"), - notmuch_message_get_header (message, "from")); - if (mime_node_open (ctx, message, params->cryptoctx, params->decrypt, ) == NOTMUCH_STATUS_SUCCESS) { - format_part_reply (mime_node_child (root, 0)); + format_part_reply (root); talloc_free (root); } -- 1.7.9.1
[PATCH v2 2/3] reply: Convert default reply format to self-recursive style
This re-arranges the default reply formatter code to use the mime_node_t abstraction. There are no semantic changes. --- notmuch-reply.c | 123 +-- 1 files changed, 47 insertions(+), 76 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 2f5ed3d..84a1220 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -24,28 +24,6 @@ #include "gmime-filter-headers.h" static void -reply_headers_message_part (GMimeMessage *message); - -static void -reply_part_content (GMimeObject *part); - -static const notmuch_show_format_t format_reply = { -"", NULL, - "", NULL, - "", NULL, reply_headers_message_part, ">\n", - "", - NULL, - NULL, - NULL, - reply_part_content, - NULL, - "", - "", - "", "", -"" -}; - -static void show_reply_headers (GMimeMessage *message) { GMimeStream *stream_stdout = NULL, *stream_filter = NULL; @@ -65,66 +43,55 @@ show_reply_headers (GMimeMessage *message) } static void -reply_headers_message_part (GMimeMessage *message) +format_part_reply (mime_node_t *node) { -InternetAddressList *recipients; -const char *recipients_string; - -printf ("> From: %s\n", g_mime_message_get_sender (message)); -recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); -recipients_string = internet_address_list_to_string (recipients, 0); -if (recipients_string) - printf ("> To: %s\n", - recipients_string); -recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); -recipients_string = internet_address_list_to_string (recipients, 0); -if (recipients_string) - printf ("> Cc: %s\n", - recipients_string); -printf ("> Subject: %s\n", g_mime_message_get_subject (message)); -printf ("> Date: %s\n", g_mime_message_get_date_as_string (message)); -} - - -static void -reply_part_content (GMimeObject *part) -{ -GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part)); -GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (part); +int i; -if (g_mime_content_type_is_type (content_type, "multipart", "*") || - g_mime_content_type_is_type (content_type, "message", "rfc822")) -{ - /* Output nothing, since multipart subparts will be handled individually. */ -} -else if (g_mime_content_type_is_type (content_type, "application", "pgp-encrypted") || -g_mime_content_type_is_type (content_type, "application", "pgp-signature")) -{ - /* Ignore PGP/MIME cruft parts */ -} -else if (g_mime_content_type_is_type (content_type, "text", "*") && - !g_mime_content_type_is_type (content_type, "text", "html")) -{ - GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - show_text_part_content (part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); - g_object_unref(stream_stdout); -} -else -{ - if (disposition && - strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) - { - const char *filename = g_mime_part_get_filename (GMIME_PART (part)); +if (GMIME_IS_MESSAGE (node->part)) { + GMimeMessage *message = GMIME_MESSAGE (node->part); + InternetAddressList *recipients; + const char *recipients_string; + + printf ("> From: %s\n", g_mime_message_get_sender (message)); + recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); + recipients_string = internet_address_list_to_string (recipients, 0); + if (recipients_string) + printf ("> To: %s\n", + recipients_string); + recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); + recipients_string = internet_address_list_to_string (recipients, 0); + if (recipients_string) + printf ("> Cc: %s\n", + recipients_string); + printf ("> Subject: %s\n", g_mime_message_get_subject (message)); + printf ("> Date: %s\n", g_mime_message_get_date_as_string (message)); + printf (">\n"); +} else if (GMIME_IS_PART (node->part)) { + GMimeContentType *content_type = g_mime_object_get_content_type (node->part); + GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (node->part); + + if (g_mime_content_type_is_type (content_type, "application", "pgp-encrypted") || + g_mime_content_type_is_type (content_type, "application", "pgp-signature")) { + /* Ignore PGP/MIME cruft parts */ + } else if (g_mime_content_type_is_type (content_type, "text", "*") && + !g_mime_content_type_is_type (content_type, "text", "html")) { +
[PATCH v2 1/3] show/reply: Unify the code that extracts text parts
Previously, show and reply had separate implementations of decoding and printing text parts. Now both use show's implementation, which was more complete. Show's implementation has been extended with an option to add reply quoting to the extracted part (this is implemented as a named flag to avoid naked booleans, even though it's the only flag it can take). --- notmuch-client.h |8 notmuch-reply.c | 28 notmuch-show.c | 23 +++ 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index fa04fa2..203ac49 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -197,6 +197,14 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first); void format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply); +typedef enum { +NOTMUCH_SHOW_TEXT_PART_REPLY = 1 << 0, +} notmuch_show_text_part_flags; + +void +show_text_part_content (GMimeObject *part, GMimeStream *stream_out, + notmuch_show_text_part_flags flags); + char * json_quote_chararray (const void *ctx, const char *str, const size_t len); diff --git a/notmuch-reply.c b/notmuch-reply.c index e2b6c25..2f5ed3d 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -21,7 +21,6 @@ */ #include "notmuch-client.h" -#include "gmime-filter-reply.h" #include "gmime-filter-headers.h" static void @@ -106,29 +105,10 @@ reply_part_content (GMimeObject *part) else if (g_mime_content_type_is_type (content_type, "text", "*") && !g_mime_content_type_is_type (content_type, "text", "html")) { - GMimeStream *stream_stdout = NULL, *stream_filter = NULL; - GMimeDataWrapper *wrapper; - const char *charset; - - charset = g_mime_object_get_content_type_parameter (part, "charset"); - stream_stdout = g_mime_stream_file_new (stdout); - if (stream_stdout) { - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - stream_filter = g_mime_stream_filter_new(stream_stdout); - if (charset) { - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), -g_mime_filter_charset_new(charset, "UTF-8")); - } - } - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), -g_mime_filter_reply_new(TRUE)); - wrapper = g_mime_part_get_content_object (GMIME_PART (part)); - if (wrapper && stream_filter) - g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); - if (stream_filter) - g_object_unref(stream_filter); - if (stream_stdout) - g_object_unref(stream_stdout); + GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); + g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); + show_text_part_content (part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); + g_object_unref(stream_stdout); } else { diff --git a/notmuch-show.c b/notmuch-show.c index ff9d427..0bf5e21 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -19,6 +19,7 @@ */ #include "notmuch-client.h" +#include "gmime-filter-reply.h" static notmuch_status_t format_part_text (const void *ctx, mime_node_t *node, @@ -247,13 +248,17 @@ format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t repl /* Write a MIME text part out to the given stream. * + * If (flags & NOTMUCH_SHOW_TEXT_PART_REPLY), this prepends "> " to + * each output line. + * * Both line-ending conversion (CRLF->LF) and charset conversion ( -> * UTF-8) will be performed, so it is inappropriate to call this * function with a non-text part. Doing so will trigger an internal * error. */ -static void -show_text_part_content (GMimeObject *part, GMimeStream *stream_out) +void +show_text_part_content (GMimeObject *part, GMimeStream *stream_out, + notmuch_show_text_part_flags flags) { GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part)); GMimeStream *stream_filter = NULL; @@ -286,6 +291,16 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) } +if (flags & NOTMUCH_SHOW_TEXT_PART_REPLY) { + GMimeFilter *reply_filter; + reply_filter = g_mime_filter_reply_new (TRUE); + if (reply_filter) { + g_mime_stream_filter_add (GMIME_STREAM_FILTER (stream_filter), + reply_filter); + g_object_unref (reply_filter); + } +} + wrapper = g_mime_part_get_content_object (GMIME_PART (part)); if (wrapper && stream_filter) g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); @@ -532,7 +547,7 @@ format_part_text (const void *ctx, mime_node_t *node, { GMimeStream *stream_stdout = g_mime_stream_file_new (stdout);
[PATCH v2 0/3] Rewrite default reply format
This version fixes two minor formatting issues that Tomi pointed out [1]. There are no other changes. [1] id:"m2d382ia9d.fsf at guru.guru-group.fi"
[PATCH 0/3] Rewrite default reply format
Thanks for the review. New version coming shortly... Quoth Tomi Ollila on Mar 24 at 12:06 pm: > Austin Clements writes: > > > The default reply format is the last bastion of the old message > > formatter style. This series converts it to the new self-recursive > > style. After this, there will be one last series to rip out the > > compatibility code and do final cleanup. > > Works fine, patches look good... just 2 "spacing" questions: > > in id:"1332473647-9133-2-git-send-email-amdragon at mit.edu" > > + typedef enum { > + NOTMUCH_SHOW_TEXT_PART_REPLY = 1<<0, > + } notmuch_show_text_part_flags; > > Should this be like: NOTMUCH_SHOW_TEXT_PART_REPLY = (1 << 0), Changed to NOTMUCH_SHOW_TEXT_PART_REPLY = 1 << 0 to be consistent with operator spacing. I left out the parens since they aren't necessary. > and this > > + * If flags_SHOW_TEXT_PART_REPLY, this prepends "> " to each > + * output line. > + * > > like: > > + * If flags & NOTMUCH_SHOW_TEXT_PART_REPLY, this prepends "> " to each Changed. On this one I put in parens to better distinguish it from the surrounding prose. > Tomi
bug in emacs reply code?
Hi all, just upgraded from 0.11 to master on one machine, and emacs reply started failing as follows. The four tests fail, all others pass. $ emacs --version GNU Emacs 23.2.1 $ make test [...] emacs: Testing emacs interface [...] FAIL Reply within emacs --- emacs.24.expected 2012-03-27 15:02:05.894335772 + +++ emacs.24.output2012-03-27 15:02:05.884335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite +From: Notmuch Test Suite +To: user at example.com To: user at example.com Subject: Re: Testing message sent via SMTP +Subject: Re: Testing message sent via SMTP In-Reply-To: Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Reply within emacs to a multipart/mixed message --- emacs.25.expected 2012-03-27 15:02:06.294335772 + +++ emacs.25.output 2012-03-27 15:02:06.294335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite +From: Notmuch Test Suite +To: Adrian Perez de Castro , notmuch at notmuchmail.org To: Adrian Perez de Castro , notmuch at notmuchmail.org Subject: Re: [notmuch] Introducing myself +Subject: Re: [notmuch] Introducing myself In-Reply-To: <20091118002059.067214ed at hikari> Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Reply within emacs to a multipart/alternative message --- emacs.26.expected 2012-03-27 15:02:06.914335772 + +++ emacs.26.output 2012-03-27 15:02:06.914335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite +From: Notmuch Test Suite +To: Alex Botero-Lowry , notmuch at notmuchmail.org To: Alex Botero-Lowry , notmuch at notmuchmail.org Subject: Re: [notmuch] preliminary FreeBSD support +Subject: Re: [notmuch] preliminary FreeBSD support In-Reply-To: Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Quote MML tags in reply --- emacs.27.expected 2012-03-27 15:02:07.524335772 + +++ emacs.27.output 2012-03-27 15:02:07.524335772 + @@ -1,6 +1,8 @@ From: Notmuch Test Suite +From: Notmuch Test Suite To: Subject: Re: Quote MML tags in reply +Subject: Re: Quote MML tags in reply In-Reply-To: Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- [...]
Goto command for existing search windows
I was looking for a function which would find a buffer based on one of my saved searches, and perform the search if it didn't exist. I've gotten it a bit closer, if I perform the search that matches a saved search, then this routine will find it because of the magic in notmuch-search-buffer-title, but perhaps someone else feels up to searching through the saved searches directly? (defun notmuch-goto-or-search ( query) "Find a notmuch-search buffer with the given query, or run \"notmuch search\" with the given `query' and display results. If `query' is nil, it is read interactively from the minibuffer." (interactive) (if (null query) (setq query (notmuch-read-query "Notmuch goto-or-search: "))) (let ((buffer-name (notmuch-search-buffer-title query))) (setq buf (get-buffer buffer-name))) (if (not buf) (notmuch-search query) (switch-to-buffer buf) ))) I then use it something like this: (global-set-key [C-f1] (lambda () (interactive) (notmuch-goto-or-search "tag:inbox and tag:unread and not tag:deleted"))) (global-set-key [C-f2] (lambda () (interactive) (notmuch-goto-or-search "tag:inbox and not tag:deleted"))) (global-set-key [C-f3] 'notmuch) (global-set-key [C-f6] (lambda () (interactive) (notmuch-goto-or-search "tag:todo and not tag:deleted"))) It would be better if I could use my Inbox, INBOX and todo names for the saved searches, but how to do that without breaking generality of searching the body of the email? Do I have to define my own ss: (saved search) prefix or something, as I believe some others have? This is what I'm willing to do today, and it works for me, I could patch notmuch.el, but I wondered about answering the other questions. Also, some elisp master could hint about how to make the binding not so ugly. ;) Another appreciated elisp hint would be how to get the buf variable to go inside the let, I keep getting complaints about buffer-name not being defined, thus the "ugly" setq, which works. Enjoy, -Mark
[PATCH 1/7] Split notmuch_database_close into two functions
Formerly notmuch_database_close closed the xapian database and destroyed the talloc structure associated with the notmuch database object. Split notmuch_database_close into notmuch_database_close and notmuch_database_destroy. This makes it possible for long running programs to close the xapian database and thus release the lock associated with it without destroying the data structures obtained from it. This also makes the api more consistent since every other data structure has a destructor function. Signed-off-by: Justus Winter <4winter at informatik.uni-hamburg.de> --- lib/database.cc | 14 -- lib/notmuch.h | 15 +++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 16c4354..2fefcad 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -642,7 +642,7 @@ notmuch_database_open (const char *path, " read-write mode.\n", notmuch_path, version, NOTMUCH_DATABASE_VERSION); notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; goto DONE; } @@ -702,7 +702,7 @@ notmuch_database_open (const char *path, } catch (const Xapian::Error ) { fprintf (stderr, "A Xapian exception occurred opening database: %s\n", error.get_msg().c_str()); - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; } @@ -738,9 +738,19 @@ notmuch_database_close (notmuch_database_t *notmuch) } delete notmuch->term_gen; +notmuch->term_gen = NULL; delete notmuch->query_parser; +notmuch->query_parser = NULL; delete notmuch->xapian_db; +notmuch->xapian_db = NULL; delete notmuch->value_range_processor; +notmuch->value_range_processor = NULL; +} + +void +notmuch_database_destroy (notmuch_database_t *notmuch) +{ +notmuch_database_close (notmuch); talloc_free (notmuch); } diff --git a/lib/notmuch.h b/lib/notmuch.h index babd208..2fb4e70 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -133,7 +133,7 @@ typedef struct _notmuch_filenames notmuch_filenames_t; * * After a successful call to notmuch_database_create, the returned * database will be open so the caller should call - * notmuch_database_close when finished with it. + * notmuch_database_destroy when finished with it. * * The database will not yet have any data in it * (notmuch_database_create itself is a very cheap function). Messages @@ -165,7 +165,7 @@ typedef enum { * An existing notmuch database can be identified by the presence of a * directory named ".notmuch" below 'path'. * - * The caller should call notmuch_database_close when finished with + * The caller should call notmuch_database_destroy when finished with * this database. * * In case of any failure, this function returns NULL, (after printing @@ -175,11 +175,18 @@ notmuch_database_t * notmuch_database_open (const char *path, notmuch_database_mode_t mode); -/* Close the given notmuch database, freeing all associated - * resources. See notmuch_database_open. */ +/* Close the given notmuch database. + * + * This function is called by notmuch_database_destroy and can be + * called multiple times. */ void notmuch_database_close (notmuch_database_t *database); +/* Destroy the notmuch database freeing all associated + * resources */ +void +notmuch_database_destroy (notmuch_database_t *database); + /* Return the database path of the given database. * * The return value is a string owned by notmuch so should not be -- 1.7.9.1
[RFC] Split notmuch_database_close into two functions
You're right of course, updated patch sent as a follow up. Justus
[PATCH 0/2] Escape message ID queries in Emacs
On Tue, Mar 27 2012, Austin Clements wrote: > Currently, Emacs does not escape message ID queries and is > inconsistent about quoting them. This patch centralizes this in one > function that always produces a properly quoted and escaped message ID > query. > > With this, Emacs no longer gets confused by Tomi's crazy message, > id:"id:""1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi""" LGTM. Things work! One observation, though: In search bar the following queries return one match: id:id:"1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi" id:"id:""1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi""" but id:"id:"1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi"" return 0 matches. It looks like the (above) search strings goes verbatim to command line i.e. 'notmuch' 'search' '--sort=oldest-first' 'id:...' can be used to compare... so that would be CLI issue if there is ever need to do do anything with it. +1 Thanks for fixing this. Tomi
[PATCH] emacs: Fix mis-named argument to notmuch-get-bodypart-internal
On Tue, Mar 27 2012, Austin Clements wrote: > Previously, this function took an argument called "message-id", even > though it was a general query, rather than a message ID. This changes > it to "query". > --- +1 Tomi > emacs/notmuch-lib.el |4 ++-- > 1 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el > index 2e367b5..4b17ecc 100644 > --- a/emacs/notmuch-lib.el > +++ b/emacs/notmuch-lib.el > @@ -219,13 +219,13 @@ the given type." > > ;; Helper for parts which are generally not included in the default > ;; JSON output. > -(defun notmuch-get-bodypart-internal (message-id part-number process-crypto) > +(defun notmuch-get-bodypart-internal (query part-number process-crypto) >(let ((args '("show" "--format=raw")) > (part-arg (format "--part=%s" part-number))) > (setq args (append args (list part-arg))) > (if process-crypto > (setq args (append args '("--decrypt" > -(setq args (append args (list message-id))) > +(setq args (append args (list query))) > (with-temp-buffer >(let ((coding-system-for-read 'no-conversion)) > (progn > -- > 1.7.7.2 > > ___ > notmuch mailing list > notmuch at notmuchmail.org > http://notmuchmail.org/mailman/listinfo/notmuch
Unofficial notmuch wiki concerns
On Mon, 26 Mar 2012 12:39:47 -0500, Kyle Sexton wrote: > > My goal is to make a site where the documentation is easy to update, > and attractive. My concern is having too many places out there for > information. I tried to assuage that with the disclaimer pointing to > the canonical site. Does the community think there is space for an > unofficial wiki like this? [ For those of you following along at home, notmuchuchmail.org is an ikiwiki instance, writable by anonymous git push ] First, it's great that you want to give something back to the notmuch project. As a project, I think we should be primarily concerned with the quality of the official documentation that ships with the software, but I recognize that some things are too transient or specialized to fit in the official docs. Personally, I would much rather you help improve the notmuch wiki than fork it. If you want to have your personal site, like a blog, of course that's fine. But if you expect other people to contribute to your wiki instead of the official one (and the generic name kind of suggests that you do, as compared to mocker.org), then I think it isn't helpful. The set of contributors to the wiki (like the overall notmuch community) is extremely small, and splitting that will decrease the quality of both splits. If there are technical barriers preventing contributing to the notmuch wiki, then the folk on #notmuch would be happy to help with that. It might just be that you prefer some other wiki software; unfortunately these are the sorts of compromises needed to make free software projects (and other projects) work. Or you can agitate to have the project change it's wiki, if you feel that strongly about it. David -- next part -- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 315 bytes Desc: not available URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20120327/6c524d7b/attachment.pgp>
[PATCH 0/2] Escape message ID queries in Emacs
On Tue, 27 Mar 2012, Tomi Ollila wrote: > On Tue, Mar 27 2012, Austin Clements wrote: > >> Currently, Emacs does not escape message ID queries and is >> inconsistent about quoting them. This patch centralizes this in one >> function that always produces a properly quoted and escaped message ID >> query. >> >> With this, Emacs no longer gets confused by Tomi's crazy message, >> id:"id:""1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi""" > > LGTM. Things work! > > > One observation, though: > > In search bar the following queries return one match: > > id:id:"1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi" > id:"id:""1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi""" > > but > > id:"id:"1332281811-24710-2b-git-send-email-tomi.ollila at iki.fi"" > > return 0 matches. > > It looks like the (above) search strings goes verbatim to command line > > i.e. 'notmuch' 'search' '--sort=oldest-first' 'id:...' > > can be used to compare... so that would be CLI issue if there > is ever need to do do anything with it. I'm not sure I would consider this a CLI "issue". It may not be intuitive, but it is an intentional part of the Xapian query parser's grammar. If a boolean prefix is followed immediately by a quote, then the term is between that quote and the next quote, modulo an escaping rule that lets you include a literal quote in the term by doubling it in the query string. Together, these rules let you reliably put anything in a boolean search term. > +1 > > Thanks for fixing this. > > Tomi
[PATCH v3 4/4] cli: refactor "notmuch restore" message tagging into a separate function
Refactor to make tagging code easier to reuse in the future. No functional changes. Signed-off-by: Jani Nikula --- notmuch-restore.c | 148 - 1 files changed, 78 insertions(+), 70 deletions(-) diff --git a/notmuch-restore.c b/notmuch-restore.c index 87d9772..d3b9246 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -20,6 +20,81 @@ #include "notmuch-client.h" +static int +tag_message (notmuch_database_t *notmuch, const char *message_id, +char *file_tags, notmuch_bool_t remove_all, +notmuch_bool_t synchronize_flags) +{ +notmuch_status_t status; +notmuch_tags_t *db_tags; +char *db_tags_str; +notmuch_message_t *message = NULL; +const char *tag; +char *next; +int ret = 0; + +status = notmuch_database_find_message (notmuch, message_id, ); +if (status || message == NULL) { + fprintf (stderr, "Warning: Cannot apply tags to %smessage: %s\n", +message ? "" : "missing ", message_id); + if (status) + fprintf (stderr, "%s\n", notmuch_status_to_string(status)); + return 1; +} + +/* In order to detect missing messages, this check/optimization is + * intentionally done *after* first finding the message. */ +if (!remove_all && (file_tags == NULL || *file_tags == '\0')) + goto DONE; + +db_tags_str = NULL; +for (db_tags = notmuch_message_get_tags (message); +notmuch_tags_valid (db_tags); +notmuch_tags_move_to_next (db_tags)) { + tag = notmuch_tags_get (db_tags); + + if (db_tags_str) + db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag); + else + db_tags_str = talloc_strdup (message, tag); +} + +if (((file_tags == NULL || *file_tags == '\0') && +(db_tags_str == NULL || *db_tags_str == '\0')) || + (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0)) + goto DONE; + +notmuch_message_freeze (message); + +if (remove_all) + notmuch_message_remove_all_tags (message); + +next = file_tags; +while (next) { + tag = strsep (, " "); + if (*tag == '\0') + continue; + status = notmuch_message_add_tag (message, tag); + if (status) { + fprintf (stderr, "Error applying tag %s to message %s:\n", +tag, message_id); + fprintf (stderr, "%s\n", notmuch_status_to_string (status)); + ret = 1; + } +} + +notmuch_message_thaw (message); + +if (synchronize_flags) + notmuch_message_tags_to_maildir_flags (message); + +DONE: +if (message) + notmuch_message_destroy (message); + +return ret; +} + int notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) { @@ -88,11 +163,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) while ((line_len = getline (, _size, input)) != -1) { regmatch_t match[3]; - char *message_id, *file_tags, *tag, *next; - notmuch_message_t *message = NULL; - notmuch_status_t status; - notmuch_tags_t *db_tags; - char *db_tags_str; + char *message_id, *file_tags; chomp_newline (line); @@ -109,72 +180,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) file_tags = xstrndup (line + match[2].rm_so, match[2].rm_eo - match[2].rm_so); - status = notmuch_database_find_message (notmuch, message_id, ); - if (status || message == NULL) { - fprintf (stderr, "Warning: Cannot apply tags to %smessage: %s\n", -message ? "" : "missing ", message_id); - if (status) - fprintf (stderr, "%s\n", -notmuch_status_to_string(status)); - goto NEXT_LINE; - } - - /* In order to detect missing messages, this check/optimization is -* intentionally done *after* first finding the message. */ - if (accumulate && (file_tags == NULL || *file_tags == '\0')) - { - goto NEXT_LINE; - } - - db_tags_str = NULL; - for (db_tags = notmuch_message_get_tags (message); -notmuch_tags_valid (db_tags); -notmuch_tags_move_to_next (db_tags)) - { - const char *tag = notmuch_tags_get (db_tags); - - if (db_tags_str) - db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag); - else - db_tags_str = talloc_strdup (message, tag); - } - - if (((file_tags == NULL || *file_tags == '\0') && -(db_tags_str == NULL || *db_tags_str == '\0')) || - (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0)) - { - goto NEXT_LINE; - } - - notmuch_message_freeze (message); - - if (!accumulate) - notmuch_message_remove_all_tags (message); - - next = file_tags; -
[PATCH v3 3/4] cli: refactor "notmuch tag" query tagging into a separate function
Refactor to make tagging code easier to reuse in the future. No functional changes. Signed-off-by: Jani Nikula --- notmuch-tag.c | 104 + 1 files changed, 60 insertions(+), 44 deletions(-) diff --git a/notmuch-tag.c b/notmuch-tag.c index 0a6b140..05feed3 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -110,6 +110,63 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, return query_string; } +/* Tag messages matching 'query_string' according to 'tag_ops', which + * must be an array of tagging operations terminated with an empty + * element. */ +static int +tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string, + tag_operation_t *tag_ops, notmuch_bool_t synchronize_flags) +{ +notmuch_query_t *query; +notmuch_messages_t *messages; +notmuch_message_t *message; +int i; + +/* Optimize the query so it excludes messages that already have + * the specified set of tags. */ +query_string = _optimize_tag_query (ctx, query_string, tag_ops); +if (query_string == NULL) { + fprintf (stderr, "Out of memory.\n"); + return 1; +} + +query = notmuch_query_create (notmuch, query_string); +if (query == NULL) { + fprintf (stderr, "Out of memory.\n"); + return 1; +} + +/* tagging is not interested in any special sort order */ +notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED); + +for (messages = notmuch_query_search_messages (query); +notmuch_messages_valid (messages) && !interrupted; +notmuch_messages_move_to_next (messages)) +{ + message = notmuch_messages_get (messages); + + notmuch_message_freeze (message); + + for (i = 0; tag_ops[i].tag; i++) { + if (tag_ops[i].remove) + notmuch_message_remove_tag (message, tag_ops[i].tag); + else + notmuch_message_add_tag (message, tag_ops[i].tag); + } + + notmuch_message_thaw (message); + + if (synchronize_flags) + notmuch_message_tags_to_maildir_flags (message); + + notmuch_message_destroy (message); +} + +notmuch_query_destroy (query); + +return interrupted; +} + int notmuch_tag_command (void *ctx, int argc, char *argv[]) { @@ -118,12 +175,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) char *query_string; notmuch_config_t *config; notmuch_database_t *notmuch; -notmuch_query_t *query; -notmuch_messages_t *messages; -notmuch_message_t *message; struct sigaction action; notmuch_bool_t synchronize_flags; int i; +int ret; /* Setup our handler for SIGINT */ memset (, 0, sizeof (struct sigaction)); @@ -170,14 +225,6 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) return 1; } -/* Optimize the query so it excludes messages that already have - * the specified set of tags. */ -query_string = _optimize_tag_query (ctx, query_string, tag_ops); -if (query_string == NULL) { - fprintf (stderr, "Out of memory.\n"); - return 1; -} - config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; @@ -189,40 +236,9 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); -query = notmuch_query_create (notmuch, query_string); -if (query == NULL) { - fprintf (stderr, "Out of memory.\n"); - return 1; -} - -/* tagging is not interested in any special sort order */ -notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED); +ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags); -for (messages = notmuch_query_search_messages (query); -notmuch_messages_valid (messages) && !interrupted; -notmuch_messages_move_to_next (messages)) -{ - message = notmuch_messages_get (messages); - - notmuch_message_freeze (message); - - for (i = 0; tag_ops[i].tag; i++) { - if (tag_ops[i].remove) - notmuch_message_remove_tag (message, tag_ops[i].tag); - else - notmuch_message_add_tag (message, tag_ops[i].tag); - } - - notmuch_message_thaw (message); - - if (synchronize_flags) - notmuch_message_tags_to_maildir_flags (message); - - notmuch_message_destroy (message); -} - -notmuch_query_destroy (query); notmuch_database_close (notmuch); -return interrupted; +return ret; } -- 1.7.5.4
[PATCH v3 2/4] cli: refactor "notmuch tag" data structures for tagging operations
To simplify code, keep all tagging operations in a single array instead of separate add and remove arrays. Apply tag changes in the order specified on the command line, instead of first removing and then adding the tags. This results in a minor functional change: If a tag is both added and removed, the last specified operation is now used. Previously the tag was always added. Change the relevant test to reflect the new behaviour. Signed-off-by: Jani Nikula --- notmuch-tag.c | 83 ++-- test/tagging |2 +- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/notmuch-tag.c b/notmuch-tag.c index 36b9b09..0a6b140 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -53,10 +53,14 @@ _escape_tag (char *buf, const char *tag) return buf; } +typedef struct { +const char *tag; +notmuch_bool_t remove; +} tag_operation_t; + static char * -_optimize_tag_query (void *ctx, const char *orig_query_string, char *argv[], -int *add_tags, int add_tags_count, -int *remove_tags, int remove_tags_count) +_optimize_tag_query (void *ctx, const char *orig_query_string, +const tag_operation_t *tag_ops) { /* This is subtler than it looks. Xapian ignores the '-' operator * at the beginning both queries and parenthesized groups and, @@ -71,15 +75,16 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, char *argv[], int i; unsigned int max_tag_len = 0; +/* Don't optimize if there are no tag changes. */ +if (tag_ops[0].tag == NULL) + return talloc_strdup (ctx, orig_query_string); + /* Allocate a buffer for escaping tags. This is large enough to * hold a fully escaped tag with every character doubled plus * enclosing quotes and a NUL. */ -for (i = 0; i < add_tags_count; i++) - if (strlen (argv[add_tags[i]] + 1) > max_tag_len) - max_tag_len = strlen (argv[add_tags[i]] + 1); -for (i = 0; i < remove_tags_count; i++) - if (strlen (argv[remove_tags[i]] + 1) > max_tag_len) - max_tag_len = strlen (argv[remove_tags[i]] + 1); +for (i = 0; tag_ops[i].tag; i++) + if (strlen (tag_ops[i].tag) > max_tag_len) + max_tag_len = strlen (tag_ops[i].tag); escaped = talloc_array(ctx, char, max_tag_len * 2 + 3); if (!escaped) return NULL; @@ -90,16 +95,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, char *argv[], else query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string); -for (i = 0; i < add_tags_count && query_string; i++) { +for (i = 0; tag_ops[i].tag && query_string; i++) { query_string = talloc_asprintf_append_buffer ( - query_string, "%snot tag:%s", join, - _escape_tag (escaped, argv[add_tags[i]] + 1)); - join = " or "; -} -for (i = 0; i < remove_tags_count && query_string; i++) { - query_string = talloc_asprintf_append_buffer ( - query_string, "%stag:%s", join, - _escape_tag (escaped, argv[remove_tags[i]] + 1)); + query_string, "%s%stag:%s", join, + tag_ops[i].remove ? "" : "not ", + _escape_tag (escaped, tag_ops[i].tag)); join = " or "; } @@ -113,9 +113,8 @@ _optimize_tag_query (void *ctx, const char *orig_query_string, char *argv[], int notmuch_tag_command (void *ctx, int argc, char *argv[]) { -int *add_tags, *remove_tags; -int add_tags_count = 0; -int remove_tags_count = 0; +tag_operation_t *tag_ops; +int tag_ops_count = 0; char *query_string; notmuch_config_t *config; notmuch_database_t *notmuch; @@ -133,35 +132,33 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) action.sa_flags = SA_RESTART; sigaction (SIGINT, , NULL); -add_tags = talloc_size (ctx, argc * sizeof (int)); -if (add_tags == NULL) { - fprintf (stderr, "Out of memory.\n"); - return 1; -} +argc--; argv++; /* skip subcommand argument */ -remove_tags = talloc_size (ctx, argc * sizeof (int)); -if (remove_tags == NULL) { +/* Array of tagging operations (add or remove), terminated with an + * empty element. */ +tag_ops = talloc_array (ctx, tag_operation_t, argc + 1); +if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } -argc--; argv++; /* skip subcommand argument */ - for (i = 0; i < argc; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } - if (argv[i][0] == '+') { - add_tags[add_tags_count++] = i; - } else if (argv[i][0] == '-') { - remove_tags[remove_tags_count++] = i; + if (argv[i][0] == '+' || argv[i][0] == '-') { + tag_ops[tag_ops_count].tag = argv[i] + 1; + tag_ops[tag_ops_count].remove = (argv[i][0] == '-'); + tag_ops_count++; } else {
[PATCH v3 1/4] test: add test for both adding and removing a tag at the same time
The current behaviour is that regardless of the order in which the addition and removal of a tag are specified, the tag is added. Signed-off-by: Jani Nikula --- test/tagging |8 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/test/tagging b/test/tagging index 77202bf..3acf1bc 100755 --- a/test/tagging +++ b/test/tagging @@ -38,4 +38,12 @@ test_expect_equal "$output" "\ thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (:\" inbox tag1 unread) thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)" +test_begin_subtest "Tagging order" +notmuch tag +tag4 -tag4 One +notmuch tag -tag4 +tag4 Two +output=$(notmuch search \* | notmuch_search_sanitize) +test_expect_equal "$output" "\ +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (:\" inbox tag1 tag4 unread) +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)" + test_done -- 1.7.5.4
[PATCH v3 0/4] cli: notmuch tag/restore refactoring
v3 of id:"cover.1332702915.git.jani at nikula.org" with the following mostly non-functional changes: - add test for the current tagging behaviour in patch 1, and change the test in patch 2 when the behaviour is changed - handle the no tag changes case in _optimize_tag_query() in patch 2 - add braces around the "tag_ops[tag_ops_count].remove = (argv[i][0] == '-');" assignment - document the tag_query() function a bit in patch 3 - make tag_message() static in patch 4 BR, Jani. Jani Nikula (4): test: add test for both adding and removing a tag at the same time cli: refactor "notmuch tag" data structures for tagging operations cli: refactor "notmuch tag" query tagging into a separate function cli: refactor "notmuch restore" message tagging into a separate function notmuch-restore.c | 148 - notmuch-tag.c | 173 - test/tagging |8 +++ 3 files changed, 178 insertions(+), 151 deletions(-) -- 1.7.5.4
Bug#628018: [PATCH] notmuch-mutt utility for notmuch/contrib/
On Mon, Mar 26, 2012 at 01:03:32PM -0600, Scott Barker wrote: > FYI, I use the following in my .muttrc, which includes an expansion of $HOME > in , and it works fine: > > macro index / "mutt-notmuch --prompt > search$HOME/.cache/mutt_results" > "notmuch search" Right, but I need support for variable expansions with defaults, because there's no guarantee that $XDG_CACHE_HOME is defined in user environment. I.e., I need the equivalent of shell ${name:-default} idiom. AFAICT that is not supported by Mutt natively. Hence the need of resorting to shell escaping. Hope this explains, -- Stefano Zacchiroli zack@{upsilon.cc,pps.jussieu.fr,debian.org} . o . Ma?tre de conf?rences .. http://upsilon.cc/zack .. . . o Debian Project Leader... @zack on identi.ca ...o o o ? the first rule of tautology club is the first rule of tautology club ?
Re: [PATCH] emacs: Fix mis-named argument to notmuch-get-bodypart-internal
On Tue, Mar 27 2012, Austin Clements amdra...@mit.edu wrote: Previously, this function took an argument called message-id, even though it was a general query, rather than a message ID. This changes it to query. --- +1 Tomi emacs/notmuch-lib.el |4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 2e367b5..4b17ecc 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -219,13 +219,13 @@ the given type. ;; Helper for parts which are generally not included in the default ;; JSON output. -(defun notmuch-get-bodypart-internal (message-id part-number process-crypto) +(defun notmuch-get-bodypart-internal (query part-number process-crypto) (let ((args '(show --format=raw)) (part-arg (format --part=%s part-number))) (setq args (append args (list part-arg))) (if process-crypto (setq args (append args '(--decrypt -(setq args (append args (list message-id))) +(setq args (append args (list query))) (with-temp-buffer (let ((coding-system-for-read 'no-conversion)) (progn -- 1.7.7.2 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 0/2] Escape message ID queries in Emacs
On Tue, Mar 27 2012, Austin Clements wrote: Currently, Emacs does not escape message ID queries and is inconsistent about quoting them. This patch centralizes this in one function that always produces a properly quoted and escaped message ID query. With this, Emacs no longer gets confused by Tomi's crazy message, id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi LGTM. Things work! One observation, though: In search bar the following queries return one match: id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi but id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi return 0 matches. It looks like the (above) search strings goes verbatim to command line i.e. 'notmuch' 'search' '--sort=oldest-first' 'id:...' can be used to compare... so that would be CLI issue if there is ever need to do do anything with it. +1 Thanks for fixing this. Tomi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 0/2] Escape message ID queries in Emacs
On Tue, 27 Mar 2012, Tomi Ollila tomi.oll...@iki.fi wrote: On Tue, Mar 27 2012, Austin Clements wrote: Currently, Emacs does not escape message ID queries and is inconsistent about quoting them. This patch centralizes this in one function that always produces a properly quoted and escaped message ID query. With this, Emacs no longer gets confused by Tomi's crazy message, id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi LGTM. Things work! One observation, though: In search bar the following queries return one match: id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi but id:id:1332281811-24710-2b-git-send-email-tomi.oll...@iki.fi return 0 matches. It looks like the (above) search strings goes verbatim to command line i.e. 'notmuch' 'search' '--sort=oldest-first' 'id:...' can be used to compare... so that would be CLI issue if there is ever need to do do anything with it. I'm not sure I would consider this a CLI issue. It may not be intuitive, but it is an intentional part of the Xapian query parser's grammar. If a boolean prefix is followed immediately by a quote, then the term is between that quote and the next quote, modulo an escaping rule that lets you include a literal quote in the term by doubling it in the query string. Together, these rules let you reliably put anything in a boolean search term. +1 Thanks for fixing this. Tomi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [RFC] Split notmuch_database_close into two functions
You're right of course, updated patch sent as a follow up. Justus ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/7] Split notmuch_database_close into two functions
Formerly notmuch_database_close closed the xapian database and destroyed the talloc structure associated with the notmuch database object. Split notmuch_database_close into notmuch_database_close and notmuch_database_destroy. This makes it possible for long running programs to close the xapian database and thus release the lock associated with it without destroying the data structures obtained from it. This also makes the api more consistent since every other data structure has a destructor function. Signed-off-by: Justus Winter 4win...@informatik.uni-hamburg.de --- lib/database.cc | 14 -- lib/notmuch.h | 15 +++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 16c4354..2fefcad 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -642,7 +642,7 @@ notmuch_database_open (const char *path, read-write mode.\n, notmuch_path, version, NOTMUCH_DATABASE_VERSION); notmuch-mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; goto DONE; } @@ -702,7 +702,7 @@ notmuch_database_open (const char *path, } catch (const Xapian::Error error) { fprintf (stderr, A Xapian exception occurred opening database: %s\n, error.get_msg().c_str()); - notmuch_database_close (notmuch); + notmuch_database_destroy (notmuch); notmuch = NULL; } @@ -738,9 +738,19 @@ notmuch_database_close (notmuch_database_t *notmuch) } delete notmuch-term_gen; +notmuch-term_gen = NULL; delete notmuch-query_parser; +notmuch-query_parser = NULL; delete notmuch-xapian_db; +notmuch-xapian_db = NULL; delete notmuch-value_range_processor; +notmuch-value_range_processor = NULL; +} + +void +notmuch_database_destroy (notmuch_database_t *notmuch) +{ +notmuch_database_close (notmuch); talloc_free (notmuch); } diff --git a/lib/notmuch.h b/lib/notmuch.h index babd208..2fb4e70 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -133,7 +133,7 @@ typedef struct _notmuch_filenames notmuch_filenames_t; * * After a successful call to notmuch_database_create, the returned * database will be open so the caller should call - * notmuch_database_close when finished with it. + * notmuch_database_destroy when finished with it. * * The database will not yet have any data in it * (notmuch_database_create itself is a very cheap function). Messages @@ -165,7 +165,7 @@ typedef enum { * An existing notmuch database can be identified by the presence of a * directory named .notmuch below 'path'. * - * The caller should call notmuch_database_close when finished with + * The caller should call notmuch_database_destroy when finished with * this database. * * In case of any failure, this function returns NULL, (after printing @@ -175,11 +175,18 @@ notmuch_database_t * notmuch_database_open (const char *path, notmuch_database_mode_t mode); -/* Close the given notmuch database, freeing all associated - * resources. See notmuch_database_open. */ +/* Close the given notmuch database. + * + * This function is called by notmuch_database_destroy and can be + * called multiple times. */ void notmuch_database_close (notmuch_database_t *database); +/* Destroy the notmuch database freeing all associated + * resources */ +void +notmuch_database_destroy (notmuch_database_t *database); + /* Return the database path of the given database. * * The return value is a string owned by notmuch so should not be -- 1.7.9.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: Unofficial notmuch wiki concerns
On Mon, 26 Mar 2012 12:39:47 -0500, Kyle Sexton k...@mocker.org wrote: My goal is to make a site where the documentation is easy to update, and attractive. My concern is having too many places out there for information. I tried to assuage that with the disclaimer pointing to the canonical site. Does the community think there is space for an unofficial wiki like this? [ For those of you following along at home, notmuchuchmail.org is an ikiwiki instance, writable by anonymous git push ] First, it's great that you want to give something back to the notmuch project. As a project, I think we should be primarily concerned with the quality of the official documentation that ships with the software, but I recognize that some things are too transient or specialized to fit in the official docs. Personally, I would much rather you help improve the notmuch wiki than fork it. If you want to have your personal site, like a blog, of course that's fine. But if you expect other people to contribute to your wiki instead of the official one (and the generic name kind of suggests that you do, as compared to mocker.org), then I think it isn't helpful. The set of contributors to the wiki (like the overall notmuch community) is extremely small, and splitting that will decrease the quality of both splits. If there are technical barriers preventing contributing to the notmuch wiki, then the folk on #notmuch would be happy to help with that. It might just be that you prefer some other wiki software; unfortunately these are the sorts of compromises needed to make free software projects (and other projects) work. Or you can agitate to have the project change it's wiki, if you feel that strongly about it. David pgpgMUj3oOA36.pgp Description: PGP signature ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
bug in emacs reply code?
Hi all, just upgraded from 0.11 to master on one machine, and emacs reply started failing as follows. The four tests fail, all others pass. $ emacs --version GNU Emacs 23.2.1 $ make test [...] emacs: Testing emacs interface [...] FAIL Reply within emacs --- emacs.24.expected 2012-03-27 15:02:05.894335772 + +++ emacs.24.output2012-03-27 15:02:05.884335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite test_su...@notmuchmail.org +From: Notmuch Test Suite test_su...@notmuchmail.org +To: u...@example.com To: u...@example.com Subject: Re: Testing message sent via SMTP +Subject: Re: Testing message sent via SMTP In-Reply-To: XXX Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Reply within emacs to a multipart/mixed message --- emacs.25.expected 2012-03-27 15:02:06.294335772 + +++ emacs.25.output 2012-03-27 15:02:06.294335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite test_su...@notmuchmail.org +From: Notmuch Test Suite test_su...@notmuchmail.org +To: Adrian Perez de Castro ape...@igalia.com, notmuch@notmuchmail.org To: Adrian Perez de Castro ape...@igalia.com, notmuch@notmuchmail.org Subject: Re: [notmuch] Introducing myself +Subject: Re: [notmuch] Introducing myself In-Reply-To: 20091118002059.067214ed@hikari Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Reply within emacs to a multipart/alternative message --- emacs.26.expected 2012-03-27 15:02:06.914335772 + +++ emacs.26.output 2012-03-27 15:02:06.914335772 + @@ -1,6 +1,9 @@ From: Notmuch Test Suite test_su...@notmuchmail.org +From: Notmuch Test Suite test_su...@notmuchmail.org +To: Alex Botero-Lowry alex.boterolo...@gmail.com, notmuch@notmuchmail.org To: Alex Botero-Lowry alex.boterolo...@gmail.com, notmuch@notmuchmail.org Subject: Re: [notmuch] preliminary FreeBSD support +Subject: Re: [notmuch] preliminary FreeBSD support In-Reply-To: cf0c4d610911171136h1713aa59w9cf9aa31f052a...@mail.gmail.com Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- FAIL Quote MML tags in reply --- emacs.27.expected 2012-03-27 15:02:07.524335772 + +++ emacs.27.output 2012-03-27 15:02:07.524335772 + @@ -1,6 +1,8 @@ From: Notmuch Test Suite test_su...@notmuchmail.org +From: Notmuch Test Suite test_su...@notmuchmail.org To: Subject: Re: Quote MML tags in reply +Subject: Re: Quote MML tags in reply In-Reply-To: test-emacs-mml-quot...@message.id Fcc: /home/jani/tools/notmuch/test/tmp.emacs/mail/sent --text follows this line-- [...] ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3 0/4] cli: notmuch tag/restore refactoring
On Tue, Mar 27 2012, Jani Nikula j...@nikula.org wrote: v3 of id:cover.1332702915.git.j...@nikula.org with the following mostly non-functional changes: - add test for the current tagging behaviour in patch 1, and change the test in patch 2 when the behaviour is changed - handle the no tag changes case in _optimize_tag_query() in patch 2 - add braces around the tag_ops[tag_ops_count].remove = (argv[i][0] == '-'); assignment - document the tag_query() function a bit in patch 3 - make tag_message() static in patch 4 LGTM. BR, Jani. Tomi Jani Nikula (4): test: add test for both adding and removing a tag at the same time cli: refactor notmuch tag data structures for tagging operations cli: refactor notmuch tag query tagging into a separate function cli: refactor notmuch restore message tagging into a separate function notmuch-restore.c | 148 - notmuch-tag.c | 173 - test/tagging |8 +++ 3 files changed, 178 insertions(+), 151 deletions(-) -- 1.7.5.4 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Goto command for existing search windows
I was looking for a function which would find a buffer based on one of my saved searches, and perform the search if it didn't exist. I've gotten it a bit closer, if I perform the search that matches a saved search, then this routine will find it because of the magic in notmuch-search-buffer-title, but perhaps someone else feels up to searching through the saved searches directly? (defun notmuch-goto-or-search (optional query) Find a notmuch-search buffer with the given query, or run \notmuch search\ with the given `query' and display results. If `query' is nil, it is read interactively from the minibuffer. (interactive) (if (null query) (setq query (notmuch-read-query Notmuch goto-or-search: ))) (let ((buffer-name (notmuch-search-buffer-title query))) (setq buf (get-buffer buffer-name))) (if (not buf) (notmuch-search query) (switch-to-buffer buf) ))) I then use it something like this: (global-set-key [C-f1] (lambda () (interactive) (notmuch-goto-or-search tag:inbox and tag:unread and not tag:deleted))) (global-set-key [C-f2] (lambda () (interactive) (notmuch-goto-or-search tag:inbox and not tag:deleted))) (global-set-key [C-f3] 'notmuch) (global-set-key [C-f6] (lambda () (interactive) (notmuch-goto-or-search tag:todo and not tag:deleted))) It would be better if I could use my Inbox, INBOX and todo names for the saved searches, but how to do that without breaking generality of searching the body of the email? Do I have to define my own ss: (saved search) prefix or something, as I believe some others have? This is what I'm willing to do today, and it works for me, I could patch notmuch.el, but I wondered about answering the other questions. Also, some elisp master could hint about how to make the binding not so ugly. ;) Another appreciated elisp hint would be how to get the buf variable to go inside the let, I keep getting complaints about buffer-name not being defined, thus the ugly setq, which works. Enjoy, -Mark ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 0/3] Rewrite default reply format
Thanks for the review. New version coming shortly... Quoth Tomi Ollila on Mar 24 at 12:06 pm: Austin Clements amdra...@mit.edu writes: The default reply format is the last bastion of the old message formatter style. This series converts it to the new self-recursive style. After this, there will be one last series to rip out the compatibility code and do final cleanup. Works fine, patches look good... just 2 spacing questions: in id:1332473647-9133-2-git-send-email-amdra...@mit.edu + typedef enum { + NOTMUCH_SHOW_TEXT_PART_REPLY = 10, + } notmuch_show_text_part_flags; Should this be like: NOTMUCH_SHOW_TEXT_PART_REPLY = (1 0), Changed to NOTMUCH_SHOW_TEXT_PART_REPLY = 1 0 to be consistent with operator spacing. I left out the parens since they aren't necessary. and this + * If flagsNOTMUCH_SHOW_TEXT_PART_REPLY, this prepends to each + * output line. + * like: + * If flags NOTMUCH_SHOW_TEXT_PART_REPLY, this prepends to each Changed. On this one I put in parens to better distinguish it from the surrounding prose. Tomi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 0/3] Rewrite default reply format
This version fixes two minor formatting issues that Tomi pointed out [1]. There are no other changes. [1] id:m2d382ia9d@guru.guru-group.fi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 3/3] reply: Move reply citation printing to the recursive MIME walk
This makes more logical sense, since it makes the recursive printer responsible for the entire reply body and lets it start at the root of the MIME tree instead of the first child. (We could move reply header creation in there, too, but if we ever support proper reply to multiple messages, we'll want just one set of reply headers computed from the entire message set and many bodies.) --- notmuch-reply.c | 12 ++-- 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 84a1220..0949d9f 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -47,7 +47,11 @@ format_part_reply (mime_node_t *node) { int i; -if (GMIME_IS_MESSAGE (node-part)) { +if (node-envelope_file) { + printf (On %s, %s wrote:\n, + notmuch_message_get_header (node-envelope_file, date), + notmuch_message_get_header (node-envelope_file, from)); +} else if (GMIME_IS_MESSAGE (node-part)) { GMimeMessage *message = GMIME_MESSAGE (node-part); InternetAddressList *recipients; const char *recipients_string; @@ -540,13 +544,9 @@ notmuch_reply_format_default(void *ctx, g_object_unref (G_OBJECT (reply)); reply = NULL; - printf (On %s, %s wrote:\n, - notmuch_message_get_header (message, date), - notmuch_message_get_header (message, from)); - if (mime_node_open (ctx, message, params-cryptoctx, params-decrypt, root) == NOTMUCH_STATUS_SUCCESS) { - format_part_reply (mime_node_child (root, 0)); + format_part_reply (root); talloc_free (root); } -- 1.7.9.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 2/3] reply: Convert default reply format to self-recursive style
This re-arranges the default reply formatter code to use the mime_node_t abstraction. There are no semantic changes. --- notmuch-reply.c | 123 +-- 1 files changed, 47 insertions(+), 76 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 2f5ed3d..84a1220 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -24,28 +24,6 @@ #include gmime-filter-headers.h static void -reply_headers_message_part (GMimeMessage *message); - -static void -reply_part_content (GMimeObject *part); - -static const notmuch_show_format_t format_reply = { -, NULL, - , NULL, - , NULL, reply_headers_message_part, \n, - , - NULL, - NULL, - NULL, - reply_part_content, - NULL, - , - , - , , - -}; - -static void show_reply_headers (GMimeMessage *message) { GMimeStream *stream_stdout = NULL, *stream_filter = NULL; @@ -65,66 +43,55 @@ show_reply_headers (GMimeMessage *message) } static void -reply_headers_message_part (GMimeMessage *message) +format_part_reply (mime_node_t *node) { -InternetAddressList *recipients; -const char *recipients_string; - -printf ( From: %s\n, g_mime_message_get_sender (message)); -recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); -recipients_string = internet_address_list_to_string (recipients, 0); -if (recipients_string) - printf ( To: %s\n, - recipients_string); -recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); -recipients_string = internet_address_list_to_string (recipients, 0); -if (recipients_string) - printf ( Cc: %s\n, - recipients_string); -printf ( Subject: %s\n, g_mime_message_get_subject (message)); -printf ( Date: %s\n, g_mime_message_get_date_as_string (message)); -} - - -static void -reply_part_content (GMimeObject *part) -{ -GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part)); -GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (part); +int i; -if (g_mime_content_type_is_type (content_type, multipart, *) || - g_mime_content_type_is_type (content_type, message, rfc822)) -{ - /* Output nothing, since multipart subparts will be handled individually. */ -} -else if (g_mime_content_type_is_type (content_type, application, pgp-encrypted) || -g_mime_content_type_is_type (content_type, application, pgp-signature)) -{ - /* Ignore PGP/MIME cruft parts */ -} -else if (g_mime_content_type_is_type (content_type, text, *) - !g_mime_content_type_is_type (content_type, text, html)) -{ - GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - show_text_part_content (part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); - g_object_unref(stream_stdout); -} -else -{ - if (disposition - strcmp (disposition-disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) - { - const char *filename = g_mime_part_get_filename (GMIME_PART (part)); +if (GMIME_IS_MESSAGE (node-part)) { + GMimeMessage *message = GMIME_MESSAGE (node-part); + InternetAddressList *recipients; + const char *recipients_string; + + printf ( From: %s\n, g_mime_message_get_sender (message)); + recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); + recipients_string = internet_address_list_to_string (recipients, 0); + if (recipients_string) + printf ( To: %s\n, + recipients_string); + recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); + recipients_string = internet_address_list_to_string (recipients, 0); + if (recipients_string) + printf ( Cc: %s\n, + recipients_string); + printf ( Subject: %s\n, g_mime_message_get_subject (message)); + printf ( Date: %s\n, g_mime_message_get_date_as_string (message)); + printf (\n); +} else if (GMIME_IS_PART (node-part)) { + GMimeContentType *content_type = g_mime_object_get_content_type (node-part); + GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (node-part); + + if (g_mime_content_type_is_type (content_type, application, pgp-encrypted) || + g_mime_content_type_is_type (content_type, application, pgp-signature)) { + /* Ignore PGP/MIME cruft parts */ + } else if (g_mime_content_type_is_type (content_type, text, *) + !g_mime_content_type_is_type (content_type, text, html)) { + GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); + g_mime_stream_file_set_owner
[PATCH v2 1/3] show/reply: Unify the code that extracts text parts
Previously, show and reply had separate implementations of decoding and printing text parts. Now both use show's implementation, which was more complete. Show's implementation has been extended with an option to add reply quoting to the extracted part (this is implemented as a named flag to avoid naked booleans, even though it's the only flag it can take). --- notmuch-client.h |8 notmuch-reply.c | 28 notmuch-show.c | 23 +++ 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index fa04fa2..203ac49 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -197,6 +197,14 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first); void format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply); +typedef enum { +NOTMUCH_SHOW_TEXT_PART_REPLY = 1 0, +} notmuch_show_text_part_flags; + +void +show_text_part_content (GMimeObject *part, GMimeStream *stream_out, + notmuch_show_text_part_flags flags); + char * json_quote_chararray (const void *ctx, const char *str, const size_t len); diff --git a/notmuch-reply.c b/notmuch-reply.c index e2b6c25..2f5ed3d 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -21,7 +21,6 @@ */ #include notmuch-client.h -#include gmime-filter-reply.h #include gmime-filter-headers.h static void @@ -106,29 +105,10 @@ reply_part_content (GMimeObject *part) else if (g_mime_content_type_is_type (content_type, text, *) !g_mime_content_type_is_type (content_type, text, html)) { - GMimeStream *stream_stdout = NULL, *stream_filter = NULL; - GMimeDataWrapper *wrapper; - const char *charset; - - charset = g_mime_object_get_content_type_parameter (part, charset); - stream_stdout = g_mime_stream_file_new (stdout); - if (stream_stdout) { - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - stream_filter = g_mime_stream_filter_new(stream_stdout); - if (charset) { - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), -g_mime_filter_charset_new(charset, UTF-8)); - } - } - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), -g_mime_filter_reply_new(TRUE)); - wrapper = g_mime_part_get_content_object (GMIME_PART (part)); - if (wrapper stream_filter) - g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); - if (stream_filter) - g_object_unref(stream_filter); - if (stream_stdout) - g_object_unref(stream_stdout); + GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); + g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); + show_text_part_content (part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); + g_object_unref(stream_stdout); } else { diff --git a/notmuch-show.c b/notmuch-show.c index ff9d427..0bf5e21 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -19,6 +19,7 @@ */ #include notmuch-client.h +#include gmime-filter-reply.h static notmuch_status_t format_part_text (const void *ctx, mime_node_t *node, @@ -247,13 +248,17 @@ format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t repl /* Write a MIME text part out to the given stream. * + * If (flags NOTMUCH_SHOW_TEXT_PART_REPLY), this prepends to + * each output line. + * * Both line-ending conversion (CRLF-LF) and charset conversion ( - * UTF-8) will be performed, so it is inappropriate to call this * function with a non-text part. Doing so will trigger an internal * error. */ -static void -show_text_part_content (GMimeObject *part, GMimeStream *stream_out) +void +show_text_part_content (GMimeObject *part, GMimeStream *stream_out, + notmuch_show_text_part_flags flags) { GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part)); GMimeStream *stream_filter = NULL; @@ -286,6 +291,16 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out) } +if (flags NOTMUCH_SHOW_TEXT_PART_REPLY) { + GMimeFilter *reply_filter; + reply_filter = g_mime_filter_reply_new (TRUE); + if (reply_filter) { + g_mime_stream_filter_add (GMIME_STREAM_FILTER (stream_filter), + reply_filter); + g_object_unref (reply_filter); + } +} + wrapper = g_mime_part_get_content_object (GMIME_PART (part)); if (wrapper stream_filter) g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); @@ -532,7 +547,7 @@ format_part_text (const void *ctx, mime_node_t *node, { GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); g_mime_stream_file_set_owner
[Joerg Jaspert] Bug#666027: notmuch: get a quiet option
---BeginMessage--- Package: notmuch Version: 0.12-1 Severity: wishlist Dear Maintainer, a feature wish to get to upstream: as the subject says, it would be nice if notmuch gets a quiet option. Especially for notmuch new that is. Its printing out lots of Note: Ignoring blah and Processed blubb in almost no time and similar useless stuff. Fine if it likes itself, and heck i dont care if it ignores files, its not the only one playing in this maildir, so a possibility to make it shutup would be nice. -- bye, Joerg Maulkin db.oftc.net? * Maulkin thinks Ganneff is doing evil ldap stuff again Maulkin It's called the Ganneff fingerprint Maulkin If somethign has 'db.foo.blah' it's had Ganneff involved ---End Message--- ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch