From 5ef009f1813b3c0e224a3115bfd0edb2571290ae Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 13 Nov 2013 21:08:52 +0100 Subject: [PATCH] Re: [PATCH] notmuch: Add "maildir:" search option --- 26/29ce253bd24b814ae4cacc841a8703ff007641 | 282 ++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 26/29ce253bd24b814ae4cacc841a8703ff007641 diff --git a/26/29ce253bd24b814ae4cacc841a8703ff007641 b/26/29ce253bd24b814ae4cacc841a8703ff007641 new file mode 100644 index 000000000..7edbe7b7c --- /dev/null +++ b/26/29ce253bd24b814ae4cacc841a8703ff007641 @@ -0,0 +1,282 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 3C0FD431E82 + for ; Wed, 13 Nov 2013 12:09:02 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -2.3 +X-Spam-Level: +X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id svZTLYEtPkGu for ; + Wed, 13 Nov 2013 12:08:56 -0800 (PST) +Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id 71331431FD0 + for ; Wed, 13 Nov 2013 12:08:56 -0800 (PST) +Received: from dhcp-077-248-225-117.chello.nl ([77.248.225.117] helo=laptop) + by merlin.infradead.org with esmtpsa (Exim 4.80.1 #2 (Red Hat Linux)) + id 1VggkA-0004CF-5F; Wed, 13 Nov 2013 20:08:54 +0000 +Received: by laptop (Postfix, from userid 1000) + id 5CD05103BA2D3; Wed, 13 Nov 2013 21:08:52 +0100 (CET) +Date: Wed, 13 Nov 2013 21:08:52 +0100 +From: Peter Zijlstra +To: Austin Clements +Subject: Re: [PATCH] notmuch: Add "maildir:" search option +Message-ID: <20131113200852.GG16796@laptop.programming.kicks-ass.net> +References: <20131112155637.GA16796@laptop.programming.kicks-ass.net> + <87mwl94dte.fsf@awakening.csail.mit.edu> + <87k3gd4dfb.fsf@awakening.csail.mit.edu> +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline +In-Reply-To: <87k3gd4dfb.fsf@awakening.csail.mit.edu> +User-Agent: Mutt/1.5.21 (2012-12-30) +Cc: notmuch@notmuchmail.org +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Wed, 13 Nov 2013 20:09:02 -0000 + +On Tue, Nov 12, 2013 at 02:39:52PM -0500, Austin Clements wrote: +> On Tue, 12 Nov 2013, Austin Clements wrote: +> > I think this is a great idea. Personally I think this is how folder: +> > should work. I find the semantics of folder: to be useless except where +> > they happen to coincide with the boolean semantics used here. +> > Unfortunately, changing folder: would require versioning the database, +> > which we have only primordial support for right now. +> > +> > Various comments below, though nothing major. Of course, we'd also need +> > some tests and man page updates for this. +> +> Sorry, one important thing I missed: this doesn't correctly handle when +> file names are removed from a message +> (_notmuch_message_remove_filename). Probably the simplest thing would +> be to follow the template for how folder: works by first removing *all* +> folder terms and then adding back the still-valid ones. (Unfortunately, +> just removing the term for the removed filename's directory won't work +> because the message could have other filenames in the same directory, +> though maybe you could just scan for that possibility?) + +Oh, right you are. A little something like the below? Its compile tested +only and I've not yet had time to look at how the test infrastructure +works. + + +--- + lib/database.cc | 3 +- + lib/message.cc | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 104 insertions(+), 10 deletions(-) + +diff --git a/lib/database.cc b/lib/database.cc +index a021bf17253c..e43e17dffcd0 100644 +--- a/lib/database.cc ++++ b/lib/database.cc +@@ -208,7 +208,8 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { + { "thread", "G" }, + { "tag", "K" }, + { "is", "K" }, +- { "id", "Q" } ++ { "id", "Q" }, ++ { "maildir", "XMAILDIR:" }, + }; + + static prefix_t PROBABILISTIC_PREFIX[]= { +diff --git a/lib/message.cc b/lib/message.cc +index 1b4637950f8e..73d3bb65ab67 100644 +--- a/lib/message.cc ++++ b/lib/message.cc +@@ -22,6 +22,7 @@ + #include "database-private.h" + + #include ++#include + + #include + +@@ -473,6 +474,71 @@ notmuch_message_get_replies (notmuch_message_t *message) + return _notmuch_messages_create (message->replies); + } + ++/* Construct a proper 'maildir' from 'directory' ++ * ++ * Takes the relative directory component inside the maildir pathname and ++ * construct a maildir path from it. ++ * ++ * For filesystem layout Maildir we use the regular filesystem path except the ++ * trailing "cur"/"new" component. ++ * ++ * For Maildir++ we strip the leading '.' and replace subsequent '.'s with '/'s ++ */ ++static char * ++_notmuch_message_maildir (void *ctx, const char *directory) ++{ ++ char *maildir; ++ int i; ++ ++ maildir = talloc_strdup (ctx, directory); ++ i = strlen (maildir); ++ ++ /* Strip trailing '/' */ ++ while (i && maildir[i - 1] == '/') { ++ maildir[i - 1] = '\0'; ++ i--; ++ } ++ ++ /* Strip leading '/' */ ++ while (maildir[0] == '/') { ++ maildir++; ++ i--; ++ } ++ ++ if (i >= 3) { ++ /* Consume trailing maildir directory entries */ ++ if (STRNCMP_LITERAL (maildir, "cur") == 0 || ++ STRNCMP_LITERAL (maildir, "new") == 0) ++ { ++ maildir[i - 3] = '\0'; ++ i -= 3; ++ } ++ ++ /* Strip trailing '/' */ ++ while (i && maildir[i - 1] == '/') { ++ maildir[i-1] = '\0'; ++ i--; ++ } ++ } ++ ++ /* Maildir++ */ ++ if (maildir[0] == '.') { ++ maildir++; ++ ++ /* Replace all remaining '.' with '/' */ ++ for (i = 0; maildir[i]; i++) { ++ if (maildir[i] == '.') ++ maildir[i] = '/'; ++ } ++ } ++ ++ /* If there's no string left, we're the "INBOX" */ ++ if (maildir[0] == '\0') ++ maildir = talloc_strdup (ctx, "INBOX"); ++ ++ return maildir; ++} ++ + /* Add an additional 'filename' for 'message'. + * + * This change will not be reflected in the database until the next +@@ -485,6 +551,7 @@ _notmuch_message_add_filename (notmuch_message_t *message, + notmuch_status_t status; + void *local = talloc_new (message); + char *direntry; ++ char *maildir; + + if (filename == NULL) + INTERNAL_ERROR ("Message filename cannot be NULL."); +@@ -507,6 +574,10 @@ _notmuch_message_add_filename (notmuch_message_t *message, + /* New terms allow user to search with folder: specification. */ + _notmuch_message_gen_terms (message, "folder", directory); + ++ /* New terms allow user to serarch with maildir: specification. */ ++ maildir = _notmuch_message_maildir (local, directory); ++ _notmuch_message_add_term (message, "maildir", maildir); ++ + talloc_free (local); + + return NOTMUCH_STATUS_SUCCESS; +@@ -535,11 +606,18 @@ _notmuch_message_remove_filename (notmuch_message_t *message, + void *local = talloc_new (message); + char *zfolder_prefix = talloc_asprintf(local, "Z%s", folder_prefix); + int zfolder_prefix_len = strlen (zfolder_prefix); +- char *direntry; ++ const char *relative, *directory; ++ char *direntry, *maildir; + notmuch_private_status_t private_status; + notmuch_status_t status; + Xapian::TermIterator i, last; + ++ relative = _notmuch_database_relative_path (message->notmuch, filename); ++ ++ status = _notmuch_database_split_path (local, relative, &directory, NULL); ++ if (status) ++ return status; ++ + status = _notmuch_database_filename_to_direntry ( + local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry); + if (status || !direntry) +@@ -553,12 +631,21 @@ _notmuch_message_remove_filename (notmuch_message_t *message, + if (status) + return status; + +- /* Re-synchronize "folder:" terms for this message. This requires: +- * 1. removing all "folder:" terms +- * 2. removing all "folder:" stemmed terms +- * 3. adding back terms for all remaining filenames of the message. */ +- +- /* 1. removing all "folder:" terms */ ++ /* Re-synchronize "folder:" and "maildir:" terms for this message. This ++ * requires: ++ * 1. removing "maildir:" for this filename ++ * 2. removing all "folder:" terms ++ * 3. removing all "folder:" stemmed terms ++ * ++ * For all remaining filenames of the message: ++ * 4. adding back "folder:" terms ++ * 5. adding back "maildir:" */ ++ ++ /* 1. remove "maildir:" for this message */ ++ maildir = _notmuch_message_maildir (local, directory); ++ _notmuch_message_remove_term (message, "maildir", maildir); ++ ++ /* 2. removing all "folder:" terms */ + while (1) { + i = message->doc.termlist_begin (); + i.skip_to (folder_prefix); +@@ -577,7 +664,7 @@ _notmuch_message_remove_filename (notmuch_message_t *message, + } + } + +- /* 2. removing all "folder:" stemmed terms */ ++ /* 3. removing all "folder:" stemmed terms */ + while (1) { + i = message->doc.termlist_begin (); + i.skip_to (zfolder_prefix); +@@ -596,7 +683,7 @@ _notmuch_message_remove_filename (notmuch_message_t *message, + } + } + +- /* 3. adding back terms for all remaining filenames of the message. */ ++ /* for all remaining filenames of the message */ + i = message->doc.termlist_begin (); + i.skip_to (direntry_prefix); + +@@ -623,8 +710,14 @@ _notmuch_message_remove_filename (notmuch_message_t *message, + directory = _notmuch_database_get_directory_path (local, + message->notmuch, + directory_id); ++ ++ /* 4. adding back "folder:" terms */ + if (strlen (directory)) + _notmuch_message_gen_terms (message, "folder", directory); ++ ++ /* 5. adding back "maildir:" */ ++ maildir = _notmuch_message_maildir (local, directory); ++ _notmuch_message_add_term (message, "maildir", maildir); + } + + talloc_free (local); -- 2.26.2