[PATCH] doc: document notmuch new --verbose

2019-03-29 Thread Michal Sojka
---
 doc/man1/notmuch-new.rst | 4 
 1 file changed, 4 insertions(+)

diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 5eeb4926..20a1103b 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,6 +43,10 @@ Supported options for **new** include
 ``--quiet``
 Do not print progress or results.
 
+``--verbose``
+Print file names being processed. Ignored when combined with
+``--quiet``.
+
 ``--decrypt=(true|nostash|auto|false)``
 If ``true``, when encountering an encrypted message, try to
 decrypt it while indexing, and stash any discovered session keys.
-- 
2.20.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Failure to report send errors

2018-03-29 Thread Michal Sojka
Dear Gnus developers,

(CCing notmuch, because their users can be affected too)

When message-interactive is nil, message-send can falsely report
success even when sending fails and there is no way for the user to
learn about the failure, expect from angry peers complaining about not
receiving replies.

The function responsible for this problematic behavior is
message-send-mail-with-sendmail. It works in two modes: interactive
and non-interactive depending on the value of message-interactive. The
problem happens in the non-interactive mode (which is not the
default).

It executes sendmail with -odb -oem, which means that delivery should
happen in background and errors should be reported to the user via
email. sendmail is executed via call-process-region called without
BUFFER argument, meaning that the return value is always nil. If
sendmail fails to execute at all, perhaps due to corrupted configuration
file, neither the message nor any error email is ever sent, but the user
sees false "Sending...done" message.

I think that call-process-region should be always called with a BUFFER
so that sendmail exit code is returned and communicated to the user.

Best regards,
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: Long delay when opening signed emails

2018-01-30 Thread Michal Sojka
On Tue, Jan 30 2018, Daniel Kahn Gillmor wrote:
> Hi Michal--
>
> On Tue 2018-01-30 17:17:54 +0100, Michal Sojka wrote:
>> Hi all,
>>
>> I experience annoyingly long delay, when opening some signed emails in
>> Emacs. This is likely related to the following lines appearing in my
>> log when opening the email:
>>
>> Jan 30 17:07:46 dirmngr[7526]: no CRL available for issuer id 
>> A401B7A860C859FEA90E1A7EEE2BAF37C7FB918F
>> Jan 30 17:08:06 dirmngr[7526]: resolving 'crl3.digicert.com' failed: Server 
>> indicated a failure
>> Jan 30 17:08:06 dirmngr[7526]: can't connect to 'crl3.digicert.com': host 
>> not found
>> Jan 30 17:08:06 dirmngr[7526]: error retrieving 
>> 'http://crl3.digicert.com/TERENAeSciencePersonalCA3.crl': Server indicated a 
>> failure
>> Jan 30 17:08:06 dirmngr[7526]: crl_fetch via DP failed: Server indicated a 
>> failure
>> Jan 30 17:08:06 dirmngr[7526]: command 'ISVALID' failed: Server indicated a 
>> failure
>>
>> I don't understand why resolving crl3.digicert.com fails, because it
>> works from command line.
>
> I think the e-mail in question is S/MIME-signed.  is that right?

Yes, that's correct.

> It looks like dirmngr is having some problems with network connectivity
> -- perhaps it has the wrong information about DNS resolvers?
>
> as a workaround, have you tried terminating dirmngr to let it restart
> when needed?  you can do that with:
>
> gpgconf --kill dirmngr
>
> (it should respawn automatically as needed)

That didn't help.

>> Any suggestions how to solve the failure or at least to get rid of the
>> delay?
>
> Apart from the workaround described above, if you decide that you'd
> rather avoid doing CRL checks in general (you might want that to avoid
> metadata leakage at least), you could put "disable-crl-checks" on its
> own line in ~/.gnupg/gpgsm.conf

Perfect, that prevents the delays.

> See also https://dev.gnupg.org/T3348 -- i'm asking upstream to default
> to False there.

Hmm, now I see that my problem is probably the same as in
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842291 referenced from
your GPG bug report.

Thank you.
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Long delay when opening signed emails

2018-01-30 Thread Michal Sojka
Hi all,

I experience annoyingly long delay, when opening some signed emails in
Emacs. This is likely related to the following lines appearing in my
log when opening the email:

Jan 30 17:07:46 dirmngr[7526]: no CRL available for issuer id 
A401B7A860C859FEA90E1A7EEE2BAF37C7FB918F
Jan 30 17:08:06 dirmngr[7526]: resolving 'crl3.digicert.com' failed: Server 
indicated a failure
Jan 30 17:08:06 dirmngr[7526]: can't connect to 'crl3.digicert.com': host not 
found
Jan 30 17:08:06 dirmngr[7526]: error retrieving 
'http://crl3.digicert.com/TERENAeSciencePersonalCA3.crl': Server indicated a 
failure
Jan 30 17:08:06 dirmngr[7526]: crl_fetch via DP failed: Server indicated a 
failure
Jan 30 17:08:06 dirmngr[7526]: command 'ISVALID' failed: Server indicated a 
failure

I don't understand why resolving crl3.digicert.com fails, because it
works from command line.

$ host crl3.digicert.com
crl3.digicert.com is an alias for cs9.wac.phicdn.net.
cs9.wac.phicdn.net has address 93.184.220.29

Any suggestions how to solve the failure or at least to get rid of the
delay?

Thanks
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] emacs: address completion, allow sender/recipient and filters

2016-05-18 Thread Michal Sojka
On Wed, May 18 2016, Mark Walters wrote:
> This commit lets the user customize the address completion. It makes
> two changes.

Thanks Mark, now it LGTM.

I configured my address completion to be based on received emails and
I'm surprised how many variations of my email address is used by
spammers :)

Cheers
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v3] emacs: address completion, allow sender/recipient and filters

2016-05-17 Thread Michal Sojka
Hi Mark,

few minor comments bellow.

On Mon, May 16 2016, Mark Walters wrote:
> This commit lets the user customize the address completion.

Add "It makes two changes."

> The first change controls whether to build the address completion list
> based on messages you have sent or you have received (the latter is
> much faster).
>
> The second change add a possible filter query to limit the messages
> used -- for example, setting this to date:1y..  would limit the
> address completions to addresses used in the last year. This speeds up
> the address harvest and may also make the search less cluttered as old
> addresses may well no longer be valid.
> ---
>
> This version uses the docstrings suggested my Michal (which are much
> better than mine), and renames some variables, as he suggested, to
> make the code clearer.
>
> I wondered about allowing the user to specify completion based on
> message "sent or received", rather than either sent, or received, but
> think that is adding too much mess. We could also allow completion
> based on any messages in the database, which would the include
> completion based on messages received via mailing lists or
> distribution lists.
>
> I also note that if the user enters a bad query into the filter query
> box (eg 6M.. rather than date:6M..) they may get an obscure error as
> notmuch/xapian fails. I don't see a goo way round that so have left
> that as a "don't do that" case.

Agreed.

>  emacs/notmuch-address.el | 119 
> ---
>  emacs/notmuch-company.el |   2 +-
>  2 files changed, 82 insertions(+), 39 deletions(-)
>
> diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
> index aafbe5f..3dc5da9 100644
> --- a/emacs/notmuch-address.el
> +++ b/emacs/notmuch-address.el
> @@ -28,15 +28,51 @@
>  ;;
>  (declare-function company-manual-begin "company")
>  
> -(defcustom notmuch-address-command 'internal
> -  "The command which generates possible addresses. It must take a
> -single argument and output a list of possible matches, one per
> -line. The default value of `internal' uses built-in address
> -completion."
> +(defvar notmuch-address-last-harvest 0
> +  "Time of last address harvest")
> +
> +(defvar notmuch-address-completions (make-hash-table :test 'equal)
> +  "Hash of email addresses for completion during email composition.
> +  This variable is set by calling `notmuch-address-harvest'.")
> +
> +(defvar notmuch-address-full-harvest-finished nil
> +  "t indicates that full completion address harvesting has been
> +finished")
> +
> +(defcustom notmuch-address-command '(sent nil)
> +  "Determines how to generate address completion candidates.
> +
> +If it is a string then that string should be an external program
> +which must take a single argument (searched string) and output a
> +list of completion candidates, one per line.
> +
> +Alternatively, it can be a (non-nil) list, in which case internal
> +completion is used; in this case the list should have form
> +'(DIRECTION FILTER), where DIRECTION is either sent or received
> +and specifies whether the candidates are searched in messages
> +sent by the user or received by the user (note received by is
> +much faster), and FILTER is either nil or a filter-string, such
> +as \"date:1y..\" to append to the query.
> +
> +If this variable is nil then address completion is disabled."
>:type '(radio
> -   (const :tag "Use internal address completion" internal)
> +   (list :tag "Use internal address completion"
> + (radio
> +  :tag "Base completion on messages you have"
> +  :value sent
> +  (const :tag "sent" sent)
> +  (const :tag "received" received))

I think, users will be more happy if they understand the difference
without reading the full doc string.

 (const :tag "sent (more accurate)" sent)
 (const :tag "received (faster)" received))


Thanks.
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: message-default-mail-headers not working in notmuch 0.22

2016-05-01 Thread Michal Sojka
On Fri, Apr 29 2016, David Edmondson wrote:
> On Fri, Apr 29 2016, Sebastian Fischmeister wrote:
>> After upgrading to notmuch 0.22, my emacs config seems broken:
>>
>> (setq message-default-mail-headers "Reply-to: m...@example.com\nBcc: 
>> m...@example.com")
>>
>> When creating a new mail, it has no header other than "To:" and
>> "Subject:".
>>
>> Since I cannot find any item in the NEWS related to this release, is
>> this expected behaviour?
>
> No, it's not intended. Please try this patch:
>
> diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
> index 0445975..399e138 100644
> --- a/emacs/notmuch-mua.el
> +++ b/emacs/notmuch-mua.el
> @@ -338,7 +338,10 @@ modified. This function is notmuch addaptation of
> ;; We need to convert any string input, eg from rmail-start-mail.
> (dolist (h other-headers other-headers)
>   (if (stringp (car h)) (setcar h (intern (capitalize (car h
> - (args (list yank-action send-actions)))
> + (args (list yank-action send-actions))
> + ;; Cause `message-setup-1' to do things relevant for mail,
> + ;; such as observe `message-default-mail-headers'.
> + (message-this-is-mail t))

Yes, this seems to be reasonable thing to do. Thanks.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: Mail merge with notmuch

2016-01-21 Thread Michal Sojka
Hi Sebastian,

On Sat, Jan 16 2016, Sebastian Fischmeister wrote:
> Hi,
>
> Is there a straightforward way to for mail merge with notmuch? I need to
> send emails with only minor modifications to a number of people. If I
> could send mails from the command line, then that would be perfect.
>
> Any ideas?

You can do this easily without notmuch. You need a locally configured
MTA such as sendmail or exim (which provides sendmail alias) and then
run something like:

cat people | while read EMAIL NAME; do
  sendmail -t <
To: $EMAIL
Subject: Foobar
 
Hello $NAME!
...
EOF
done

BR
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v6] devel/emacs: add devel/try-emacs-mua

2016-01-06 Thread Michal Sojka
On Wed, Jan 06 2016, Tomi Ollila wrote:
> devel/try-emacs-mua provides an easy way to try and experiment
> with the notmuch emacs client distributed in emacs subdirectory of
> the notmuch source tree.

LGTM. I tested it with various use cases I have it my mind and it works
as I expect.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 1/3] emacs: Fix mail composition under Emacs 23

2016-01-06 Thread Michal Sojka
On Tue, Jan 05 2016, David Bremner wrote:
> Michal Sojka <sojk...@fel.cvut.cz> writes:
>
>> +(when return-action (nconc args '(return-action)))
>> +(apply 'message-setup-1
>> +   ;; The following sexp is copied from `message-mail'
>
>> +   (nconc
>> +`((To . ,(or to "")) (Subject . ,(or subject "")))
>
> I missed this the first time, but I a bit worried about this used of
> nconc. It seems to fall under "A common pitfall is to use a quoted
> constant list as a non-last argument to ‘nconc’"  (from the elisp
> manual).  In any case it's not really performance critical code (I
> guess?) so maybe we could just use append?

Yes, this make sense. As the nconc is already in master, I'll not
combine the fix for this with 1/3, but I'm sending a separate patch that
applies after 3/3.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v5] devel/emacs: add devel/try-emacs-mua

2016-01-04 Thread Michal Sojka
Hi Tomi,

this looks almost good. A few minor comments below.

On Mon, Jan 04 2016, Tomi Ollila wrote:
> devel/try-emacs-mua provides an easy way to try and experiment
> with the notmuch emacs client distributed in emacs subdirectory of
> the notmuch source tree.
>
> try-emacs-mua starts a new emacs process and if initial checks pass
> *scratch* buffer is filled with information of how to begin.
>
> Normal emacs command line arguments can be used, like -q or -Q.
> These arguments are appended verbatim to the starting emacs process.
>
> If the emacs version in use is smaller than 24.4, special care is taken
> to ensure that notmuch*.elc files older than corresponding .el files
> are not loaded. Since emacs 24.4, setting `load-prefer-newer' variable
> takes care of this.
> ---
>
> Thanks to Michal for review of v4
> id:1450610032-23776-1-git-send-email-tomi.oll...@iki.fi
> in id:87vb7aco77@steelpick.2x.cz
>
> This should address the issues:
>
> 1) no more command line filtering
> 2) dropped (outcommented) file-truename advising
> 3) added information which notmuch binary is used
> 4) dropped popping messages buffer (fixing 2 issues)
>
> Added:
> * code to load ~/.emacs.d/notmuch-config.el if exists and not loaded already
> * pop messages buffer if (require 'notmuch) fails
> * don't suggest package-initialize if emacs version is 23
>
>
>  devel/try-emacs-mua | 156 
> 
>  1 file changed, 156 insertions(+)
>  create mode 100755 devel/try-emacs-mua
>
> diff --git a/devel/try-emacs-mua b/devel/try-emacs-mua
> new file mode 100755
> index ..0236b438c30a
> --- /dev/null
> +++ b/devel/try-emacs-mua
> @@ -0,0 +1,156 @@
> +#!/bin/sh
> +:; set -x; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit
> +;;
> +;; Try the notmuch emacs client located in ../notmuch/emacs directory
> +;;
> +;; Run this without arguments; emacs window opens with some usage information
> +;;
> +;; Authors: Tomi Ollila 
> +;;
> +;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point...
> +;;
> +;; Licence: GPLv3+
> +;;
> +
> +(message "Starting '%s'" load-file-name)
> +
> +(set-buffer "*scratch*")
> +
> +(setq initial-buffer-choice t) ;; *scratch* buffer

After initial tuning of emacs configuration according to the hints in
the scratch buffer, people might want to automate things. For example
something like:

devel/try-emacs-mua -q -f notmuch-hello

This does not work as one would expect, because the notmuch-hello buffer
is buried under the *scratch* buffer. To get the hello screen after the
start one has to run:

devel/try-emacs-mua -q -f notmuch-hello --eval '(setq initial-buffer-choice 
nil)'

This could be also documented in the scratch buffer. Or it might be
easier, in this case, to run emacs without this script at all. There
will be no logging, but this might be OK for some people.

Actually I don't know who are you writing this script for? For notmuch
developers or for users who want to debug or test development version? 

> +
> +(when (featurep 'notmuch)
> +  (insert "
> +Notmuch has been loaded to this emacs (during processing of the init file)
> +which means it is (most probably) loaded from different source than expected.
> +
> +Please run \"" (file-name-nondirectory load-file-name)
> +"\" with '-q' (or '-Q') as an argument, to disable
> +processing of the init file -- you can load it after emacs has started\n
> +exit emacs (y or n)? ")
> +  (if (y-or-n-p "exit emacs")
> +  (kill-emacs)
> +(error "Stopped reading %s" load-file-name)))
> +
> +(let ((pdir (file-name-directory
> +  (directory-file-name (file-name-directory load-file-name)
> +  (unless (file-exists-p (concat pdir "emacs/notmuch-lib.el"))
> +(insert "Cannot find notmuch-emacs source directory
> +while looking at: " pdir "emacs\n\nexit emacs (y or n)? ")
> +(if (y-or-n-p "exit emacs")
> + (kill-emacs)
> +  (error "Stopped reading %s" load-file-name)))
> +  (setq try-notmuch-source-directory (directory-file-name pdir))
> +  (setq try-notmuch-emacs-directory (concat pdir "emacs/"))
> +  (setq load-path (cons try-notmuch-emacs-directory load-path)))
> +
> +;; they say advice doesn't work for primitives (functions from c source)
> +;; well, these 'before' advice works for emacs 23.1 - 24.5 (at least)
> +;; ...and for our purposes 24.3 is enough (there is no load-prefer-newer 
> there)
> +;; note also that the old, "obsolete" defadvice mechanism was used, but that
> +;; is the only one available for emacs 23 and 24 up to 24.3.
> +
> +(if (boundp 'load-prefer-newer)
> +(defadvice require (before before-require activate)
> +  (unless (featurep feature)
> + (message "require: %s" feature)))
> +  ;; else: special require "short-circuit"; after load feature is provided...
> +  ;; ... in notmuch sources we always use require and there are no loops
> +  (defadvice require (before before-require activate)
> +

Re: [PATCH] devel/emacs: add devel/try-emacs-mua.sh

2016-01-04 Thread Michal Sojka
On Thu, Nov 12 2015, Mark Walters wrote:
>> devel/try-emacs-mua.sh provides an easy way to try and experiment with
>> the notmuch emacs client provided in emacs subdirectory of notmuch
>> source tree.

[...]

>
> I like this -- a nice simple way to get to a working but uncustomised
> test environment.
>
> I don't know how easy the following would be but one addition that I
> would find very useful would be a way to make this use the test
> corpus. This would be useful for giving a completely standard
> environment for interactive testing, but also for debugging what is
> going on when tests fail.

This is already possible. If a test fails, its working directory is not
deleted and you can run e.g. ./test/tmp.T310-emacs/run_emacs, where you
can interactively reproduce the test. What you miss compared to Tomi's
approach is the hint screen and extra logging.

BR
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v4] devel/emacs: add devel/try-emacs-mua

2016-01-03 Thread Michal Sojka
Hi Tomi,

On Sun, Dec 20 2015, Tomi Ollila wrote:
> devel/try-emacs-mua provides an easy way to try and experiment
> with the notmuch emacs client distributed in emacs subdirectory of
> the notmuch source tree.
>
> try-emacs-mua starts a new emacs process and if initial checks pass
> *scratch* buffer is filled with information of how to begin.
>
> User can add normal emacs flags, like -q, -Q and e.g. -f notmuch. Any
> given plain filename arguments are filtered out (with message).

Why are plain files filtered? Yesterday, when I experimented with
jl-encrypt.el, I started emacs like:

  emacs -Q --directory ... --load notmuch.el /tmp/jl-encrypt.el

This allowed me to experiment with it (edebug), modify it and restart
emacs sessions easily without the need to type C-x C-f and the file name
in every session.

> If the emacs version in use is smaller than 24.4, special care is taken
> to ensure that notmuch*.elc files older than corresponding .el files
> are not loaded. Since emacs 24.4, setting `load-prefer-newer' variable
> takes care of this.
> ---
>
> Now all-elisp, vs v3: id:1447457397-11688-1-git-send-email-tomi.oll...@iki.fi
>
> In id:87si4bwrsu@qmul.ac.uk Mark had some thoughts if something from this
> could perhaps be used as some idea how to improve test testing. We'll have
> to think more of that later. Rest of the review is pretty much irrelevant
> for this (But I fixed that particular bug before turning to this ;).
>
>  devel/try-emacs-mua | 153 
> 
>  1 file changed, 153 insertions(+)
>  create mode 100755 devel/try-emacs-mua
>
> diff --git a/devel/try-emacs-mua b/devel/try-emacs-mua
> new file mode 100755
> index ..1f916d2806f9
> --- /dev/null
> +++ b/devel/try-emacs-mua
> @@ -0,0 +1,153 @@
> +#!/bin/sh
> +:; set -x; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit

Huh! Nice trick, especially the ":;" :)

> +;;
> +;; Try the notmuch emacs client located in ../notmuch/emacs directory
> +;;
> +;; Run this without arguments; emacs window opens with some usage information
> +;;
> +;; Authors: Tomi Ollila 
> +;;
> +;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point...
> +;;
> +;; Licence: GPLv3+
> +;;
> +
> +(message "Starting '%s'" load-file-name)
> +
> +(set-buffer "*scratch*")
> +
> +(setq initial-buffer-choice t) ;; *scratch* buffer
> +
> +(when (featurep 'notmuch)
> +  (insert "
> +Notmuch has been loaded to this emacs (during processing of the init file)
> +which means it is (most probably) loaded from different source than expected.
> +
> +Please run \"" (file-name-nondirectory load-file-name)
> +"\" with '-q' (or '-Q') as an argument, to disable
> +processing of the init file -- you can load it after emacs has started\n
> +exit emacs (y or n)? ")
> +  (if (y-or-n-p "exit emacs")
> +  (kill-emacs)
> +(error "Stopped reading %s" load-file-name)))
> +
> +(let ((pdir (file-name-directory
> +  (directory-file-name (file-name-directory load-file-name)
> +  (unless (file-exists-p (concat pdir "emacs/notmuch-lib.el"))
> +(insert "Cannot find notmuch-emacs source directory
> +while looking at: " pdir "emacs\n\nexit emacs (y or n)? ")
> +(if (y-or-n-p "exit emacs")
> + (kill-emacs)
> +  (error "Stopped reading %s" load-file-name)))
> +  (setq try-notmuch-source-directory (directory-file-name pdir))
> +  (setq try-notmuch-emacs-directory (concat pdir "emacs/"))
> +  (setq load-path (cons try-notmuch-emacs-directory load-path)))
> +
> +;; for logging, debugging and load tracing (broke on emacs 24.5.1 :O)
> +;;(defadvice file-truename (before before-file-truename activate)
> +  (message "file-truename: '%s' '%s' '%s'" filename counter prev-dirs))
> +;;  (unless counter
> +;;(message "file-truename: %s" filename)))

What is the benefit of advising file-truename? Especially when you do
other type of logging below. I'd prefer to have this as a comment in the
file. Or is this some leftover from your experiments?

> +
> +;; they say advice doesn't work for primitives (functions from c source)
> +;; well, these 'before' advice works for emacs 23.1 - 24.5 (at least)
> +;; ...and for our purposes 24.3 is enough (there is no load-prefer-newer 
> there)
> +;; note also that the old, "obsolete" defadvice mechanism was used, but that
> +;; is the only one available for emacs 23 and 24 up to 24.3.
> +
> +(if (boundp 'load-prefer-newer)
> +(defadvice require (before before-require activate)
> +  (unless (featurep feature)
> + (message "require: %s" feature)))
> +  ;; else: special require "short-circuit"; after load feature is provided...
> +  ;; ... in notmuch sources we always use require and there are no loops
> +  (defadvice require (before before-require activate)
> +(unless (featurep feature)
> +  (message "require: %s" feature)
> +  (let ((name (symbol-name feature)))
> + (if (and (string-match "^notmuch" name)
> +  

[PATCH 2/3] emacs: Refactor notmuch-mua-mail

2016-01-02 Thread Michal Sojka
This should be more readable.
---
 emacs/notmuch-mua.el | 26 +-
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index d4950cb..2d6825d 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -320,24 +320,24 @@ modified. This function is notmuch addaptation of
   (notmuch-user-name) " <" (notmuch-user-primary-email) 
">")) other-headers))
 
   (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to))
-  (let ((args (list yank-action send-actions)))
+  (let ((headers
+;; The following sexp is copied from `message-mail'
+(nconc
+ `((To . ,(or to "")) (Subject . ,(or subject "")))
+ ;; C-h f compose-mail says that headers should be specified as
+ ;; (string . value); however all the rest of message expects
+ ;; headers to be symbols, not strings (eg 
message-header-format-alist).
+ ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
+ ;; We need to convert any string input, eg from rmail-start-mail.
+ (dolist (h other-headers other-headers)
+   (if (stringp (car h)) (setcar h (intern (capitalize (car h
+   (args (list yank-action send-actions)))
 ;; message-setup-1 in Emacs 23 does not accept return-action
 ;; argument. Pass it only if it is supplied by the caller. This
 ;; will never be the case when we're called by `compose-mail' in
 ;; Emacs 23.
 (when return-action (nconc args '(return-action)))
-(apply 'message-setup-1
-  ;; The following sexp is copied from `message-mail'
-  (nconc
-   `((To . ,(or to "")) (Subject . ,(or subject "")))
-   ;; C-h f compose-mail says that headers should be specified as
-   ;; (string . value); however all the rest of message expects
-   ;; headers to be symbols, not strings (eg 
message-header-format-alist).
-   ;; 
http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
-   ;; We need to convert any string input, eg from rmail-start-mail.
-   (dolist (h other-headers other-headers)
- (if (stringp (car h)) (setcar h (intern (capitalize (car h)))
-  args))
+(apply 'message-setup-1 headers args))
   (notmuch-fcc-header-setup)
   (message-sort-headers)
   (message-hide-headers)
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 3/3] emacs: Handle switch-function argument of notmuch-mua-mail

2016-01-02 Thread Michal Sojka
notmuch-mua-mail ignored the switch-function argument and always used
the function returned by notmuch-mua-get-switch-function instead. In
order to support standard emacs interfaces (compose-mail in this
case), this commit changes notmuch-mua-mail to use the switch-function
argument if it is non-nil and notmuch-mua-get-switch-function
otherwise.
---
 emacs/notmuch-mua.el | 9 +
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 2d6825d..45a6daa 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -278,7 +278,7 @@ Note that these functions use `mail-citation-hook' if that 
is non-nil."
 (define-key notmuch-message-mode-map (kbd "C-c C-c") 
#'notmuch-mua-send-and-exit)
 (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
 
-(defun notmuch-mua-pop-to-buffer (name)
+(defun notmuch-mua-pop-to-buffer (name switch-function)
   "Pop to buffer NAME, and warn if it already exists and is
 modified. This function is notmuch addaptation of
 `message-pop-to-buffer'."
@@ -291,7 +291,7 @@ modified. This function is notmuch addaptation of
  (progn
(gnus-select-frame-set-input-focus (window-frame window))
(select-window window))
-   (funcall (notmuch-mua-get-switch-function) buffer)
+   (funcall switch-function buffer)
(set-buffer buffer))
  (when (and (buffer-modified-p)
 (not (prog1
@@ -299,7 +299,7 @@ modified. This function is notmuch addaptation of
   "Message already being composed; erase? ")
(message nil
(error "Message being composed")))
-  (funcall (notmuch-mua-get-switch-function) name)
+  (funcall switch-function name)
   (set-buffer name))
 (erase-buffer)
 (notmuch-message-mode)))
@@ -319,7 +319,8 @@ modified. This function is notmuch addaptation of
 (push (cons 'From (concat
   (notmuch-user-name) " <" (notmuch-user-primary-email) 
">")) other-headers))
 
-  (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to))
+  (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)
+(or switch-function 
(notmuch-mua-get-switch-function)))
   (let ((headers
 ;; The following sexp is copied from `message-mail'
 (nconc
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


notmuch-message-mode initialization follow-up

2016-01-02 Thread Michal Sojka
Hi,

the first patch fixes the problem reported in
id:87wprt2r4f.fsf@zancas.localnet. Patch 2/3 is just refactoring.
Patch 3/3 is reaction to id:87y4cc3qse.fsf@zancas.localnet; I'm not
sure whether it addresses exactly what David had in mind, but IMHO it
is an improvement.

Cheers.
-Michal

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/3] emacs: Fix mail composition under Emacs 23

2016-01-02 Thread Michal Sojka
Commit 570c0aeb40bd0c3af8174624a55e968f62c44f09 reworked
notmuch-mua-mail function in a way that worked only under Emacs 24.
The reason was that message-setup-1 took one argument less in Emacs
23.

We fix this by only supplying the return-action argument when it is
actually set by the caller.
---
 emacs/notmuch-mua.el | 30 ++
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index a66a306..d4950cb 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -320,18 +320,24 @@ modified. This function is notmuch addaptation of
   (notmuch-user-name) " <" (notmuch-user-primary-email) 
">")) other-headers))
 
   (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to))
-  (message-setup-1
-   ;; The following sexp is copied from `message-mail'
-   (nconc
-`((To . ,(or to "")) (Subject . ,(or subject "")))
-;; C-h f compose-mail says that headers should be specified as
-;; (string . value); however all the rest of message expects
-;; headers to be symbols, not strings (eg message-header-format-alist).
-;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
-;; We need to convert any string input, eg from rmail-start-mail.
-(dolist (h other-headers other-headers)
-  (if (stringp (car h)) (setcar h (intern (capitalize (car h)))
-   yank-action send-actions return-action)
+  (let ((args (list yank-action send-actions)))
+;; message-setup-1 in Emacs 23 does not accept return-action
+;; argument. Pass it only if it is supplied by the caller. This
+;; will never be the case when we're called by `compose-mail' in
+;; Emacs 23.
+(when return-action (nconc args '(return-action)))
+(apply 'message-setup-1
+  ;; The following sexp is copied from `message-mail'
+  (nconc
+   `((To . ,(or to "")) (Subject . ,(or subject "")))
+   ;; C-h f compose-mail says that headers should be specified as
+   ;; (string . value); however all the rest of message expects
+   ;; headers to be symbols, not strings (eg 
message-header-format-alist).
+   ;; 
http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
+   ;; We need to convert any string input, eg from rmail-start-mail.
+   (dolist (h other-headers other-headers)
+ (if (stringp (car h)) (setcar h (intern (capitalize (car h)))
+  args))
   (notmuch-fcc-header-setup)
   (message-sort-headers)
   (message-hide-headers)
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2] emacs: Improve notmuch-message-mode initialization

2016-01-02 Thread Michal Sojka
On Fri, Jan 01 2016, David Bremner wrote:
> David Bremner <da...@tethera.net> writes:
>
>> Michal Sojka <sojk...@fel.cvut.cz> writes:
>>
>>> This commit fixes these problems by replacing a call to message-mail
>>> with notmuch-specific code that is (hopefully) equivalent to
>>> message-mail functionality before introduction of
>>> notmuch-message-mode.
>>
>> pushed to master
>
> It seems to have broken something with emacs23
>
>https://travis-ci.org/notmuch/notmuch/builds/99715812
>
> I just verified the same problems (more or less) exist in debian
> oldstable.

A fixup patch was sent as
id:1451753265-26713-2-git-send-email-sojk...@fel.cvut.cz.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: notmuch-mua and jl-encrypt (was: file-error "not a regular file")

2016-01-02 Thread Michal Sojka
Hi fauno,

On Wed, Dec 30 2015, fauno wrote:
> Michal Sojka <sojk...@fel.cvut.cz> writes:
>> can you share more details about how do you use jl-encrypt and which
>> functions do not work? Recently, I posted a few patches that fix some
>> problems related to the replacing message-mode with
>> notmuch-message-mode. Maybe, there is more what we can do to not break
>> user's setups.
>
> i've been using jl-encrypt unmodified with notmuch for a while now.  i
> just require it on my .emacs
>
> since 0.21, i had to rebind notmuch-message-mode C-c C-c and C-s C-s
> keys:
>
> (define-key notmuch-message-mode-map (kbd "C-c C-c") 
> 'jl-message-send-and-exit)
> (define-key notmuch-message-mode-map (kbd "C-c C-s") 'jl-message-send)
>
> and solved the issue of fcc by setting notmuch-fcc-dirs to nil and
> making my mta send me bcc of my own email.
>
> this has worked correctly for the last week.
>
>> From a brief look at jl-encrypt, it seems it is tightly bound to gnus,
>> because it uses gnus-message-setup-hook. Maybe it will work again with
>> notmuch if you use my patch [1] and run
>>
>> (add-hook 'message-setup-hook 'jl-encrypt-if-possible)
>
> i applied your patch to the 0.21 release and byte-compiled notmuch-mua.el
>
> the message is sent unencrypted unless i rebind C-c C-c as before.  the
> email is encrypted but it asks for the recipient, which i'm guessing
> emacs can't figure out for itself anymore?
>
> also tested with `emacs -q` and loading this file, and jl-encrypt never
> asks to encrypt the email when possible:
>
> # /tmp/emacs
>
> (require 'notmuch)
> (add-to-list 'load-path "~/.emacs.d/lisp/")
> (require 'jl-encrypt)
> (add-hook 'message-setup-hook 'jl-encrypt-if-possible)
> (add-hook 'message-setup-hook 'mml-secure-message-sign-pgpmime)
>
> by adding the C-c C-c rebind, it gets to encrypt, but asks for recipient
> again.

I looked into this and I think that the following workaround could work
for you:

(defun jl-notmuch-message-send-and-exit ( arg)
  (interactive "P")
  (let ((message-fcc-handler-function #'notmuch-fcc-handler))
(jl-message-send-and-exit arg)))
 
(defun jl-notmuch-message-send ( arg)
  (interactive "P")
  (let ((message-fcc-handler-function #'notmuch-fcc-handler))
(jl-message-send arg)))
 
(define-key notmuch-message-mode-map (kbd "C-c C-c") 
'jl-notmuch-message-send-and-exit)
(define-key notmuch-message-mode-map (kbd "C-c C-s") 
'jl-notmuch-message-send)

Unfortunately, I couldn't find a less intrusive solution for now,
because notmuch uses the same "trick" (remapping keyboard shortcuts) as
jl-encrypt to override message sending from message.el. This means that
either jl-encrypt or notmuch can be bound to these keys but not both.

Notmuch has to use this mechanism until the bug [1] is fixed in Emacs.

Another possibility would be to introduce something like
notmuch-send-hook which would allow other packages to hook into notmuch
sending process, but this could be even more complex that the above
workaround, so I don't think it is worth the effort.

Best regards,
-Michal

[1] http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21174
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: file-error "not a regular file"

2015-12-30 Thread Michal Sojka
Hi fauno,

On Tue, Dec 29 2015, fauno wrote:
> David Bremner  writes:
>
>> I guess the tl;dr is that I can't duplicate this problem. Looking at the
>> traceback Peter provided, it looks like he is using
>> "send-message-without-bullets" to send the message. Since this isn't a
>> notmuch function, it's likely bypassing the notmuch fcc setup that
>> notmuch-mua-send and notmuch-mua-send-and-exit do.
>
> i've found the issue.  i'm using jl-encrypt that expects email written
> in message-mode, and since notmuch is now a minor mode some functions
> that were expected to be there didn't work anymore.

can you share more details about how do you use jl-encrypt and which
functions do not work? Recently, I posted a few patches that fix some
problems related to the replacing message-mode with
notmuch-message-mode. Maybe, there is more what we can do to not break
user's setups.

From a brief look at jl-encrypt, it seems it is tightly bound to gnus,
because it uses gnus-message-setup-hook. Maybe it will work again with
notmuch if you use my patch [1] and run

(add-hook 'message-setup-hook 'jl-encrypt-if-possible)

-Michal

[1] id:1450690875-2111-2-git-send-email-sojk...@fel.cvut.cz
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] doc/reply: Clarify how reply-to header is handled

2015-12-29 Thread Michal Sojka
Current documentation and comments in the code do not correspond to
the actual code and tests in the test suite ("Un-munging Reply-To" in
T230-reply-to-sender.sh). Fix it.
---
 doc/man1/notmuch-reply.rst | 6 --
 notmuch-reply.c| 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/doc/man1/notmuch-reply.rst b/doc/man1/notmuch-reply.rst
index cfbd4ea..d73f8f1 100644
--- a/doc/man1/notmuch-reply.rst
+++ b/doc/man1/notmuch-reply.rst
@@ -13,8 +13,10 @@ DESCRIPTION
 Constructs a reply template for a set of messages.
 
 To make replying to email easier, **notmuch reply** takes an existing
-set of messages and constructs a suitable mail template. The Reply-to:
-header (if any, otherwise From:) is used for the To: address. Unless
+set of messages and constructs a suitable mail template. Its To:
+address is set according to the original email in this way: if the
+Reply-to: header is present and different from any To:/Cc: address it
+is used, otherwise From: header is used. Unless
 ``--reply-to=sender`` is specified, values from the To: and Cc: headers
 are copied, but not including any of the current user's email addresses
 (as configured in primary\_mail or other\_email in the .notmuch-config
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 6df54fc..3c6d685 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -332,7 +332,7 @@ add_recipients_from_message (GMimeMessage *reply,
  * field and use the From header. This ensures the original sender
  * will get the reply even if not subscribed to the list. Note
  * that the address in the Reply-To header will always appear in
- * the reply.
+ * the reply if reply_all is true.
  */
 if (reply_to_header_is_redundant (message)) {
reply_to_map[0].header = "from";
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: notmuch-reply doesn't use Reply-To

2015-12-29 Thread Michal Sojka
Hi all,

On Fri, Dec 04 2015, Jani Nikula wrote:
> On Fri, 04 Dec 2015, Damien Cassou  wrote:
>> David Bremner  writes:
>>
>>> Damien Cassou  writes:
>>>
"To" : "r...@inria.fr",
"Reply-To" : "r...@inria.fr",
"From" : "seas...@rmod.inria.fr",
"Subject" : "[rmod] [Mm10s] 2015-11-30",
"Date" : "Mon, 30 Nov 2015 07:00:01 +0100"
>>>
>>> A quick look at the code suggests this is falling victim to the
>>> "reply-to munging" detection code, which considers a reply-to field
>>> redudant if it duplicates one of the other fields. From the source
>>>
>>> /* Some mailing lists munge the Reply-To header despite it being A Bad
>>>  * Thing, see http://www.unicom.com/pw/reply-to-harmful.html
>>>  *
>>>  * The munging is easy to detect, because it results in a
>>>  * redundant reply-to header, (with an address that already exists
>>>  * in either To or Cc). So in this case, we ignore the Reply-To
>>>  * field and use the From header. This ensures the original sender
>>>  * will get the reply even if not subscribed to the list. Note
>>>  * that the address in the Reply-To header will always appear in
>>>  * the reply.
>>>  */
>>
>>
>> The last sentence seems to contradict my example:
>>
>> Note that the address in the Reply-To header will always appear in
>> the reply.
>>
>> Here is the reply message, and it does not contain the address in Reply-To.
>
> This was true way back when notmuch reply only knew about reply all. For
> --reply-to=sender, it's broken. The simplest "fix" might be

I don't think that this is broken for two reasons:

1. In tests/T230-reply-to-sender.sh, there is "Un-munging Reply-To"
   test, which checks the same combination of headers as in Damien's
   case and uses --reply-to=sender. The test passes and the reply has
   To=From.

2. When replying to mailing lists using reply-to munging, current
   notmuch behavior allows me to decide whether to reply 1) privately to
   the mail sender (--reply-to=sender) or 2) to the mailing list
   (--reply-to=all). The proposed change would make option 1) harder.

Therefore I suggest to fix this by applying the documentation patch from
the follow-up mail.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2] test: Unset ALTERNATE_EDITOR before running emacsclient

2015-12-29 Thread Michal Sojka
ALTERNATE_EDITOR causes emacsclient to run an alternate editor if the
emacs server is not ready. This can collide with intended
functionality in test-lib.sh.

If the ALTERNATE_EDITOR is set but empty, emacsclient runs emacs
daemon and tries to connect to it. When this happens the emacs run by
test-lib.sh fails to start the server and the subsequent attempts to
use the server fail because the daemon started by emacsclient does not
know about notmuch-test-progn. This leads to test suite failure due to
time out on any emacs test.
---
 test/test-lib.sh | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index 126911f..270c718 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -82,6 +82,9 @@ unset CDPATH
 
 unset GREP_OPTIONS
 
+# For emacsclient
+unset ALTERNATE_EDITOR
+
 # Convenience
 #
 # A regexp to match 5 and 40 hexdigits
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: Always use paths without symlinks

2015-12-29 Thread Michal Sojka
When notmuch sources are at a symlinked path, some tests fail because
one part of the test uses physical path and another uses logical
path (with symlinks). For example the following test output is
produced when the test is started from /home/src/symlink-to-notmuch,
which is a symlink to /home/src/notmuch.

FAIL   notmuch-fcc-dirs set to a string
--- T310-emacs.26.OUTPUT2015-12-29 08:54:29.055878637 +
+++ T310-emacs.26.EXPECTED  2015-12-29 08:54:29.055878637 +
@@ -1,5 +1,5 @@
 From: Notmuch Test Suite 
 To:
 Subject:
-Fcc: /home/src/notmuch/test/tmp.T310-emacs/mail/sent-string
+Fcc: /home/src/symlink-to-notmuch/test/tmp.T310-emacs/mail/sent-string
 --text follows this line--
nil

This commit makes all paths in test scripts physical. With it, all
tests pass even when run from a symlinked directory.
---
 test/test-lib-common.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh
index 5eb618c..4e17b78 100644
--- a/test/test-lib-common.sh
+++ b/test/test-lib-common.sh
@@ -48,7 +48,7 @@ restore_database () {
 
 # Test the binaries we have just built.  The tests are kept in
 # test/ subdirectory and are run in 'trash directory' subdirectory.
-TEST_DIRECTORY=$(pwd)
+TEST_DIRECTORY=$(pwd -P)
 notmuch_path=`find_notmuch_path "$TEST_DIRECTORY"`
 
 # configure output
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: Unset ALTERNATE_EDITOR before running emacsclient

2015-12-28 Thread Michal Sojka
ALTERNATE_EDITOR causes emacsclient to run an alternate editor if the
emacs server is not ready. This can collide with intended
functionality in test-lib.sh.

If the ALTERNATE_EDITOR is set but empty, emacsclient runs emacs
daemon and tries to connect to it. When this happens the emacs run by
test-lib.sh fails to start the server and the subsequent attempts to
use the server fail because the daemon started by emacsclient does not
know about notmuch-test-progn. This leads to test suite failure due to
time out on any emacs test.
---
 test/test-lib.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index 126911f..0f6a6cf 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -1152,6 +1152,7 @@ test_emacs () {
rm -f OUTPUT
touch OUTPUT
 
+   unset ALTERNATE_EDITOR
${TEST_EMACSCLIENT} --socket-name="$EMACS_SERVER" --eval 
"(notmuch-test-progn $@)"
 }
 
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] emacs: address completion, allow sender/recipient and filters

2015-12-28 Thread Michal Sojka
Hi Mark,

I tried this patch. When I set the variable to 'received, first
completion candidates are generated incorrectly. The query looked as
"(to:sojk...@fel.cvut.cz) and (to:prefix*)". It should be "...
(from:prefix*)". This "to:" comes from notmuch-address-options or from
notmuch-company so these should be changes as well.

Other minor comments are below.

Thanks.
-Michal

On Sun, Dec 20 2015, Mark Walters wrote:
> This commit lets the user customize the address completion.
>
> The first change controls whether to build the address completion list
> based on messages you have sent or you have received (the latter is
> much faster).
>
> The second change add a possible filter query to limit the messages
> used -- for example, setting this to date:1y..  would limit the
> address completions to addresses used in the last year. This speeds up
> the address harvest and may also make the search less cluttered as old
> addresses may well no longer be valid.
> ---
>
> There was some discussion on irc about the address completion harvest
> taking a long time. This patch makes is possible to tune this
> behaviour. It may be that this complicates the customize variable too
> much -- but if nothing else its a patch that anyone who experiences
> problems can try.
>
> Best wishes
>
> Mark
>
> emacs/notmuch-address.el | 64 +---
>  1 file changed, 44 insertions(+), 20 deletions(-)
>
> diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
> index 49e2402..8942fe4 100644
> --- a/emacs/notmuch-address.el
> +++ b/emacs/notmuch-address.el
> @@ -26,15 +26,40 @@
>  ;;
>  (declare-function company-manual-begin "company")
>
> +(defvar notmuch-address-last-harvest 0
> +  "Time of last address harvest")
> +
> +(defvar notmuch-address-completions (make-hash-table :test 'equal)
> +  "Hash of email addresses for completion during email composition.
> +  This variable is set by calling `notmuch-address-harvest'.")
> +
> +(defvar notmuch-address-full-harvest-finished nil
> +  "t indicates that full completion address harvesting has been
> +finished")
> +
>  (defcustom notmuch-address-command 'internal

Default value should be changed to something matching the :type. Perhaps
'(sent nil).

>"The command which generates possible addresses. It must take a
>  single argument and output a list of possible matches, one per
>  line. The default value of `internal' uses built-in address
>  completion."

Doc text can be updated as well.

>:type '(radio
> -   (const :tag "Use internal address completion" internal)
> +   (list :tag "Use internal address completion"
> + (radio
> +  :tag "Build list based on messages you have"
> +  :value sent
> +  (const :tag "sent" sent)
> +  (const :tag "received" received))

I would mention that received is faster here.

> + (radio :tag "Filter messages used for completion"
> +(const :tag "Use all messages" nil)
> +(string :tag "Filter query")))
> (const :tag "Disable address completion" nil)
> -   (string :tag "Use external completion command" "notmuch-addresses"))
> +   (string :tag "Use external completion command"))
> +  ;; We override set so that we can clear the cache when this changes
> +  :set (lambda (symbol value)
> +  (set-default symbol value)
> +  (setq notmuch-address-last-harvest 0)
> +  (setq notmuch-address-completions (clrhash 
> notmuch-address-completions))
> +  (setq notmuch-address-full-harvest-finished nil))
>:group 'notmuch-send
>:group 'notmuch-external)
>
> @@ -49,17 +74,6 @@ to know how address selection is made by default."
>:group 'notmuch-send
>:group 'notmuch-external)
>
> -(defvar notmuch-address-last-harvest 0
> -  "Time of last address harvest")
> -
> -(defvar notmuch-address-completions (make-hash-table :test 'equal)
> -  "Hash of email addresses for completion during email composition.
> -  This variable is set by calling `notmuch-address-harvest'.")
> -
> -(defvar notmuch-address-full-harvest-finished nil
> -  "t indicates that full completion address harvesting has been
> -finished")
> -
>  (defun notmuch-address-selection-function (prompt collection initial-input)
>"Call (`completing-read'
>PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)"
> @@ -81,7 +95,8 @@ finished")
>
>  (defun notmuch-address-setup ()
>(let* ((use-company (and notmuch-address-use-company
> -(eq notmuch-address-command 'internal)
> +notmuch-address-command
> +(listp notmuch-address-command)
>  (require 'company nil t)))
>(pair (cons notmuch-address-completion-headers-regexp
>(if use-company
> @@ -109,7 +124,7 @@ The candidates are taken from 
> `notmuch-address-completions'."
>  elisp-based implementation or older 

[PATCH v2] emacs: Improve notmuch-message-mode initialization

2015-12-21 Thread Michal Sojka
Recent addition of notmuch-message-mode introduced several problems:

1. When message-setup-hook is used to set buffer local variables,
   these settings are not effective, because all buffer local
   variables are immediately erased by notmuch-message-mode
   initialization.

2. message-mode-hook gets invoked twice - first when message-mail
   invokes message-mode and second when notmuch-mua-mail invokes
   notmuch-message-mode.

This commit fixes these problems by replacing a call to message-mail
with notmuch-specific code that is (hopefully) equivalent to
message-mail functionality before introduction of
notmuch-message-mode.

We first initialize notmuch-message-mode with
notmuch-mua-pop-to-buffer, which is a modified version of
message-pop-to-buffer and then call message-setup-1, which is the only
functionality of message-mail that is needed for notmuch.
---
 emacs/notmuch-mua.el | 49 +++--
 1 file changed, 43 insertions(+), 6 deletions(-)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 466edd7..a66a306 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -278,10 +278,36 @@ Note that these functions use `mail-citation-hook' if 
that is non-nil."
 (define-key notmuch-message-mode-map (kbd "C-c C-c") 
#'notmuch-mua-send-and-exit)
 (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
 
-(defun notmuch-mua-mail ( to subject other-headers  other-args)
-  "Invoke the notmuch mail composition window.
-
-OTHER-ARGS are passed through to `message-mail'."
+(defun notmuch-mua-pop-to-buffer (name)
+  "Pop to buffer NAME, and warn if it already exists and is
+modified. This function is notmuch addaptation of
+`message-pop-to-buffer'."
+  (let ((buffer (get-buffer name)))
+(if (and buffer
+(buffer-name buffer))
+   (let ((window (get-buffer-window buffer 0)))
+ (if window
+ ;; Raise the frame already displaying the message buffer.
+ (progn
+   (gnus-select-frame-set-input-focus (window-frame window))
+   (select-window window))
+   (funcall (notmuch-mua-get-switch-function) buffer)
+   (set-buffer buffer))
+ (when (and (buffer-modified-p)
+(not (prog1
+ (y-or-n-p
+  "Message already being composed; erase? ")
+   (message nil
+   (error "Message being composed")))
+  (funcall (notmuch-mua-get-switch-function) name)
+  (set-buffer name))
+(erase-buffer)
+(notmuch-message-mode)))
+
+(defun notmuch-mua-mail ( to subject other-headers continue
+  switch-function yank-action send-actions
+  return-action  ignored)
+  "Invoke the notmuch mail composition window."
   (interactive)
 
   (when notmuch-mua-user-agent-function
@@ -293,8 +319,19 @@ OTHER-ARGS are passed through to `message-mail'."
 (push (cons 'From (concat
   (notmuch-user-name) " <" (notmuch-user-primary-email) 
">")) other-headers))
 
-  (apply #'message-mail to subject other-headers other-args)
-  (notmuch-message-mode)
+  (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to))
+  (message-setup-1
+   ;; The following sexp is copied from `message-mail'
+   (nconc
+`((To . ,(or to "")) (Subject . ,(or subject "")))
+;; C-h f compose-mail says that headers should be specified as
+;; (string . value); however all the rest of message expects
+;; headers to be symbols, not strings (eg message-header-format-alist).
+;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
+;; We need to convert any string input, eg from rmail-start-mail.
+(dolist (h other-headers other-headers)
+  (if (stringp (car h)) (setcar h (intern (capitalize (car h)))
+   yank-action send-actions return-action)
   (notmuch-fcc-header-setup)
   (message-sort-headers)
   (message-hide-headers)
-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2] emacs: Improve notmuch-message-mode initialization

2015-12-21 Thread Michal Sojka
Hi,

this is v2 of id:1446573161-28068-1-git-send-email-sojk...@fel.cvut.cz.
As suggested by David, instead of using advices, it reimplements the
needed pieces of message-mail in notmuch-specific way.

Michal Sojka (1):
  emacs: Improve notmuch-message-mode initialization

 emacs/notmuch-mua.el | 49 +++--
 1 file changed, 43 insertions(+), 6 deletions(-)

-- 
2.6.4

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] emacs: Fix regression in (notmuch-)message-mode initialization

2015-11-09 Thread Michal Sojka
On Mon, Nov 09 2015, David Bremner wrote:
> Michal Sojka <sojk...@fel.cvut.cz> writes:
>
>> This commit uses advice mechanism to call notmuch-message-mode instead
>> of message-mode. This way, a call to message-mail initializes directly
>> notmuch-message-mode rather than message-mode which is later changed
>> to notmuch-message-mode. The advice is constructed in such a way, that
>> it is effective only once and when called by notmuch. The second call
>> to message-mode (from notmuch-message-mode) calls the original
>> message-mode.
>
> I wanted to answer this with an alternative patch, but I haven't had
> time.
>
> I admit to being somewhat prejudiced against shipping code with advice
> in it (IMHO it's fine for user specific customization, but not very
> maintainable).
>
> I think we might be better off in the long run replacing the call to
> message-mail. Code duplication is obviously not great, but perhaps some
> of the complexity of message-mail / message-pop-to-buffer can be
> eliminated, since we don't need to support all of the use cases of
> message-mail.

OK. I'll try implement notmuch-message-mail as a simpler alternative to
message-mail. Let's see how it will look like.

>
>> This implementation uses the new advice mechanism introduced in Emacs
>> 24.4. If we want to support older version, this must be changed.
>
> We do try to support Emacs 23 still (except for some optional
> features). Unfortunately old advice is even nastier.

Hopefully, advises won't be needed then.

Thanks,
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] emacs: Fix regression in (notmuch-)message-mode initialization

2015-11-03 Thread Michal Sojka
Recent addition of notmuch-message-mode introduced a few regressions.
I use message-setup-hook to set some buffer local variables and this
stopped working. The reason is that notmuch-message-mode, which cleans
all buffer local variables, is called after calling message-mail,
which calls the message-setup-hook. Another problem with the previous
implementation might be that the message-mode-hook is called twice -
first when message-mail calls message-mode and second when
notmuch-message-mode calls it. This can be problematic when the hook
has side effects.

This commit uses advice mechanism to call notmuch-message-mode instead
of message-mode. This way, a call to message-mail initializes directly
notmuch-message-mode rather than message-mode which is later changed
to notmuch-message-mode. The advice is constructed in such a way, that
it is effective only once and when called by notmuch. The second call
to message-mode (from notmuch-message-mode) calls the original
message-mode.

This implementation uses the new advice mechanism introduced in Emacs
24.4. If we want to support older version, this must be changed.
---
 emacs/notmuch-mua.el | 20 ++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index fd98ea4..540a676 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -276,6 +276,19 @@ Note that these functions use `mail-citation-hook' if that 
is non-nil."
 (define-key notmuch-message-mode-map (kbd "C-c C-c") 
#'notmuch-mua-send-and-exit)
 (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
 
+(defun message-mode--notmuch-advice ()
+  (if (boundp 'use-notmuch-message-mode)
+  (progn
+   (makunbound 'use-notmuch-message-mode)
+   (notmuch-message-mode)
+   nil) ; Do not call original message-mode
+t)) ; Call original message-mode
+
+;; Advice message-mode to call notmuch-message-mode when
+;; use-notmuch-message-mode is bound. Otherwise message-mode is called
+;; without changes.
+(advice-add 'message-mode :before-while #'message-mode--notmuch-advice)
+
 (defun notmuch-mua-mail ( to subject other-headers  other-args)
   "Invoke the notmuch mail composition window.
 
@@ -291,8 +304,11 @@ OTHER-ARGS are passed through to `message-mail'."
 (push (cons 'From (concat
   (notmuch-user-name) " <" (notmuch-user-primary-email) 
">")) other-headers))
 
-  (apply #'message-mail to subject other-headers other-args)
-  (notmuch-message-mode)
+  ;; message-mail calls message-mode directly (via
+  ;; message-pop-to-buffer). We want it to call notmuch-message-mode
+  ;; instead.
+  (let ((use-notmuch-message-mode))
+(apply #'message-mail to subject other-headers other-args))
   (notmuch-fcc-header-setup)
   (message-sort-headers)
   (message-hide-headers)
-- 
2.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] emacs: Make notmuch-message-mode play nicely with flyspell

2015-11-03 Thread Michal Sojka
Flyspell mode uses a special setting for message-mode to not
spell-check message headers except Subject. Apply this setting also to
notmuch-message-mode.
---
 emacs/notmuch-mua.el | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index fd98ea4..466edd7 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -273,6 +273,8 @@ Note that these functions use `mail-citation-hook' if that 
is non-nil."
   (when notmuch-address-command
 (notmuch-address-setup)))
 
+(put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
+
 (define-key notmuch-message-mode-map (kbd "C-c C-c") 
#'notmuch-mua-send-and-exit)
 (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
 
-- 
2.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v8 3/3] Emacs: Add address completion based on company-mode

2015-10-26 Thread Michal Sojka
When company-mode is available (Emacs >= 24), address completion
candidates are shown in a nice popup box. This is triggered either by
pressing TAB or by waiting a while during typing an address. The
completion is based entirely on the asynchronous address harvesting
from notmuch-address.el so the GUI is theoretically not blocked for
long time.

The completion works similarly as the TAB-initiated completion from
notmuch-address.el, i.e. quick harvest based on user input is executed
first and only after full harvesting is finished, in-memory cached data
is used.

[Improved by David Bremner]
---
 emacs/Makefile.local |  1 +
 emacs/notmuch-address.el | 18 --
 emacs/notmuch-company.el | 86 
 3 files changed, 103 insertions(+), 2 deletions(-)
 create mode 100644 emacs/notmuch-company.el

diff --git a/emacs/Makefile.local b/emacs/Makefile.local
index 1109cfa..4c06c52 100644
--- a/emacs/Makefile.local
+++ b/emacs/Makefile.local
@@ -20,6 +20,7 @@ emacs_sources := \
$(dir)/notmuch-print.el \
$(dir)/notmuch-version.el \
$(dir)/notmuch-jump.el \
+   $(dir)/notmuch-company.el
 
 $(dir)/notmuch-version.el: $(dir)/Makefile.local version.stamp
 $(dir)/notmuch-version.el: $(srcdir)/$(dir)/notmuch-version.el.tmpl
diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
index 498ef8a..49e2402 100644
--- a/emacs/notmuch-address.el
+++ b/emacs/notmuch-address.el
@@ -22,7 +22,9 @@
 (require 'message)
 (require 'notmuch-parser)
 (require 'notmuch-lib)
+(require 'notmuch-company)
 ;;
+(declare-function company-manual-begin "company")
 
 (defcustom notmuch-address-command 'internal
   "The command which generates possible addresses. It must take a
@@ -72,9 +74,21 @@ finished")
 (defun notmuch-address-message-insinuate ()
   (message "calling notmuch-address-message-insinuate is no longer needed"))
 
+(defcustom notmuch-address-use-company t
+  "If available, use company mode for address completion"
+  :type 'boolean
+  :group 'notmuch-send)
+
 (defun notmuch-address-setup ()
-  (let ((pair (cons notmuch-address-completion-headers-regexp
-   #'notmuch-address-expand-name)))
+  (let* ((use-company (and notmuch-address-use-company
+  (eq notmuch-address-command 'internal)
+  (require 'company nil t)))
+(pair (cons notmuch-address-completion-headers-regexp
+(if use-company
+#'company-manual-begin
+  #'notmuch-address-expand-name
+  (when use-company
+   (notmuch-company-setup))
   (unless (memq pair message-completion-alist)
(setq message-completion-alist
  (push pair message-completion-alist)
diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el
new file mode 100644
index 000..add3161
--- /dev/null
+++ b/emacs/notmuch-company.el
@@ -0,0 +1,86 @@
+;; notmuch-company.el --- Mail address completion for notmuch via company-mode 
 -*- lexical-binding: t -*-
+
+;; Authors: Trevor Jim <t...@mac.com>
+;; Michal Sojka <sojk...@fel.cvut.cz>
+;;
+;; Keywords: mail, completion
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; To enable this, install company mode (https://company-mode.github.io/)
+;;
+;; NB company-minimum-prefix-length defaults to 3 so you don't get
+;; completion unless you type 3 characters
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defvar notmuch-company-last-prefix nil)
+(make-variable-buffer-local 'notmuch-company-last-prefix)
+(declare-function company-begin-backend "company")
+(declare-function company-grab "company")
+(declare-function company-mode "company")
+(declare-function company-manual-begin "company")
+(defvar company-backends)
+
+(declare-function notmuch-address-harvest "notmuch-address")
+(declare-function notmuch-address-harvest-trigger "notmuch-address")
+(declare-function notmuch-address-matching "notmuch-address")
+(defvar notmuch-address-full-harvest-finished)
+(defvar notmuch-address-completion-headers-regexp)
+
+;;;###autoload
+(defun notmuch-company-setup ()
+  (company-mode)
+  (make-local-variable 'company-backends)
+  (setq company-backends '(notmu

[PATCH v8 1/3] emacs: replace use of notmuch-address-message-insinuate

2015-10-26 Thread Michal Sojka
From: David Bremner 

This allows e.g. Gnus users to load this file without changing
message-mode behaviour.

This will disable completion for those that did not customize the
variable but relied on the existence of a file named "notmuch-addresses"
in their path. In the next commit the default behaviour will change to
use a "workalike" internal completion mechanism.
---
 emacs/notmuch-address.el | 19 ---
 emacs/notmuch-mua.el |  4 +++-
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
index fde3c1b..e2af879 100644
--- a/emacs/notmuch-address.el
+++ b/emacs/notmuch-address.el
@@ -23,11 +23,13 @@
 
 ;;
 
-(defcustom notmuch-address-command "notmuch-addresses"
+(defcustom notmuch-address-command nil
   "The command which generates possible addresses. It must take a
 single argument and output a list of possible matches, one per
-line."
-  :type 'string
+line. The default value of nil disables address completion."
+  :type '(radio
+ (const :tag "Disable address completion" nil)
+ (string :tag "Use external completion command" "notmuch-addresses"))
   :group 'notmuch-send
   :group 'notmuch-external)
 
@@ -55,10 +57,12 @@ to know how address selection is made by default."
 (defvar notmuch-address-history nil)
 
 (defun notmuch-address-message-insinuate ()
+  (message "calling notmuch-address-message-insinuate is no longer needed"))
+
+(defun notmuch-address-setup ()
   (unless (memq notmuch-address-message-alist-member message-completion-alist)
 (setq message-completion-alist
  (push notmuch-address-message-alist-member 
message-completion-alist
-
 (defun notmuch-address-options (original)
   (process-lines notmuch-address-command original))
 
@@ -109,11 +113,4 @@ to know how address selection is made by default."
   (not (file-directory-p bin
  (throw 'found-command bin
 
-;; If we can find the program specified by `notmuch-address-command',
-;; insinuate ourselves into `message-mode'.
-(when (notmuch-address-locate-command notmuch-address-command)
-  (notmuch-address-message-insinuate))
-
-;;
-
 (provide 'notmuch-address)
diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el
index 57465b2..fd98ea4 100644
--- a/emacs/notmuch-mua.el
+++ b/emacs/notmuch-mua.el
@@ -269,7 +269,9 @@ Note that these functions use `mail-citation-hook' if that 
is non-nil."
   (set-buffer-modified-p nil))
 
 (define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]"
-  "Notmuch message composition mode. Mostly like `message-mode'")
+  "Notmuch message composition mode. Mostly like `message-mode'"
+  (when notmuch-address-command
+(notmuch-address-setup)))
 
 (define-key notmuch-message-mode-map (kbd "C-c C-c") 
#'notmuch-mua-send-and-exit)
 (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
-- 
2.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v8 2/3] Emacs: Add address completion mechanism implemented in elisp

2015-10-26 Thread Michal Sojka
Currently, notmuch has an address completion mechanism that requires
external command to provide completion candidates. This commit adds a
completion mechanism inspired by https://github.com/tjim/nevermore,
which is implemented in Emacs lisp only.

The preexisting address completion mechanism, activated by pressing
TAB on To/Cc lines, is extended to use the new mechanism when
notmuch-address-command to 'internal, which is the new default.

The core of the new mechanism is the function notmuch-address-harvest,
which collects the completion candidates from the notmuch database and
stores them in notmuch-address-completions variable. The address
harvesting can run either synchronously (same as with the previous
mechanism) or asynchronously. When the user presses TAB for the first
time, synchronous harvesting limited to user entered text is performed.
If the entered text is reasonably long, this operation is relatively
fast. Then, asynchronous harvesting over the full database is triggered.
This operation may take long time (minutes on rotating disk). After it
finishes, no harvesting is normally performed again and subsequent
completion requests use the harvested data cached in memory. Completion
cache is updated after 24 hours.

Note that this commit restores (different) completion functionality for
users when the user used external command named "notmuch-addresses",
i.e. the old default.  The result will be that the user will use
the new mechanism instead of this command. I believe that many users may
not even recognize this because the new mechanism works the same as
http://commonmeasure.org/~jkr/git/notmuch_addresses.git and perhaps also
as other commands suggested at
http://notmuchmail.org/emacstips/#address_completion.

[This feature was significantly improved by David Bremner and Mark Walters]
---
 emacs/notmuch-address.el | 192 ++-
 emacs/notmuch-lib.el |   3 +
 2 files changed, 159 insertions(+), 36 deletions(-)

diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el
index e2af879..498ef8a 100644
--- a/emacs/notmuch-address.el
+++ b/emacs/notmuch-address.el
@@ -20,14 +20,17 @@
 ;; Authors: David Edmondson 
 
 (require 'message)
-
+(require 'notmuch-parser)
+(require 'notmuch-lib)
 ;;
 
-(defcustom notmuch-address-command nil
+(defcustom notmuch-address-command 'internal
   "The command which generates possible addresses. It must take a
 single argument and output a list of possible matches, one per
-line. The default value of nil disables address completion."
+line. The default value of `internal' uses built-in address
+completion."
   :type '(radio
+ (const :tag "Use internal address completion" internal)
  (const :tag "Disable address completion" nil)
  (string :tag "Use external completion command" "notmuch-addresses"))
   :group 'notmuch-send
@@ -44,15 +47,25 @@ to know how address selection is made by default."
   :group 'notmuch-send
   :group 'notmuch-external)
 
+(defvar notmuch-address-last-harvest 0
+  "Time of last address harvest")
+
+(defvar notmuch-address-completions (make-hash-table :test 'equal)
+  "Hash of email addresses for completion during email composition.
+  This variable is set by calling `notmuch-address-harvest'.")
+
+(defvar notmuch-address-full-harvest-finished nil
+  "t indicates that full completion address harvesting has been
+finished")
+
 (defun notmuch-address-selection-function (prompt collection initial-input)
   "Call (`completing-read'
   PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)"
   (completing-read
prompt collection nil nil initial-input 'notmuch-address-history))
 
-(defvar notmuch-address-message-alist-member
-  
'("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):"
- . notmuch-address-expand-name))
+(defvar notmuch-address-completion-headers-regexp
+  
"^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):")
 
 (defvar notmuch-address-history nil)
 
@@ -60,39 +73,67 @@ to know how address selection is made by default."
   (message "calling notmuch-address-message-insinuate is no longer needed"))
 
 (defun notmuch-address-setup ()
-  (unless (memq notmuch-address-message-alist-member message-completion-alist)
-(setq message-completion-alist
- (push notmuch-address-message-alist-member 
message-completion-alist
+  (let ((pair (cons notmuch-address-completion-headers-regexp
+   #'notmuch-address-expand-name)))
+  (unless (memq pair message-completion-alist)
+   (setq message-completion-alist
+ (push pair message-completion-alist)
+
+(defun notmuch-address-matching (substring)
+  "Returns a list of completion candidates matching SUBSTRING.
+The candidates are taken from `notmuch-address-completions'."
+  (let ((candidates)
+   (re (regexp-quote substring)))
+(maphash (lambda (key val)
+

[PATCH v8 0/3] elisp + company completion patches

2015-10-26 Thread Michal Sojka
Hi David, Mark and others,

thank you for pushing these patches forward. I gave them some testing
and they work as expected. In this version I addressed the comments to
v7 including Tomi's request for rewording :) The commit message of 2/3
was also updated to reflect changes in the code.

Cheers.
-Michal

David Bremner (1):
  emacs: replace use of notmuch-address-message-insinuate

Michal Sojka (2):
  Emacs: Add address completion mechanism implemented in elisp
  Emacs: Add address completion based on company-mode

 emacs/Makefile.local |   1 +
 emacs/notmuch-address.el | 213 ++-
 emacs/notmuch-company.el |  86 +++
 emacs/notmuch-lib.el |   3 +
 emacs/notmuch-mua.el |   4 +-
 5 files changed, 265 insertions(+), 42 deletions(-)
 create mode 100644 emacs/notmuch-company.el

-- 
2.5.3

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2015-01-09 Thread Michal Sojka
On Fri, Jan 09 2015, Tomi Ollila wrote:
> On Fri, Jan 09 2015, Michal Sojka  wrote:
>
>> Hi,
>>
>> sorry for longer response time :)
>>
>> On Thu, Jan 01 2015, Tomi Ollila wrote:
>>> On Wed, Dec 31 2014, David Bremner  wrote:
>>>
>>>> Michal Sojka  writes:
>>>>
>>>>> This option allows to configure the criterion for duplicate address
>>>>> filtering. Without this option, all unique combinations of name and
>>>>> address parts are printed. This option allows to filter the output
>>>>> more, for example to only contain unique address parts.
>>>>
>>>> I had the feeling there was some "controversy" about the UI here, but
>>>> following back the 3 versions of the series I didn't see it. Does that
>>>> mean we just need to sanity check the code, or are there outstanding
>>>> bikes to shed?
>>
>> I'd tend to rename this option to --unique as it was in some previous
>> version of the patch. Another thing in my mind is the implementation of
>> the --complete option mentioned in id:878uid9qjl.fsf at nautilus.nautilus.
>> This would also involve some kind of address filtering. I'll look into
>> this and send patches later.
>>
>>> I have intentionally been guiet on this during the review process of the
>>> other patches to not slow down the acceptance of the others. I have not
>>> got enough time to look the implemenentation or think this last patch
>>> further -- from the user interface point of view I recall seeing there
>>> both useless features (but which might be warranted by implementation
>>> simplicity) and missing features (but which might not be there due to 
>>> difficulty in implementation). Also, I am not sure whether the --filter-by
>>> is good option (and options descriptive...)...
>>
>> I'd be interested in what are these "missing features".
>
> Last night when I tried to catch sleep I was also thinking of this...
> ... let's see what I remember...
>
> First, Currently if we have addresses:
>
>  "Uni Que" 
>  "Uni Que" 
>
> I presume these are thought as a separate addresses -- and an option to
> thought these as the same would be useful.

Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold
from my patch.

> but let's consider second set of addresses:
>
>  "Uni Que" 
>  "Uni Keko" 
>
> Now, if there were an option to consider these 2 as the same, that would
> hide user from one of the names -- It is clear that "Uni Que" is the right
> one but if only "Uni Keko" (sleepyhead, that is) is shown user don't have
> a choice to select the right one. I am not sure what the use case for
> "uniquing" these 2 were.

For example, when you are interested in the number of people involved in
a discussion. You care only about the address and not about the names.
Perhaps you'd like to see only the addresses in the output and not the
names in this case, wouldn't you?

> Finally (for now), 3rd set of addresses
>
>  "Uni Que" 
>  "Uni Que" 
>
> Now, if there were an option to consider these 2 as same, and
> user is then given "Uni Que"  (which clearly is
> the wrong one) I don't see the usefullness of this option...

I agree. This would correspond to --unique=name. So I'll drop this
option.

-Michal


[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2015-01-09 Thread Michal Sojka
Hi,

sorry for longer response time :)

On Thu, Jan 01 2015, Tomi Ollila wrote:
> On Wed, Dec 31 2014, David Bremner  wrote:
>
>> Michal Sojka  writes:
>>
>>> This option allows to configure the criterion for duplicate address
>>> filtering. Without this option, all unique combinations of name and
>>> address parts are printed. This option allows to filter the output
>>> more, for example to only contain unique address parts.
>>
>> I had the feeling there was some "controversy" about the UI here, but
>> following back the 3 versions of the series I didn't see it. Does that
>> mean we just need to sanity check the code, or are there outstanding
>> bikes to shed?

I'd tend to rename this option to --unique as it was in some previous
version of the patch. Another thing in my mind is the implementation of
the --complete option mentioned in id:878uid9qjl.fsf at nautilus.nautilus.
This would also involve some kind of address filtering. I'll look into
this and send patches later.

> I have intentionally been guiet on this during the review process of the
> other patches to not slow down the acceptance of the others. I have not
> got enough time to look the implemenentation or think this last patch
> further -- from the user interface point of view I recall seeing there
> both useless features (but which might be warranted by implementation
> simplicity) and missing features (but which might not be there due to 
> difficulty in implementation). Also, I am not sure whether the --filter-by
> is good option (and options descriptive...)...

I'd be interested in what are these "missing features".

Cheers,
-Michal


Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2015-01-09 Thread Michal Sojka
On Fri, Jan 09 2015, Michal Sojka wrote:
 On Fri, Jan 09 2015, Tomi Ollila wrote:
 On Fri, Jan 09 2015, Michal Sojka sojk...@fel.cvut.cz wrote:

 Hi,

 sorry for longer response time :)

 On Thu, Jan 01 2015, Tomi Ollila wrote:
 On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote:

 Michal Sojka sojk...@fel.cvut.cz writes:

 This option allows to configure the criterion for duplicate address
 filtering. Without this option, all unique combinations of name and
 address parts are printed. This option allows to filter the output
 more, for example to only contain unique address parts.

 I had the feeling there was some controversy about the UI here, but
 following back the 3 versions of the series I didn't see it. Does that
 mean we just need to sanity check the code, or are there outstanding
 bikes to shed?

 I'd tend to rename this option to --unique as it was in some previous
 version of the patch. Another thing in my mind is the implementation of
 the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus.
 This would also involve some kind of address filtering. I'll look into
 this and send patches later.

 I have intentionally been guiet on this during the review process of the
 other patches to not slow down the acceptance of the others. I have not
 got enough time to look the implemenentation or think this last patch
 further -- from the user interface point of view I recall seeing there
 both useless features (but which might be warranted by implementation
 simplicity) and missing features (but which might not be there due to
 difficulty in implementation). Also, I am not sure whether the --filter-by
 is good option (and options descriptive...)...

 I'd be interested in what are these missing features.

 Last night when I tried to catch sleep I was also thinking of this...
 ... let's see what I remember...

 First, Currently if we have addresses:

  Uni Que uni...@example.org
  Uni Que uni...@example.org

 I presume these are thought as a separate addresses -- and an option to
 thought these as the same would be useful.

 Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold
 from my patch.

 but let's consider second set of addresses:

  Uni Que uni...@example.org
  Uni Keko uni...@example.org

 Now, if there were an option to consider these 2 as the same, that would
 hide user from one of the names -- It is clear that Uni Que is the right
 one but if only Uni Keko (sleepyhead, that is) is shown user don't have
 a choice to select the right one. I am not sure what the use case for
 uniquing these 2 were.

 For example, when you are interested in the number of people involved in
 a discussion. You care only about the address and not about the names.
 Perhaps you'd like to see only the addresses in the output and not the
 names in this case, wouldn't you?

I meant something like the patch bellow. Unique options would be
no/yes/yes-but-case-insensitive and the type of uniqueness would be
determined by the --output flags. I don't like one thing on this
approach: --output flags now determine visibility of both rows and
columns in the output. But this is already present in master, because we
have count column there.

-Michal

diff --git a/notmuch-search.c b/notmuch-search.c
index 14b9f01..760f59a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -34,6 +34,8 @@ typedef enum {
 OUTPUT_SENDER  = 1  5,
 OUTPUT_RECIPIENTS  = 1  6,
 OUTPUT_COUNT   = 1  7,
+OUTPUT_NAME= 1  8,
+OUTPUT_ADDR= 1  9,
 } output_t;
 
 typedef enum {
@@ -43,6 +45,12 @@ typedef enum {
 NOTMUCH_FORMAT_SEXP
 } format_sel_t;
 
+typedef enum {
+UNIQUE_NO = 0,
+UNIQUE_YES,
+UNIQUE_CASEFOLD,
+} unique_t;
+
 typedef struct {
 notmuch_database_t *notmuch;
 format_sel_t format_sel;
@@ -55,6 +63,7 @@ typedef struct {
 int limit;
 int dupe;
 GHashTable *addresses;
+unique_t unique;
 } search_context_t;
 
 typedef struct {
@@ -243,18 +252,45 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }
 
-/* Returns TRUE iff name and addr is duplicate. If not, stores the
- * name/addr pair in order to detect subsequent duplicates. */
+/* Returns TRUE iff name and/or addr is considered duplicate. If not,
+ * stores the name/addr pair in order to detect subsequent
+ * duplicates. */
 static notmuch_bool_t
 is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
 {
 notmuch_bool_t duplicate;
 char *key;
+gchar *addrfold = NULL;
+gchar *namefold = NULL;
 mailbox_t *mailbox;
 
-key = talloc_asprintf (ctx-format, %s %s, name, addr);
+if (ctx-unique == UNIQUE_CASEFOLD) {
+   addrfold = g_utf8_casefold (addr, -1);
+   namefold = g_utf8_casefold (name, -1);
+}
+
+switch (ctx-output  (OUTPUT_NAME | OUTPUT_ADDR)) {
+case OUTPUT_NAME | OUTPUT_ADDR:
+   key = talloc_asprintf (ctx-format, %s %s, namefold ? namefold : 
name

Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2015-01-09 Thread Michal Sojka
On Fri, Jan 09 2015, Tomi Ollila wrote:
 On Fri, Jan 09 2015, Michal Sojka sojk...@fel.cvut.cz wrote:

 Hi,

 sorry for longer response time :)

 On Thu, Jan 01 2015, Tomi Ollila wrote:
 On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote:

 Michal Sojka sojk...@fel.cvut.cz writes:

 This option allows to configure the criterion for duplicate address
 filtering. Without this option, all unique combinations of name and
 address parts are printed. This option allows to filter the output
 more, for example to only contain unique address parts.

 I had the feeling there was some controversy about the UI here, but
 following back the 3 versions of the series I didn't see it. Does that
 mean we just need to sanity check the code, or are there outstanding
 bikes to shed?

 I'd tend to rename this option to --unique as it was in some previous
 version of the patch. Another thing in my mind is the implementation of
 the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus.
 This would also involve some kind of address filtering. I'll look into
 this and send patches later.

 I have intentionally been guiet on this during the review process of the
 other patches to not slow down the acceptance of the others. I have not
 got enough time to look the implemenentation or think this last patch
 further -- from the user interface point of view I recall seeing there
 both useless features (but which might be warranted by implementation
 simplicity) and missing features (but which might not be there due to 
 difficulty in implementation). Also, I am not sure whether the --filter-by
 is good option (and options descriptive...)...

 I'd be interested in what are these missing features.

 Last night when I tried to catch sleep I was also thinking of this...
 ... let's see what I remember...

 First, Currently if we have addresses:

  Uni Que uni...@example.org
  Uni Que uni...@example.org

 I presume these are thought as a separate addresses -- and an option to
 thought these as the same would be useful.

Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold
from my patch.

 but let's consider second set of addresses:

  Uni Que uni...@example.org
  Uni Keko uni...@example.org

 Now, if there were an option to consider these 2 as the same, that would
 hide user from one of the names -- It is clear that Uni Que is the right
 one but if only Uni Keko (sleepyhead, that is) is shown user don't have
 a choice to select the right one. I am not sure what the use case for
 uniquing these 2 were.

For example, when you are interested in the number of people involved in
a discussion. You care only about the address and not about the names.
Perhaps you'd like to see only the addresses in the output and not the
names in this case, wouldn't you?

 Finally (for now), 3rd set of addresses

  Uni Que uni...@example.org
  Uni Que unique@foobar.invalid

 Now, if there were an option to consider these 2 as same, and
 user is then given Uni Que unique@foobar.invalid (which clearly is
 the wrong one) I don't see the usefullness of this option...

I agree. This would correspond to --unique=name. So I'll drop this
option.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2015-01-09 Thread Michal Sojka
Hi,

sorry for longer response time :)

On Thu, Jan 01 2015, Tomi Ollila wrote:
 On Wed, Dec 31 2014, David Bremner da...@tethera.net wrote:

 Michal Sojka sojk...@fel.cvut.cz writes:

 This option allows to configure the criterion for duplicate address
 filtering. Without this option, all unique combinations of name and
 address parts are printed. This option allows to filter the output
 more, for example to only contain unique address parts.

 I had the feeling there was some controversy about the UI here, but
 following back the 3 versions of the series I didn't see it. Does that
 mean we just need to sanity check the code, or are there outstanding
 bikes to shed?

I'd tend to rename this option to --unique as it was in some previous
version of the patch. Another thing in my mind is the implementation of
the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus.
This would also involve some kind of address filtering. I'll look into
this and send patches later.

 I have intentionally been guiet on this during the review process of the
 other patches to not slow down the acceptance of the others. I have not
 got enough time to look the implemenentation or think this last patch
 further -- from the user interface point of view I recall seeing there
 both useless features (but which might be warranted by implementation
 simplicity) and missing features (but which might not be there due to 
 difficulty in implementation). Also, I am not sure whether the --filter-by
 is good option (and options descriptive...)...

I'd be interested in what are these missing features.

Cheers,
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


New "notmuch address" command

2014-12-14 Thread Michal Sojka
On P?, pro 12 2014, Lele Gaifax wrote:
> Michal Sojka  writes:
>
>>> An (almost) equivalent of "notmuch-addrlookup foo" could be "notmuch
>>> address to:foo* OR from:foo*", but it has at least one indesiderable
>>> difference: it seems considering the "CC" field, but always emits the
>>> "TO" content (i.e., assuming I have a message I sent to "john at doe.com"
>>> and CCed to "foo at bar.com", "notmuch address to:foo" emits
>>> "john at doe.com", not "foo at bar.com") so the candidates it generates are
>>> way too much.
>>>
>>> I don't know it that's done on purpose (I clearly miss the use case if
>>> so).
>>
>> Yes, this is expected behavior. Notmuch address is basically a wrapper
>> around search command. The command does not interpret the query at all,
>> because there might be no from:/to: term. The use case was to SIMPLIFY
>> address completers.
>
> Ok, even if I still miss the point of searching for an address and
> obtaining (only, see below) something (apparently) unrelated.
>
>>> I wonder if it would be reasonable adding a "--complete" flag to the
>>> "address" command that selects a more specific behaviour, so that
>>> "notmuch address --complete foo":
>> ...
>>> b) searches the given text only in the related headers (hiding the
>>>difference between "incoming" and "outgoing" messages, 
>>
>> This should be configurable, because --output=sender is much faster than
>> --output=recipients. I think that ideal address completion should offer
>> you the addresses you have already written to, i.e.
>>
>> notmuch address --output=recipient from:my at address to:"prefix*"
>>
>> But this may be too slow on non-SSD disks. Some users may therefore prefer
>>
>> notmuch address --output=sender to:my at address from:"prefix*"
>>
>> which would be faster, but also includes every spammer/robot/... who
>> sends anything to you.
>
> Yes, seems reasonable!
>
>>> and not
>>>considering the body at all)
>>
>> What considers body now?
>
> Well, "notmuch address foo" currently does that, and that sounds useful,
> to obtain a list of recipients who talked about "foo".
>
>>> c) avoids the "bug"/"feature" explained above
>>
>> Yes, if you know the substring you are looking for, implementing a
>> filter would be trivial.
>
> It's not just a matter of filtering, but rather *which* address is
> emitted: trying it out, in the case above the "foo at bar.com" is not even
> mentioned in the output, because it appears only as a CCed recipient.

Are you saying that Cc address is not printed or that you want it not to
be printed? If the former than it is a bug. The following test passes
for me, i.e. foo at bar.com is printed.

diff --git a/test/T095-address.sh b/test/T095-address.sh
index ed0cac7..17a7b08 100755
--- a/test/T095-address.sh
+++ b/test/T095-address.sh
@@ -145,4 +145,13 @@ cat <EXPECTED
 EOF
 test_expect_equal_file OUTPUT EXPECTED

+test_begin_subtest "Cc address is printed while searching for To address"
+add_message [to]=john at doe.com [cc]=foo at bar.com
+notmuch address --output=recipients to:john at doe.com > OUTPUT
+cat <EXPECTED
+john at doe.com
+foo at bar.com
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_done

Cheers,
-Michal


Re: New notmuch address command

2014-12-14 Thread Michal Sojka
On Pá, pro 12 2014, Lele Gaifax wrote:
 Michal Sojka sojk...@fel.cvut.cz writes:

 An (almost) equivalent of notmuch-addrlookup foo could be notmuch
 address to:foo* OR from:foo*, but it has at least one indesiderable
 difference: it seems considering the CC field, but always emits the
 TO content (i.e., assuming I have a message I sent to j...@doe.com
 and CCed to f...@bar.com, notmuch address to:foo emits
 j...@doe.com, not f...@bar.com) so the candidates it generates are
 way too much.

 I don't know it that's done on purpose (I clearly miss the use case if
 so).

 Yes, this is expected behavior. Notmuch address is basically a wrapper
 around search command. The command does not interpret the query at all,
 because there might be no from:/to: term. The use case was to SIMPLIFY
 address completers.

 Ok, even if I still miss the point of searching for an address and
 obtaining (only, see below) something (apparently) unrelated.

 I wonder if it would be reasonable adding a --complete flag to the
 address command that selects a more specific behaviour, so that
 notmuch address --complete foo:
 ...
 b) searches the given text only in the related headers (hiding the
difference between incoming and outgoing messages, 

 This should be configurable, because --output=sender is much faster than
 --output=recipients. I think that ideal address completion should offer
 you the addresses you have already written to, i.e.

 notmuch address --output=recipient from:my@address to:prefix*

 But this may be too slow on non-SSD disks. Some users may therefore prefer

 notmuch address --output=sender to:my@address from:prefix*

 which would be faster, but also includes every spammer/robot/... who
 sends anything to you.

 Yes, seems reasonable!

 and not
considering the body at all)

 What considers body now?

 Well, notmuch address foo currently does that, and that sounds useful,
 to obtain a list of recipients who talked about foo.

 c) avoids the bug/feature explained above

 Yes, if you know the substring you are looking for, implementing a
 filter would be trivial.

 It's not just a matter of filtering, but rather *which* address is
 emitted: trying it out, in the case above the f...@bar.com is not even
 mentioned in the output, because it appears only as a CCed recipient.

Are you saying that Cc address is not printed or that you want it not to
be printed? If the former than it is a bug. The following test passes
for me, i.e. f...@bar.com is printed.

diff --git a/test/T095-address.sh b/test/T095-address.sh
index ed0cac7..17a7b08 100755
--- a/test/T095-address.sh
+++ b/test/T095-address.sh
@@ -145,4 +145,13 @@ cat EOF EXPECTED
 EOF
 test_expect_equal_file OUTPUT EXPECTED
 
+test_begin_subtest Cc address is printed while searching for To address
+add_message [to]=j...@doe.com [cc]=f...@bar.com
+notmuch address --output=recipients to:j...@doe.com  OUTPUT
+cat EOF EXPECTED
+j...@doe.com
+f...@bar.com
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
 test_done

Cheers,
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


New "notmuch address" command

2014-12-12 Thread Michal Sojka
Hi Lele,

On Fri, Dec 12 2014, Lele Gaifax wrote:
> Hi all again,
>
> I'm happily using "notmuch-addrlookup"[1] as "notmuch-address-command", in
> my Emacs configuration.
>
> As explained in my other message, yesterday I spent some time tweaking
> that configuration and tried to replace it with the new "notmuch
> address" introduced in version 0.19.
>
> An (almost) equivalent of "notmuch-addrlookup foo" could be "notmuch
> address to:foo* OR from:foo*", but it has at least one indesiderable
> difference: it seems considering the "CC" field, but always emits the
> "TO" content (i.e., assuming I have a message I sent to "john at doe.com"
> and CCed to "foo at bar.com", "notmuch address to:foo" emits
> "john at doe.com", not "foo at bar.com") so the candidates it generates are
> way too much.
>
> I don't know it that's done on purpose (I clearly miss the use case if
> so).

Yes, this is expected behavior. Notmuch address is basically a wrapper
around search command. The command does not interpret the query at all,
because there might be no from:/to: term. The use case was to SIMPLIFY
address completers. 

> I wonder if it would be reasonable adding a "--complete" flag to the
> "address" command that selects a more specific behaviour, so that
> "notmuch address --complete foo":

This would definitely be useful.

> a) automatically performs a partial match (i.e. it adds the '*' suffix
>on its own)

OK

> b) searches the given text only in the related headers (hiding the
>difference between "incoming" and "outgoing" messages, 

This should be configurable, because --output=sender is much faster than
--output=recipients. I think that ideal address completion should offer
you the addresses you have already written to, i.e.

notmuch address --output=recipient from:my at address to:"prefix*"

But this may be too slow on non-SSD disks. Some users may therefore prefer

notmuch address --output=sender to:my at address from:"prefix*"

which would be faster, but also includes every spammer/robot/... who
sends anything to you.

> and not
>considering the body at all)

What considers body now?

>
> c) avoids the "bug"/"feature" explained above

Yes, if you know the substring you are looking for, implementing a
filter would be trivial.

> What do you think?

Another question is that uses may want to select which my at address to
use. So maybe you can add --my-address option that allow specifying one
or more addresses. If this option would not be given, all configured
addresses in .notmuch-config would be used.

So I think that --complete should just construct the query containing
from:/to: terms and this should be concatenated with what user specified
as a query. For example:

  notmuch address --complete prefix tag:attachment

-Michal


Re: New notmuch address command

2014-12-12 Thread Michal Sojka
Hi Lele,

On Fri, Dec 12 2014, Lele Gaifax wrote:
 Hi all again,

 I'm happily using notmuch-addrlookup[1] as notmuch-address-command, in
 my Emacs configuration.

 As explained in my other message, yesterday I spent some time tweaking
 that configuration and tried to replace it with the new notmuch
 address introduced in version 0.19.

 An (almost) equivalent of notmuch-addrlookup foo could be notmuch
 address to:foo* OR from:foo*, but it has at least one indesiderable
 difference: it seems considering the CC field, but always emits the
 TO content (i.e., assuming I have a message I sent to j...@doe.com
 and CCed to f...@bar.com, notmuch address to:foo emits
 j...@doe.com, not f...@bar.com) so the candidates it generates are
 way too much.

 I don't know it that's done on purpose (I clearly miss the use case if
 so).

Yes, this is expected behavior. Notmuch address is basically a wrapper
around search command. The command does not interpret the query at all,
because there might be no from:/to: term. The use case was to SIMPLIFY
address completers. 

 I wonder if it would be reasonable adding a --complete flag to the
 address command that selects a more specific behaviour, so that
 notmuch address --complete foo:

This would definitely be useful.

 a) automatically performs a partial match (i.e. it adds the '*' suffix
on its own)

OK

 b) searches the given text only in the related headers (hiding the
difference between incoming and outgoing messages, 

This should be configurable, because --output=sender is much faster than
--output=recipients. I think that ideal address completion should offer
you the addresses you have already written to, i.e.

notmuch address --output=recipient from:my@address to:prefix*

But this may be too slow on non-SSD disks. Some users may therefore prefer

notmuch address --output=sender to:my@address from:prefix*

which would be faster, but also includes every spammer/robot/... who
sends anything to you.

 and not
considering the body at all)

What considers body now?


 c) avoids the bug/feature explained above

Yes, if you know the substring you are looking for, implementing a
filter would be trivial.

 What do you think?

Another question is that uses may want to select which my@address to
use. So maybe you can add --my-address option that allow specifying one
or more addresses. If this option would not be given, all configured
addresses in .notmuch-config would be used.

So I think that --complete should just construct the query containing
from:/to: terms and this should be concatenated with what user specified
as a query. For example:

  notmuch address --complete prefix tag:attachment

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3] nmbug: Add an 'init' command

2014-11-29 Thread Michal Sojka
Hi Trevor,

On ?t, ??j 28 2014, W. Trevor King wrote:
> For folks that want to start versioning a new tag-space, instead of
> cloning one that someone else has already started.
>
> The empty-blob hash-object call avoids errors like:
>
>   $ nmbug commit
>   error: invalid object 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 for
> 'tags/...'
>   fatal: git-write-tree: error building trees
>   'git HASH(0x9ef3eb8) write-tree' exited with nonzero value
>
> David Bremner suggested [1]:
>
>   $ git hash-object -w /dev/null
>
> instead of my Python version of:
>
>   $ git hash-object -w --stdin <&-
>
> but I expect that closing stdin is more portable than the /dev/null
> path (which doesn't exist on Windows, for example).
>
> [1]: id:87y4vu6uvf.fsf at maritornes.cs.unb.ca
>  http://thread.gmane.org/gmane.mail.notmuch.general/18626/focus=18720
> ---
> The only change since v2 [1] is a commit-message tweak:
>
> * Mention Windows as an OS with stdin but no /dev/null [2].
>
> Cheers,
> Trevor
>
> [1]: id:eaa9cf1cb3c00c591dc675c0f314ca31909ff74c.1412965476.git.wking at 
> tremily.us
>  http://thread.gmane.org/gmane.mail.notmuch.general/19289
> [2]: id:20141011071000.GB10926 at odin.tremily.us
>  http://article.gmane.org/gmane.mail.notmuch.general/19294
>
>  devel/nmbug/nmbug | 24 
>  1 file changed, 24 insertions(+)
>
> diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug
> index 9402ead..23bac5c 100755
> --- a/devel/nmbug/nmbug
> +++ b/devel/nmbug/nmbug
> @@ -373,6 +373,29 @@ def fetch(remote=None):
>  _git(args=args, wait=True)
>  
>  
> +def init(remote=None):
> +"""
> +Create an empty nmbug repository.
> +
> +This wraps 'git init' with a few extra steps to support subsequent
> +status and commit commands.
> +"""
> +with _tempfile.TemporaryDirectory(prefix='nmbug-init.') as workdir:
> +_spawn(
> +args=['git', 'init', '--separate-git-dir', NMBGIT, workdir],
> +wait=True)
> +_git(args=['config', '--unset', 'core.worktree'], wait=True)
> +_git(args=['config', 'core.bare', 'true'], wait=True)

Why do you create a non-bare repository and then make it bare? Before I
discovered this patch, I created the nmbug repository by hand and it was
possible to do it with bare repo from beginning. The following code
seems to work and is a bit simpler:

def init(remote=None):
"""
Create an empty nmbug repository.

This wraps 'git init' with a few extra steps to support subsequent
status and commit commands.
"""
_spawn(
args=['git', '--git-dir', NMBGIT, 'init', '--bare'],
wait=True)
# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
_git(args=['hash-object', '-w', '--stdin'], input='', wait=True)
_git(
args=[
'commit', '--allow-empty', '-m', 'Start a new nmbug repository'
],
additional_env={'GIT_WORK_TREE': NMBGIT},
wait=True)

Note that in the initial commit I set the work tree to NMBGIT. This is
because 'git commit' needs some work tree, but in our case it doesn't
matter which one because the commit is empty.

-Michal


> +# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
> +_git(args=['hash-object', '-w', '--stdin'], input='', wait=True)
> +_git(
> +args=[
> +'commit', '--allow-empty', '-m', 'Start a new nmbug 
> repository'
> +],
> +additional_env={'GIT_WORK_TREE': workdir},
> +wait=True)
> +
> +
>  def checkout():
>  """
>  Update the notmuch database from Git.
> @@ -703,6 +726,7 @@ if __name__ == '__main__':
>  'clone',
>  'commit',
>  'fetch',
> +'init',
>  'log',
>  'merge',
>  'pull',
> -- 
> 2.1.0.60.g85f0837
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v3] nmbug: Add an 'init' command

2014-11-29 Thread Michal Sojka
Hi Trevor,

On Út, říj 28 2014, W. Trevor King wrote:
 For folks that want to start versioning a new tag-space, instead of
 cloning one that someone else has already started.

 The empty-blob hash-object call avoids errors like:

   $ nmbug commit
   error: invalid object 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 for
 'tags/...'
   fatal: git-write-tree: error building trees
   'git HASH(0x9ef3eb8) write-tree' exited with nonzero value

 David Bremner suggested [1]:

   $ git hash-object -w /dev/null

 instead of my Python version of:

   $ git hash-object -w --stdin -

 but I expect that closing stdin is more portable than the /dev/null
 path (which doesn't exist on Windows, for example).

 [1]: id:87y4vu6uvf@maritornes.cs.unb.ca
  http://thread.gmane.org/gmane.mail.notmuch.general/18626/focus=18720
 ---
 The only change since v2 [1] is a commit-message tweak:

 * Mention Windows as an OS with stdin but no /dev/null [2].

 Cheers,
 Trevor

 [1]: 
 id:eaa9cf1cb3c00c591dc675c0f314ca31909ff74c.1412965476.git.wk...@tremily.us
  http://thread.gmane.org/gmane.mail.notmuch.general/19289
 [2]: id:20141011071000.gb10...@odin.tremily.us
  http://article.gmane.org/gmane.mail.notmuch.general/19294

  devel/nmbug/nmbug | 24 
  1 file changed, 24 insertions(+)

 diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug
 index 9402ead..23bac5c 100755
 --- a/devel/nmbug/nmbug
 +++ b/devel/nmbug/nmbug
 @@ -373,6 +373,29 @@ def fetch(remote=None):
  _git(args=args, wait=True)
  
  
 +def init(remote=None):
 +
 +Create an empty nmbug repository.
 +
 +This wraps 'git init' with a few extra steps to support subsequent
 +status and commit commands.
 +
 +with _tempfile.TemporaryDirectory(prefix='nmbug-init.') as workdir:
 +_spawn(
 +args=['git', 'init', '--separate-git-dir', NMBGIT, workdir],
 +wait=True)
 +_git(args=['config', '--unset', 'core.worktree'], wait=True)
 +_git(args=['config', 'core.bare', 'true'], wait=True)

Why do you create a non-bare repository and then make it bare? Before I
discovered this patch, I created the nmbug repository by hand and it was
possible to do it with bare repo from beginning. The following code
seems to work and is a bit simpler:

def init(remote=None):

Create an empty nmbug repository.

This wraps 'git init' with a few extra steps to support subsequent
status and commit commands.

_spawn(
args=['git', '--git-dir', NMBGIT, 'init', '--bare'],
wait=True)
# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
_git(args=['hash-object', '-w', '--stdin'], input='', wait=True)
_git(
args=[
'commit', '--allow-empty', '-m', 'Start a new nmbug repository'
],
additional_env={'GIT_WORK_TREE': NMBGIT},
wait=True)

Note that in the initial commit I set the work tree to NMBGIT. This is
because 'git commit' needs some work tree, but in our case it doesn't
matter which one because the commit is empty.

-Michal


 +# create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
 +_git(args=['hash-object', '-w', '--stdin'], input='', wait=True)
 +_git(
 +args=[
 +'commit', '--allow-empty', '-m', 'Start a new nmbug 
 repository'
 +],
 +additional_env={'GIT_WORK_TREE': workdir},
 +wait=True)
 +
 +
  def checkout():
  
  Update the notmuch database from Git.
 @@ -703,6 +726,7 @@ if __name__ == '__main__':
  'clone',
  'commit',
  'fetch',
 +'init',
  'log',
  'merge',
  'pull',
 -- 
 2.1.0.60.g85f0837

 ___
 notmuch mailing list
 notmuch@notmuchmail.org
 http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 2/2] doc: Minor fixes related to notmuch-address

2014-11-12 Thread Michal Sojka
- Add notmuch-address to HTML index
- Remove extra '*'
---
 doc/index.rst| 1 +
 doc/man1/notmuch.rst | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/doc/index.rst b/doc/index.rst
index ba6d5b4..3f0e6e6 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -8,6 +8,7 @@ Contents:
:titlesonly:

man1/notmuch
+   man1/notmuch-address
man1/notmuch-compact
man1/notmuch-config
man1/notmuch-count
diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst
index 98590a4..b33738e 100644
--- a/doc/man1/notmuch.rst
+++ b/doc/man1/notmuch.rst
@@ -129,7 +129,7 @@ SEE ALSO
 **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
 **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
 **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**,
-***notmuch-address(1)**
+**notmuch-address(1)**

 The notmuch website: **http://notmuchmail.org**

-- 
2.1.1



[PATCH 1/2] NEWS: notmuch address

2014-11-12 Thread Michal Sojka
---
 NEWS | 8 
 1 file changed, 8 insertions(+)

diff --git a/NEWS b/NEWS
index 8d7ed0a..16cc002 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,14 @@ Stopped `notmuch dump` failing if someone writes to the 
database
   limits output of message IDs to messages matching search terms that
   have at least `N` files associated with them.

+Added `notmuch address` subcommand
+
+  This new subcommand searches for messages matching the given search
+  terms, and prints the addresses from them. Duplicate addresses are
+  filtered out. The `--output` option controls which of the following
+  information is printed: sender addresses, recipient addresses and
+  count of duplicate addresses.
+
 Emacs Interface
 ---

-- 
2.1.1



[PATCH 1/2] NEWS: notmuch address

2014-11-12 Thread Michal Sojka
---
 NEWS | 8 
 1 file changed, 8 insertions(+)

diff --git a/NEWS b/NEWS
index 8d7ed0a..16cc002 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,14 @@ Stopped `notmuch dump` failing if someone writes to the 
database
   limits output of message IDs to messages matching search terms that
   have at least `N` files associated with them.
 
+Added `notmuch address` subcommand
+
+  This new subcommand searches for messages matching the given search
+  terms, and prints the addresses from them. Duplicate addresses are
+  filtered out. The `--output` option controls which of the following
+  information is printed: sender addresses, recipient addresses and
+  count of duplicate addresses.
+
 Emacs Interface
 ---
 
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 2/2] doc: Minor fixes related to notmuch-address

2014-11-12 Thread Michal Sojka
- Add notmuch-address to HTML index
- Remove extra '*'
---
 doc/index.rst| 1 +
 doc/man1/notmuch.rst | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/doc/index.rst b/doc/index.rst
index ba6d5b4..3f0e6e6 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -8,6 +8,7 @@ Contents:
:titlesonly:
 
man1/notmuch
+   man1/notmuch-address
man1/notmuch-compact
man1/notmuch-config
man1/notmuch-count
diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst
index 98590a4..b33738e 100644
--- a/doc/man1/notmuch.rst
+++ b/doc/man1/notmuch.rst
@@ -129,7 +129,7 @@ SEE ALSO
 **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
 **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
 **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**,
-***notmuch-address(1)**
+**notmuch-address(1)**
 
 The notmuch website: **http://notmuchmail.org**
 
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] cli: notmuch address option defaults update

2014-11-07 Thread Michal Sojka
On Thu, Nov 06 2014, Tomi Ollila wrote:
> When no --output option were given, change default to display senders
> only. This is faster and provides useful-enough list of addresses.
>
> When only --count option is given, display senders (in contrary to not
> displaying anything).
>
> Document how --count affects to "sort order" a bit more accurately.
>
> Clean up some whitespace in the documentation.
>
> One test updated to have --output=count without sender nor recipient
> output option.
> ---
>
> Some quick updates to the notmuch address interface which I hope will
> be considered to be included in 0.19 release. As we are in feature freeze
> I hope this gets quick feedback, in any way you desire.
>
> Tomi
>
>  doc/man1/notmuch-address.rst | 34 ++
>  notmuch-search.c |  4 ++--
>  test/T095-address.sh | 14 +++---
>  3 files changed, 27 insertions(+), 25 deletions(-)
>
> diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
> index 359616e0dc5f..034607c434d2 100644
> --- a/doc/man1/notmuch-address.rst
> +++ b/doc/man1/notmuch-address.rst
> @@ -32,28 +32,28 @@ Supported options for **address** include
>  ``--output=(sender|recipients|count)``
>  
>  Controls which information appears in the output. This option
> - can be given multiple times to combine different outputs.
> - Omitting this option is equivalent to
> - --output=sender --output=recipients.
> +can be given multiple times to combine different outputs.
> +Omitting this option is equivalent to --output=sender.

The implementation does a bit more. What about:

When neither --output=sender nor --output=recipients is given,
--output=sender is implied.

Otherwise it LGTM.

Thanks,
-Michal


Re: [PATCH] cli: notmuch address option defaults update

2014-11-07 Thread Michal Sojka
On Thu, Nov 06 2014, Tomi Ollila wrote:
 When no --output option were given, change default to display senders
 only. This is faster and provides useful-enough list of addresses.

 When only --count option is given, display senders (in contrary to not
 displaying anything).

 Document how --count affects to sort order a bit more accurately.

 Clean up some whitespace in the documentation.

 One test updated to have --output=count without sender nor recipient
 output option.
 ---

 Some quick updates to the notmuch address interface which I hope will
 be considered to be included in 0.19 release. As we are in feature freeze
 I hope this gets quick feedback, in any way you desire.

 Tomi

  doc/man1/notmuch-address.rst | 34 ++
  notmuch-search.c |  4 ++--
  test/T095-address.sh | 14 +++---
  3 files changed, 27 insertions(+), 25 deletions(-)

 diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
 index 359616e0dc5f..034607c434d2 100644
 --- a/doc/man1/notmuch-address.rst
 +++ b/doc/man1/notmuch-address.rst
 @@ -32,28 +32,28 @@ Supported options for **address** include
  ``--output=(sender|recipients|count)``
  
  Controls which information appears in the output. This option
 - can be given multiple times to combine different outputs.
 - Omitting this option is equivalent to
 - --output=sender --output=recipients.
 +can be given multiple times to combine different outputs.
 +Omitting this option is equivalent to --output=sender.

The implementation does a bit more. What about:

When neither --output=sender nor --output=recipients is given,
--output=sender is implied.

Otherwise it LGTM.

Thanks,
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 06/10] cli: Introduce "notmuch address" command

2014-11-05 Thread Michal Sojka
On Wed, Nov 05 2014, Mark Walters wrote:
> On Tue, 04 Nov 2014, Michal Sojka  wrote:
>> On Tue, Nov 04 2014, Mark Walters wrote:
>>> On Mon, 03 Nov 2014, Michal Sojka  wrote:
>>>> This moves address-related functionality from search command to the
>>>> new address command. The implementation shares almost all code and
>>>> some command line options.
>>>>
>>>> Options --offset and --limit were intentionally not included in the
>>>> address command, because they refer to messages numbers, which users
>>>> do not see in the output. This could confuse users because, for
>>>> example, they could see more addresses in the output that what was
>>>> specified with --limit. This functionality can be correctly
>>>> reimplemented for addresses later.
>>>
>>> I am not sure about this: we already have this anomaly for output=files
>>> say. Also I can imagine calling notmuch address --limit=1000 ... to get
>>> a bunch of recent addresses quickly and I really am wanting to look at
>>> 1000 messages, not collect 1000 addresses.
>>
>> I think that one of the reasons for having the new "address" command is
>> to have cleaner user interface. And including "anomalies" doesn't sound
>> like a way to achieve this. I think that now you can use "date:" query
>> to limit the search.
>>
>> I volunteer to implement "address --limit" properly after 0.19. This
>> should be easy.
>
> I think this depends on how you view limit: is it to limit the output
> (roughly to run "head" on the output), or is to bound the amount of work
> notmuch has to do (eg to make sure you don't get a long delay). Your
> suggestion is definitely the former, whereas I am more worried about the
> latter: limit in your definition could take an essentially unbounded
> amount of time.

Why? If I understand you correctly, you think of limit in terms of
messages. There is 1:N mapping between messages and addresses, where
N?>=?1. If I limit the number of printed addresses, I limit the number
of messages as well. Only if N is zero (which probably can be the case
with Bcc and --output=recipients) then it can result in unbounded work
(provided you have infinite number of Bcc only messages in your
database?:-)).

Do I miss something?

-Michal


[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2014-11-05 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  1 +
 doc/man1/notmuch-address.rst   | 36 ++-
 notmuch-search.c   | 51 --
 test/T097-address-filter-by.sh | 73 ++
 5 files changed, 162 insertions(+), 5 deletions(-)
 create mode 100755 test/T097-address-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index db152f3..2cb1586 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -310,7 +310,7 @@ _notmuch_search()
 ! $split &&
 case "${cur}" in
-*)
-   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate="
+   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by="
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
@@ -343,6 +343,10 @@ _notmuch_address()
COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W "nameaddr name addr addrfold 
nameaddrfold" -- "${cur}" ) )
+   return
+   ;;
 esac

 ! $split &&
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 8968562..3758f1a 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -62,6 +62,7 @@ _notmuch_address()
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
 '--output=[select what to output]:output:((sender recipients count))'
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both 
name and address part" name\:"name part" addr\:"address part" 
addrfold\:"case-insensitive address part" nameaddrfold\:"name and 
case-insensitive address part"))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 359616e..00582c3 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,8 @@ DESCRIPTION
 ===

 Search for messages matching the given search terms, and display the
-addresses from them. Duplicate addresses are filtered out.
+addresses from them. Duplicate addresses are filtered out. Filtering
+can be configured with the --filter-by option.

 See **notmuch-search-terms(7)** for details of the supported syntax for
 .
@@ -77,6 +78,39 @@ Supported options for **address** include
 **false** allows excluded messages to match search terms and
 appear in displayed results.

+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Controls how to filter out duplicate addresses. The filtering
+   algorithm receives a sequence of email addresses and outputs
+   the same sequence without the addresses that are considered a
+   duplicate of a previously output address. What is considered a
+   duplicate depends on how the two addresses are compared:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "John Doe " will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For example, the
+   addresses "John Doe " and "John Doe
+   " will be considered duplicate.
+
 EXIT STATUS
 ===

diff --git a/notmuch-search.c b/notmuch-search.c
index 5036d8e..246ec0a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -43,6 +43,14 @@ typedef enum {
 NOTMUCH_FORMAT_SEXP
 } format_sel_t;

+typedef enum {
+FILTER_BY_NAMEADDR = 0,
+FILTER_BY_NAME,
+FILTER_BY_ADDR,
+FILTER_BY_ADDRFOLD,
+FILTER_BY_NAMEADDRFOLD,
+} filter_by_t;
+
 typedef struct {
 notmuch_database_t *notmuch;
 format_sel_t format_sel;

[PATCH v3 09/10] cli: address: Add --output=count

2014-11-05 Thread Michal Sojka
This output prints how many times was each address encountered during
search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-address.rst   | 11 -
 notmuch-search.c   | 49 --
 test/T095-address.sh   | 49 ++
 5 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 94ea2d5..db152f3 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -332,7 +332,7 @@ _notmuch_address()
return
;;
--output)
-   COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+   COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index c606b75..8968562 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -61,7 +61,7 @@ _notmuch_address()
 {
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
-'--output=[select what to output]:output:((sender recipients))'
+'--output=[select what to output]:output:((sender recipients count))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 01eb811..359616e 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -29,7 +29,7 @@ Supported options for **address** include
 intended for programs that invoke **notmuch(1)** internally. If
 omitted, the latest supported version will be used.

-``--output=(sender|recipients)``
+``--output=(sender|recipients|count)``

 Controls which information appears in the output. This option
can be given multiple times to combine different outputs.
@@ -48,6 +48,13 @@ Supported options for **address** include
 Output all addresses from the *To*, *Cc* and *Bcc*
 headers.

+   **count**
+   Print the count of how many times was the address
+   encountered during search.
+
+   Note: With this option, addresses are printed only after
+   the whole search is finished. This may take long time.
+
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
@@ -56,6 +63,8 @@ Supported options for **address** include
 By default, results will be displayed in reverse chronological
 order, (that is, the newest results will be displayed first).

+   This option has no effect when used with --output=count.
+
 ``--exclude=(true|false)``
 A message is called "excluded" if it matches at least one tag in
 search.tag\_exclude that does not appear explicitly in the
diff --git a/notmuch-search.c b/notmuch-search.c
index 86d54ba..5036d8e 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -33,6 +33,7 @@ typedef enum {
 /* Address command */
 OUTPUT_SENDER  = 1 << 5,
 OUTPUT_RECIPIENTS  = 1 << 6,
+OUTPUT_COUNT   = 1 << 7,
 } output_t;

 typedef enum {
@@ -59,6 +60,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;

 /* Return two stable query strings that identify exactly the matched
@@ -248,17 +250,24 @@ is_duplicate (const search_context_t *ctx, const char 
*name, const char *addr)
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;

 key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
 if (! key)
return FALSE;

-duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, 
(gpointer));

-if (! duplicate)
-   g_hash_table_insert (ctx->addresses, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (ctx->format, mailbox_t);
+   mailbox->name = talloc_strdup (mailbox, name);
+   mailbox->addr = talloc_strdup (mailbox, addr);
+   mailbox->count = 1;
+   g_hash_table_insert (ctx->addresses, key, mailbox);
+} else {
+   mailbox->count++;
talloc_free (key);
+}

 return duplicate;
 }
@@ -268,6 +277,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+int count = mailbox->count;
 sprinter_t *format = ctx->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
@@ -277,6 +287,10 @@ print_mailbox (const search_context_t *ctx, const 
mailbox_t *mailbox)
 name_addr = 

[PATCH v3 08/10] cli: address: Do not output duplicate addresses

2014-11-05 Thread Michal Sojka
This filters out duplicate addresses from address command output.

It also also adds tests for the address command.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-address.rst |  2 +-
 notmuch-search.c | 42 ++-
 test/T095-address.sh | 99 
 3 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100755 test/T095-address.sh

diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index d349237..01eb811 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,7 @@ DESCRIPTION
 ===

 Search for messages matching the given search terms, and display the
-addresses from them.
+addresses from them. Duplicate addresses are filtered out.

 See **notmuch-search-terms(7)** for details of the supported syntax for
 .
diff --git a/notmuch-search.c b/notmuch-search.c
index e084d25..86d54ba 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -53,6 +53,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
+GHashTable *addresses;
 } search_context_t;

 typedef struct {
@@ -240,6 +241,28 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }

+/* Returns TRUE iff name and addr is duplicate. If not, stores the
+ * name/addr pair in order to detect subsequent duplicates. */
+static notmuch_bool_t
+is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (ctx->addresses, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
@@ -274,7 +297,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_context_t *ctx, InternetAddressList *list)
+process_address_list (const search_context_t *ctx,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -298,6 +322,9 @@ process_address_list (const search_context_t *ctx, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

+   if (is_duplicate (ctx, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (ctx, );
}
 }
@@ -321,6 +348,13 @@ process_address_header (const search_context_t *ctx, const 
char *value)
 g_object_unref (list);
 }

+/* Destructor for talloc-allocated GHashTable keys and values. */
+static void
+_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
+}
+
 static int
 _count_filenames (notmuch_message_t *message)
 {
@@ -673,8 +707,14 @@ notmuch_address_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

+ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+   _talloc_free_for_g_hash, NULL);
+
 ret = do_search_messages (ctx);

+g_hash_table_unref (ctx->addresses);
+
+
 _notmuch_search_cleanup (ctx);

 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/test/T095-address.sh b/test/T095-address.sh
new file mode 100755
index 000..0d47c0d
--- /dev/null
+++ b/test/T095-address.sh
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+test_description='"notmuch address" in several variants'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "--output=sender"
+notmuch address --output=sender '*' >OUTPUT
+cat OUTPUT
+cat 

[PATCH v3 07/10] cli: search: Convert --output to keyword argument

2014-11-05 Thread Michal Sojka
Now, when address related outputs are in a separate command, it makes
no sense to combine multiple --output options in search command line.
Using switch statement to handle different outputs is more readable
than a series of if statements.
---
 doc/man1/notmuch-search.rst |  3 ---
 notmuch-search.c| 25 +
 2 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 65df288..0cc2911 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -78,9 +78,6 @@ Supported options for **search** include
 by null characters (--format=text0), as a JSON array
 (--format=json), or as an S-Expression list (--format=sexp).

-   This option can be given multiple times to combine different
-   outputs.
-
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
diff --git a/notmuch-search.c b/notmuch-search.c
index 69938d6..e084d25 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -587,7 +587,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 int opt_index, ret;

 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o',
+   { NOTMUCH_OPT_KEYWORD, >output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
{ 0, 0, 0, 0, 0 }
 };

+ctx->output = OUTPUT_SUMMARY;
 opt_index = parse_arguments (argc, argv, options, 1);
 if (opt_index < 0)
return EXIT_FAILURE;

-if (! ctx->output)
-   ctx->output = OUTPUT_SUMMARY;
-
 if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
ctx->dupe != -1) {
 fprintf (stderr, "Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n");
@@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

-if (ctx->output == OUTPUT_SUMMARY ||
-   ctx->output == OUTPUT_THREADS)
+switch (ctx->output) {
+case OUTPUT_SUMMARY:
+case OUTPUT_THREADS:
ret = do_search_threads (ctx);
-else if (ctx->output == OUTPUT_MESSAGES ||
-ctx->output == OUTPUT_FILES)
+   break;
+case OUTPUT_MESSAGES:
+case OUTPUT_FILES:
ret = do_search_messages (ctx);
-else if (ctx->output == OUTPUT_TAGS)
+   break;
+case OUTPUT_TAGS:
ret = do_search_tags (ctx);
-else {
-   fprintf (stderr, "Error: the combination of outputs is not 
supported.\n");
-   ret = 1;
+   break;
+default:
+   INTERNAL_ERROR ("Unexpected output");
 }

 _notmuch_search_cleanup (ctx);
-- 
2.1.1



[PATCH v3 06/10] cli: Introduce "notmuch address" command

2014-11-05 Thread Michal Sojka
This moves address-related functionality from search command to the
new address command. The implementation shares almost all code and
some command line options.

Options --offset and --limit were intentionally not included in the
address command, because they refer to messages numbers, which users
do not see in the output. This could confuse users because, for
example, they could see more addresses in the output that what was
specified with --limit. This functionality can be correctly
reimplemented for address subcommand later.

Also useless values of --exclude flag were not included in the address
command.

This was inspired by a patch from Jani Nikula.
---
 completion/notmuch-completion.bash | 42 -
 completion/notmuch-completion.zsh  | 10 +++-
 doc/man1/notmuch-address.rst   | 89 
 doc/man1/notmuch-search.rst| 20 +---
 doc/man1/notmuch.rst   |  7 +--
 notmuch-client.h   |  3 ++
 notmuch-search.c   | 93 +++---
 notmuch.c  |  2 +
 8 files changed, 216 insertions(+), 50 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..94ea2d5 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -294,7 +294,7 @@ _notmuch_search()
return
;;
--output)
-   COMPREPLY=( $( compgen -W "summary threads messages files tags 
sender recipients" -- "${cur}" ) )
+   COMPREPLY=( $( compgen -W "summary threads messages files tags" -- 
"${cur}" ) )
return
;;
--sort)
@@ -320,6 +320,44 @@ _notmuch_search()
 esac
 }

+_notmuch_address()
+{
+local cur prev words cword split
+_init_completion -s || return
+
+$split &&
+case "${prev}" in
+   --format)
+   COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
+   return
+   ;;
+   --output)
+   COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+   return
+   ;;
+   --sort)
+   COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) 
)
+   return
+   ;;
+   --exclude)
+   COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
+   return
+   ;;
+esac
+
+! $split &&
+case "${cur}" in
+   -*)
+   local options="--format= --output= --sort= --exclude="
+   compopt -o nospace
+   COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
+   ;;
+   *)
+   _notmuch_search_terms
+   ;;
+esac
+}
+
 _notmuch_show()
 {
 local cur prev words cword split
@@ -393,7 +431,7 @@ _notmuch_tag()

 _notmuch()
 {
-local _notmuch_commands="compact config count dump help insert new reply 
restore search setup show tag"
+local _notmuch_commands="compact config count dump help insert new reply 
restore search address setup show tag"
 local arg cur prev words cword split

 # require bash-completion with _init_completion
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..c606b75 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -10,6 +10,7 @@ _notmuch_commands()
 'setup:interactively set up notmuch for first use'
 'new:find and import any new message to the database'
 'search:search for messages matching the search terms, display matching 
threads as results'
+'address:get addresses from messages matching the given search terms'
 'reply:constructs a reply template for a set of messages'
 'show:show all messages matching the search terms'
 'tag:add or remove tags for all messages matching the search terms'
@@ -53,7 +54,14 @@ _notmuch_search()
 '--max-threads=[display only the first x threads from the search 
results]:number of threads to show: ' \
 '--first=[omit the first x threads from the search results]:number of 
threads to omit: ' \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
-'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients))'
+'--output=[select what to output]:output:((summary threads messages files 
tags))'
+}
+
+_notmuch_address()
+{
+  _arguments -s : \
+'--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
+'--output=[select what to output]:output:((sender recipients))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
new file mode 100644
index 000..d349237
--- /dev/null
+++ b/doc/man1/notmuch-address.rst
@@ -0,0 +1,89 @@
+===
+notmuch-address
+===
+
+SYNOPSIS
+

[PATCH v3 05/10] cli: add support for hierarchical command line option arrays

2014-11-05 Thread Michal Sojka
From: Jani Nikula 

NOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in
output_var.

The "Unrecognized option" message was moved out of parse_option() to
not be emitted twice or when parsing a non-inherited option.
---
 command-line-arguments.c | 16 +---
 command-line-arguments.h |  1 +
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index c6f7269..de6b453 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int 
pos_arg_index,
  */

 notmuch_bool_t
-parse_option (const char *arg,
- const notmuch_opt_desc_t *options) {
-
-assert(arg);
+parse_option (const char *_arg, const notmuch_opt_desc_t *options)
+{
+assert(_arg);
 assert(options);

-arg += 2;
-
+const char *arg = _arg + 2; /* _arg starts with -- */
 const notmuch_opt_desc_t *try;
 for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) {
+   if (try->opt_type == NOTMUCH_OPT_INHERIT &&
+   parse_option (_arg, try->output_var))
+   return TRUE;
+
if (! try->name)
continue;

@@ -170,7 +172,6 @@ parse_option (const char *arg,
/*UNREACHED*/
}
 }
-fprintf (stderr, "Unrecognized option: --%s\n", arg);
 return FALSE;
 }

@@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv,
if (more_args) {
opt_index++;
} else {
+   fprintf (stderr, "Unrecognized option: %s\n", argv[opt_index]);
opt_index = -1;
}

diff --git a/command-line-arguments.h b/command-line-arguments.h
index 6444129..309aaf2 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -5,6 +5,7 @@

 enum notmuch_opt_type {
 NOTMUCH_OPT_END = 0,
+NOTMUCH_OPT_INHERIT,   /* another options table */
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
-- 
2.1.1



[PATCH v3 04/10] cli: search: Split notmuch_search_command to smaller functions

2014-11-05 Thread Michal Sojka
In a subsequent commit, these functions will be used to share some
functionality between search and address commands.
---
 notmuch-search.c | 155 ++-
 1 file changed, 86 insertions(+), 69 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6765a16..f115359 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx)
 return 0;
 }

+static int
+_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int 
argc, char *argv[])
+{
+char *query_str;
+unsigned int i;
+
+switch (ctx->format_sel) {
+case NOTMUCH_FORMAT_TEXT:
+   ctx->format = sprinter_text_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_TEXT0:
+   if (ctx->output == OUTPUT_SUMMARY) {
+   fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
+   return EXIT_FAILURE;
+   }
+   ctx->format = sprinter_text0_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_JSON:
+   ctx->format = sprinter_json_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_SEXP:
+   ctx->format = sprinter_sexp_create (config, stdout);
+   break;
+default:
+   /* this should never happen */
+   INTERNAL_ERROR("no output format selected");
+}
+
+notmuch_exit_if_unsupported_format ();
+
+if (notmuch_database_open (notmuch_config_get_database_path (config),
+  NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch))
+   return EXIT_FAILURE;
+
+query_str = query_string_from_args (ctx->notmuch, argc, argv);
+if (query_str == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return EXIT_FAILURE;
+}
+if (*query_str == '\0') {
+   fprintf (stderr, "Error: notmuch search requires at least one search 
term.\n");
+   return EXIT_FAILURE;
+}
+
+ctx->query = notmuch_query_create (ctx->notmuch, query_str);
+if (ctx->query == NULL) {
+   fprintf (stderr, "Out of memory\n");
+   return EXIT_FAILURE;
+}
+
+notmuch_query_set_sort (ctx->query, ctx->sort);
+
+if (ctx->exclude == NOTMUCH_EXCLUDE_FLAG && ctx->output != OUTPUT_SUMMARY) 
{
+   /* If we are not doing summary output there is nowhere to
+* print the excluded flag so fall back on including the
+* excluded messages. */
+   fprintf (stderr, "Warning: this output format cannot flag excluded 
messages.\n");
+   ctx->exclude = NOTMUCH_EXCLUDE_FALSE;
+}
+
+if (ctx->exclude != NOTMUCH_EXCLUDE_FALSE) {
+   const char **search_exclude_tags;
+   size_t search_exclude_tags_length;
+
+   search_exclude_tags = notmuch_config_get_search_exclude_tags
+   (config, _exclude_tags_length);
+   for (i = 0; i < search_exclude_tags_length; i++)
+   notmuch_query_add_tag_exclude (ctx->query, search_exclude_tags[i]);
+   notmuch_query_set_omit_excluded (ctx->query, ctx->exclude);
+}
+
+return 0;
+}
+
+static void
+_notmuch_search_cleanup (search_context_t *ctx)
+{
+notmuch_query_destroy (ctx->query);
+notmuch_database_destroy (ctx->notmuch);
+
+talloc_free (ctx->format);
+}
+
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
@@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
.dupe = -1,
 };
 search_context_t *ctx = _context;
-char *query_str;
 int opt_index, ret;
-unsigned int i;

 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, >sort, "sort", 's',
@@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 return EXIT_FAILURE;
 }

-switch (ctx->format_sel) {
-case NOTMUCH_FORMAT_TEXT:
-   ctx->format = sprinter_text_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_TEXT0:
-   if (ctx->output == OUTPUT_SUMMARY) {
-   fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
-   return EXIT_FAILURE;
-   }
-   ctx->format = sprinter_text0_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_JSON:
-   ctx->format = sprinter_json_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_SEXP:
-   ctx->format = sprinter_sexp_create (config, stdout);
-   break;
-default:
-   /* this should never happen */
-   INTERNAL_ERROR("no output format selected");
-}
-
-notmuch_exit_if_unsupported_format ();
-
-if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch))
+if (_notmuch_search_prepare (ctx, config,
+argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

-query_str = query_string_from_args (ctx->notmuch, argc-opt_index, 
argv+opt_index);
-if 

[PATCH v3 03/10] cli: search: Convert ctx. to ctx->

2014-11-05 Thread Michal Sojka
In the next commit, notmuch_search_command will be refactored to
several smaller functions. In order to simplify the next commit to
verbatim move of several lines to new functions with search_context_t*
argument, we convert all references to this structure to pointer
dereferences. To do so we rename the context variable and use the
original name ctx as the pointer to the renamed structure.
---
 notmuch-search.c | 81 
 1 file changed, 41 insertions(+), 40 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3d2012b..6765a16 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx)
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-search_context_t ctx = {
+search_context_t search_context = {
.format_sel = NOTMUCH_FORMAT_TEXT,
.exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
@@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
.limit = -1, /* unlimited */
.dupe = -1,
 };
+search_context_t *ctx = _context;
 char *query_str;
 int opt_index, ret;
 unsigned int i;

 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD, , "sort", 's',
+   { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's',
  (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
  { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
+   { NOTMUCH_OPT_KEYWORD, >format_sel, "format", 'f',
  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
  { "sexp", NOTMUCH_FORMAT_SEXP },
  { "text", NOTMUCH_FORMAT_TEXT },
  { "text0", NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, _format_version, "format-version", 0, 0 },
-   { NOTMUCH_OPT_KEYWORD_FLAGS, , "output", 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
  { "files", OUTPUT_FILES },
  { "tags", OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
+{ NOTMUCH_OPT_KEYWORD, >exclude, "exclude", 'x',
   (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
   { "false", NOTMUCH_EXCLUDE_FALSE },
   { "flag", NOTMUCH_EXCLUDE_FLAG },
   { "all", NOTMUCH_EXCLUDE_ALL },
   { 0, 0 } } },
-   { NOTMUCH_OPT_INT, , "offset", 'O', 0 },
-   { NOTMUCH_OPT_INT, , "limit", 'L', 0  },
-   { NOTMUCH_OPT_INT, , "duplicate", 'D', 0  },
+   { NOTMUCH_OPT_INT, >offset, "offset", 'O', 0 },
+   { NOTMUCH_OPT_INT, >limit, "limit", 'L', 0  },
+   { NOTMUCH_OPT_INT, >dupe, "duplicate", 'D', 0  },
{ 0, 0, 0, 0, 0 }
 };

@@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 if (opt_index < 0)
return EXIT_FAILURE;

-if (! ctx.output)
-   ctx.output = OUTPUT_SUMMARY;
+if (! ctx->output)
+   ctx->output = OUTPUT_SUMMARY;

-if (ctx.output != OUTPUT_FILES && ctx.output != OUTPUT_MESSAGES &&
-   ctx.dupe != -1) {
+if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
+   ctx->dupe != -1) {
 fprintf (stderr, "Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n");
 return EXIT_FAILURE;
 }

-switch (ctx.format_sel) {
+switch (ctx->format_sel) {
 case NOTMUCH_FORMAT_TEXT:
-   ctx.format = sprinter_text_create (config, stdout);
+   ctx->format = sprinter_text_create (config, stdout);
break;
 case NOTMUCH_FORMAT_TEXT0:
-   if (ctx.output == OUTPUT_SUMMARY) {
+   if (ctx->output == OUTPUT_SUMMARY) {
fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
return EXIT_FAILURE;
}
-   ctx.format = sprinter_text0_create (config, stdout);
+   ctx->format = sprinter_text0_create (config, stdout);
break;
 case NOTMUCH_FORMAT_JSON:
-   ctx.format = sprinter_json_create (config, stdout);
+   ctx->format = sprinter_json_create (config, stdout);
break;
 case NOTMUCH_FORMAT_SEXP:
-   ctx.format = sprinter_sexp_create (config, stdout);
+

[PATCH v3 02/10] cli: search: Move more variables into search_context_t

2014-11-05 Thread Michal Sojka
In order to share some command line options between search and address
subcommands we need to add corresponding variables to the context
structure. While we are at it, we also add notmuch_database_t to unify
parameters of all do_search_* functions and to simplify subsequent
commits.

Otherwise, there are no functional changes.
---
 notmuch-search.c | 49 ++---
 1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 2c47b80..3d2012b 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -34,8 +34,18 @@ typedef enum {

 #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)

+typedef enum {
+NOTMUCH_FORMAT_JSON,
+NOTMUCH_FORMAT_TEXT,
+NOTMUCH_FORMAT_TEXT0,
+NOTMUCH_FORMAT_SEXP
+} format_sel_t;
+
 typedef struct {
+notmuch_database_t *notmuch;
+format_sel_t format_sel;
 sprinter_t *format;
+notmuch_exclude_t exclude;
 notmuch_query_t *query;
 notmuch_sort_t sort;
 output_t output;
@@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx)
 }

 static int
-do_search_tags (notmuch_database_t *notmuch,
-   const search_context_t *ctx)
+do_search_tags (const search_context_t *ctx)
 {
 notmuch_messages_t *messages = NULL;
 notmuch_tags_t *tags;
 const char *tag;
 sprinter_t *format = ctx->format;
 notmuch_query_t *query = ctx->query;
+notmuch_database_t *notmuch = ctx->notmuch;

 /* should the following only special case if no excluded terms
  * specified? */
@@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch,
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-notmuch_database_t *notmuch;
 search_context_t ctx = {
+   .format_sel = NOTMUCH_FORMAT_TEXT,
+   .exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
.output = 0,
.offset = 0,
@@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 };
 char *query_str;
 int opt_index, ret;
-notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE;
 unsigned int i;

-enum {
-   NOTMUCH_FORMAT_JSON,
-   NOTMUCH_FORMAT_TEXT,
-   NOTMUCH_FORMAT_TEXT0,
-   NOTMUCH_FORMAT_SEXP
-} format_sel = NOTMUCH_FORMAT_TEXT;
-
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, , "sort", 's',
  (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
  { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
+   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
  { "sexp", NOTMUCH_FORMAT_SEXP },
  { "text", NOTMUCH_FORMAT_TEXT },
@@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "files", OUTPUT_FILES },
  { "tags", OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
+{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
   (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
   { "false", NOTMUCH_EXCLUDE_FALSE },
   { "flag", NOTMUCH_EXCLUDE_FLAG },
@@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 return EXIT_FAILURE;
 }

-switch (format_sel) {
+switch (ctx.format_sel) {
 case NOTMUCH_FORMAT_TEXT:
ctx.format = sprinter_text_create (config, stdout);
break;
@@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 notmuch_exit_if_unsupported_format ();

 if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, ))
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ))
return EXIT_FAILURE;

-query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
+query_str = query_string_from_args (ctx.notmuch, argc-opt_index, 
argv+opt_index);
 if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return EXIT_FAILURE;
@@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
return EXIT_FAILURE;
 }

-ctx.query = notmuch_query_create (notmuch, query_str);
+ctx.query = notmuch_query_create (ctx.notmuch, query_str);
 if (ctx.query == NULL) {
fprintf (stderr, "Out of memory\n");
return EXIT_FAILURE;
@@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])

 notmuch_query_set_sort (ctx.query, ctx.sort);

-if 

[PATCH v3 01/10] cli: search: Rename options to context

2014-11-05 Thread Michal Sojka
In the next commit the options structure will be extended by
non-option variables. Therefore we need a more generic name.

Just text replacement, no other changes.
---
 notmuch-search.c | 142 +++
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6345fb6..2c47b80 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -42,7 +42,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
-} search_options_t;
+} search_context_t;

 typedef struct {
 const char *name;
@@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread,
 }

 static int
-do_search_threads (search_options_t *opt)
+do_search_threads (search_context_t *ctx)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 time_t date;
 int i;

-if (opt->offset < 0) {
-   opt->offset += notmuch_query_count_threads (opt->query);
-   if (opt->offset < 0)
-   opt->offset = 0;
+if (ctx->offset < 0) {
+   ctx->offset += notmuch_query_count_threads (ctx->query);
+   if (ctx->offset < 0)
+   ctx->offset = 0;
 }

-threads = notmuch_query_search_threads (opt->query);
+threads = notmuch_query_search_threads (ctx->query);
 if (threads == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset 
+ opt->limit);
+notmuch_threads_valid (threads) && (ctx->limit < 0 || i < ctx->offset 
+ ctx->limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);

-   if (i < opt->offset) {
+   if (i < ctx->offset) {
notmuch_thread_destroy (thread);
continue;
}

-   if (opt->output == OUTPUT_THREADS) {
+   if (ctx->output == OUTPUT_THREADS) {
format->set_prefix (format, "thread");
format->string (format,
notmuch_thread_get_thread_id (thread));
@@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt)

format->begin_map (format);

-   if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (ctx->sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt)
 }

 static void
-print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
+print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;

@@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_context_t *ctx, InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;

-   process_address_list (opt, group_list);
+   process_address_list (ctx, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

-   print_mailbox (opt, );
+   print_mailbox (ctx, );
}
 }
 }

 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_context_t *ctx, const char *value)
 {
 InternetAddressList *list;

@@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const 
char *value)
 if (list == NULL)
return;

-process_address_list (opt, list);
+process_address_list (ctx, list);

 g_object_unref (list);
 }
@@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message)
 }

 static int
-do_search_messages (search_options_t *opt)
+do_search_messages (search_context_t *ctx)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 int i;

-if (opt->offset < 0) {
-   opt->offset += notmuch_query_count_messages (opt->query);
-   if 

[PATCH v3 00/10] "notmuch address" command

2014-11-05 Thread Michal Sojka
Hi all,

this is v3 of "notmuch address" patchset. It obsoletes [1].

I think I addressed all comments made to v2. The diff between v2 and
v3 is below. Besides this, I improved commit messages.

I also tried to get rid of global variables in 6/10, but it looked
ugly, because the definition of keywords (e.g. "text", "json", ...)
had to be outside of option definition. So I kept the code as it was
in v2.

It seems that the agreement is to merge patches 1-9 for 0.19 and leave
patch 10 for further discussion.

Thanks
-Michal

[1] id:1415058622-21162-1-git-send-email-sojkam1 at fel.cvut.cz


Jani Nikula (1):
  cli: add support for hierarchical command line option arrays

Michal Sojka (9):
  cli: search: Rename options to context
  cli: search: Move more variables into search_context_t
  cli: search: Convert ctx. to ctx->
  cli: search: Split notmuch_search_command to smaller functions
  cli: Introduce "notmuch address" command
  cli: search: Convert --output to keyword argument
  cli: address: Do not output duplicate addresses
  cli: address: Add --output=count
  cli: address: Add --filter-by option to configure address filtering

 command-line-arguments.c   |  16 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |  48 +++-
 completion/notmuch-completion.zsh  |  11 +-
 doc/man1/notmuch-address.rst   | 132 +++
 doc/man1/notmuch-search.rst|  21 +-
 doc/man1/notmuch.rst   |   7 +-
 notmuch-client.h   |   3 +
 notmuch-search.c   | 461 ++---
 notmuch.c  |   2 +
 test/T095-address.sh   | 148 
 test/T097-address-filter-by.sh |  73 ++
 12 files changed, 750 insertions(+), 173 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst
 create mode 100755 test/T095-address.sh
 create mode 100755 test/T097-address-filter-by.sh

-- 
2.1.1

diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 524ab91..00582c3 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -30,7 +30,7 @@ Supported options for **address** include
 intended for programs that invoke **notmuch(1)** internally. If
 omitted, the latest supported version will be used.

-``--output=(sender|recipients)``
+``--output=(sender|recipients|count)``

 Controls which information appears in the output. This option
can be given multiple times to combine different outputs.
@@ -64,7 +64,9 @@ Supported options for **address** include
 By default, results will be displayed in reverse chronological
 order, (that is, the newest results will be displayed first).

-``--exclude=(true|false|all|flag)``
+   This option has no effect when used with --output=count.
+
+``--exclude=(true|false)``
 A message is called "excluded" if it matches at least one tag in
 search.tag\_exclude that does not appear explicitly in the
 search terms. This option specifies whether to omit excluded
@@ -73,18 +75,8 @@ Supported options for **address** include
 The default value, **true**, prevents excluded messages from
 matching the search terms.

-**all** additionally prevents excluded messages from appearing
-in displayed results, in effect behaving as though the excluded
-messages do not exist.
-
 **false** allows excluded messages to match search terms and
-appear in displayed results. Excluded messages are still marked
-in the relevant outputs.
-
-**flag** only has an effect when ``--output=summary``. The
-output is almost identical to **false**, but the "match count"
-is the number of matching non-excluded messages in the thread,
-rather than the number of matching messages.
+appear in displayed results.

 ``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)

diff --git a/notmuch-search.c b/notmuch-search.c
index 04e33c6..246ec0a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -252,7 +252,9 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }

-/* Returns TRUE iff name and/or addr is considered duplicate. */
+/* Returns TRUE iff name and/or addr is considered duplicate. If not,
+ * stores the name/addr pair in order to detect subsequent
+ * duplicates. */
 static notmuch_bool_t
 is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
 {
@@ -405,8 +407,9 @@ process_address_header (const search_context_t *ctx, const 
char *value)
 g_object_unref (list);
 }

+/* Destructor for talloc-allocated GHashTable keys and values. */
 static void
-_my_talloc_free_for_g_hash (void *ptr)
+_talloc_free_for_g_hash (void *ptr)
 {
 talloc_free (ptr);
 }
@@ -679,12 +682,6 @@ static const notmuch_opt_des

Re: [PATCH v2 06/10] cli: Introduce notmuch address command

2014-11-05 Thread Michal Sojka
On Wed, Nov 05 2014, Mark Walters wrote:
 On Tue, 04 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 On Tue, Nov 04 2014, Mark Walters wrote:
 On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 This moves address-related functionality from search command to the
 new address command. The implementation shares almost all code and
 some command line options.

 Options --offset and --limit were intentionally not included in the
 address command, because they refer to messages numbers, which users
 do not see in the output. This could confuse users because, for
 example, they could see more addresses in the output that what was
 specified with --limit. This functionality can be correctly
 reimplemented for addresses later.

 I am not sure about this: we already have this anomaly for output=files
 say. Also I can imagine calling notmuch address --limit=1000 ... to get
 a bunch of recent addresses quickly and I really am wanting to look at
 1000 messages, not collect 1000 addresses.

 I think that one of the reasons for having the new address command is
 to have cleaner user interface. And including anomalies doesn't sound
 like a way to achieve this. I think that now you can use date: query
 to limit the search.

 I volunteer to implement address --limit properly after 0.19. This
 should be easy.

 I think this depends on how you view limit: is it to limit the output
 (roughly to run head on the output), or is to bound the amount of work
 notmuch has to do (eg to make sure you don't get a long delay). Your
 suggestion is definitely the former, whereas I am more worried about the
 latter: limit in your definition could take an essentially unbounded
 amount of time.

Why? If I understand you correctly, you think of limit in terms of
messages. There is 1:N mapping between messages and addresses, where
N = 1. If I limit the number of printed addresses, I limit the number
of messages as well. Only if N is zero (which probably can be the case
with Bcc and --output=recipients) then it can result in unbounded work
(provided you have infinite number of Bcc only messages in your
database :-)).

Do I miss something?

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 06/10] cli: Introduce "notmuch address" command

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, Mark Walters wrote:
> On Mon, 03 Nov 2014, Michal Sojka  wrote:
>> This moves address-related functionality from search command to the
>> new address command. The implementation shares almost all code and
>> some command line options.
>>
>> Options --offset and --limit were intentionally not included in the
>> address command, because they refer to messages numbers, which users
>> do not see in the output. This could confuse users because, for
>> example, they could see more addresses in the output that what was
>> specified with --limit. This functionality can be correctly
>> reimplemented for addresses later.
>
> I am not sure about this: we already have this anomaly for output=files
> say. Also I can imagine calling notmuch address --limit=1000 ... to get
> a bunch of recent addresses quickly and I really am wanting to look at
> 1000 messages, not collect 1000 addresses.

I think that one of the reasons for having the new "address" command is
to have cleaner user interface. And including "anomalies" doesn't sound
like a way to achieve this. I think that now you can use "date:" query
to limit the search.

I volunteer to implement "address --limit" properly after 0.19. This
should be easy.

-Michal

> Additionally, the 1000 message approach makes sense when we start
> deduping whereas 1000 authors becomes unclear.
>
>>
>> This was inspired by a patch from Jani Nikula.
>> ---
>>  completion/notmuch-completion.bash |  42 ++-
>>  completion/notmuch-completion.zsh  |  10 +++-
>>  doc/man1/notmuch-address.rst   |  99 
>> 
>>  doc/man1/notmuch-search.rst|  20 +---
>>  doc/man1/notmuch.rst   |   7 +--
>>  notmuch-client.h   |   3 ++
>>  notmuch-search.c   | 101 
>> +
>>  notmuch.c  |   2 +
>>  8 files changed, 228 insertions(+), 56 deletions(-)
>>  create mode 100644 doc/man1/notmuch-address.rst
>>
>> diff --git a/completion/notmuch-completion.bash 
>> b/completion/notmuch-completion.bash
>> index cfbd389..94ea2d5 100644
>> --- a/completion/notmuch-completion.bash
>> +++ b/completion/notmuch-completion.bash
>> @@ -294,7 +294,7 @@ _notmuch_search()
>>  return
>>  ;;
>>  --output)
>> -COMPREPLY=( $( compgen -W "summary threads messages files tags 
>> sender recipients" -- "${cur}" ) )
>> +COMPREPLY=( $( compgen -W "summary threads messages files tags" -- 
>> "${cur}" ) )
>>  return
>>  ;;
>>  --sort)
>> @@ -320,6 +320,44 @@ _notmuch_search()
>>  esac
>>  }
>>  
>> +_notmuch_address()
>> +{
>> +local cur prev words cword split
>> +_init_completion -s || return
>> +
>> +$split &&
>> +case "${prev}" in
>> +--format)
>> +COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
>> +return
>> +;;
>> +--output)
>> +COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
>> +return
>> +;;
>> +--sort)
>> +COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) 
>> )
>> +return
>> +;;
>> +--exclude)
>> +COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
>> +return
>> +;;
>> +esac
>> +
>> +! $split &&
>> +case "${cur}" in
>> +-*)
>> +local options="--format= --output= --sort= --exclude="
>> +compopt -o nospace
>> +COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
>> +;;
>> +*)
>> +_notmuch_search_terms
>> +;;
>> +esac
>> +}
>> +
>>  _notmuch_show()
>>  {
>>  local cur prev words cword split
>> @@ -393,7 +431,7 @@ _notmuch_tag()
>>  
>>  _notmuch()
>>  {
>> -local _notmuch_commands="compact config count dump help insert new 
>> reply restore search setup show tag"
>> +local _notmuch_commands="compact config count dump help insert new 
>> reply restore search address setup show tag"
>>  local arg cur prev words cword split
>>  
>>  # require bash-completion with _init_completion
>> 

[PATCH v2 06/10] cli: Introduce "notmuch address" command

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, David Bremner wrote:
> Michal Sojka  writes:
>
>> +{
>> +local cur prev words cword split
>> +_init_completion -s || return
>> +
>> +$split &&
>> +case "${prev}" in
>> +--format)
>> +COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
>> +return
>> +;;
>> +--output)
>> +COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
>> +return
>> +;;
>> +--sort)
>> +COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) 
>> )
>> +return
>> +;;
>> +--exclude)
>> +COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
>> +return
>> +;;
>> +esac
>> +
>> +! $split &&
>> +case "${cur}" in
>> +-*)
>> +local options="--format= --output= --sort= --exclude="
>> +compopt -o nospace
>> +COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
>> +;;
>> +*)
>> +_notmuch_search_terms
>> +;;
>> +esac
>> +}
>> +
>
> I am reminded that we have no tests for shell completion stuff, which
> seems pretty fragile.
>
>> +
>> +``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**)
>> +Presents the results in either JSON, S-Expressions, newline
>> +character separated plain-text (default), or null character
>> +separated plain-text (compatible with **xargs(1)** -0 option
>> +where available).
>> +
>> +``--format-version=N``
>> +Use the specified structured output format version. This is
>> +intended for programs that invoke **notmuch(1)** internally. If
>> +omitted, the latest supported version will be used.
>> +
>
>
> I wonder if at some point we should have a notmuch-output-formats.7 page.
>
>
>> +``--exclude=(true|false|all|flag)``
>> +A message is called "excluded" if it matches at least one tag in
>> +search.tag\_exclude that does not appear explicitly in the
>> +search terms. This option specifies whether to omit excluded
>> +messages in the search process.
>
> Similarly for excludes.  I'm ok with the duplication for now, and I can
> see an argument for not making the user chase references.

What about using RST include directive [1] to include shared parts into
more documents?

[1] 
http://docutils.sourceforge.net/docs/ref/rst/directives.html#including-an-external-document-fragment

-Michal


[PATCH v2 08/10] cli: address: Do not output duplicate addresses

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, David Bremner wrote:
> Michal Sojka  writes:
>
>>  
>> +/* Returns TRUE iff name and addr is duplicate. */
>
> If you're revising this patch, it would be good to mention the side
> effect of this function.
>
>> -process_address_list (const search_context_t *ctx, InternetAddressList 
>> *list)
>> +process_address_list (const search_context_t *ctx,
>> +  InternetAddressList *list)
>
> It probably doesn't make any difference, but this looks like a needless
> whitespace change.
>
> This function definitely needs some comment / pointer to
> documention. And probably not to have _my in the name.
>
>> +static void
>> +_my_talloc_free_for_g_hash (void *ptr)
>> +{
>> +talloc_free (ptr);
>> +}
>> +
>
> I don't understand the name of the next subtest
>
>> +test_begin_subtest "No --output"
>> +notmuch address --output=sender --output=recipients '*' >OUTPUT

This should be "notmuch address '*' >OUTPUT". I'll fix that.

>> +# Use EXPECTED from previous subtest
>> +test_expect_equal_file OUTPUT EXPECTED
>> +
>> +
>> +test_done
>
> nitpick, extra blank lines
>
> So, AIUI, this is all of the series proposed for 0.19. 

Agreed.

> It looks close to OK to me, modulo some minor style nits. One
> anonymous commentator on IRC mentioned the use of module scope
> variables, I guess in patch 6/10. I'm not sure of a better solution,
> but it's true in a perfect world we wouldn't have module local state.

A possible solution would be fill in common_options structure
programmatically, but this would make the code much less readable. I can
think of a few other solutions but none of them would fit into "perfect
world" :)

I'll send updated patches in the evening (CET timezone).

Thanks
-Michal


[PATCH v2 07/10] cli: search: Convert --output to keyword argument

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, Mark Walters wrote:
> Hi
>
> On Mon, 03 Nov 2014, Michal Sojka  wrote:
>> Now, when address related outputs are in a separate command, it makes
>> no sense to combine multiple --output options in search command line.
>> Using switch statement to handle different outputs is more readable
>> than a series of if statements.
>
> I am not keen on this change: I think the user should always be able to
> force the default output by setting command line options (which should
> protect against future changes in the default). 

You can do this even with this patch.

> Thus I would like to continue to allow --output=sender
> --output=recipients.

This is allowed in notmuch address. This patch modifies only notmuch
search. Currently it makes no sense to run --output=files --output=tags
or other output combinations.

> I do approve of making the default do something useful but whether it
> should be both or just sender (which is much faster) is unclear to me.

Does this comment refer to 7/10 or to 6/10?

I'd say it refers to the following line from 6/10.

search_context.output = OUTPUT_SENDER | OUTPUT_RECIPIENTS;

Even there you can override the default by command line option. This get
executed only if no --output appears on the command line.

Thanks
-Michal


>
> Best wishes
>
> Mark
>
>  
>> ---
>>  doc/man1/notmuch-search.rst |  3 ---
>>  notmuch-search.c| 25 +
>>  2 files changed, 13 insertions(+), 15 deletions(-)
>>
>> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
>> index 65df288..0cc2911 100644
>> --- a/doc/man1/notmuch-search.rst
>> +++ b/doc/man1/notmuch-search.rst
>> @@ -78,9 +78,6 @@ Supported options for **search** include
>>  by null characters (--format=text0), as a JSON array
>>  (--format=json), or as an S-Expression list (--format=sexp).
>>  
>> -This option can be given multiple times to combine different
>> -outputs.
>> -
>>  ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
>>  This option can be used to present results in either
>>  chronological order (**oldest-first**) or reverse chronological
>> diff --git a/notmuch-search.c b/notmuch-search.c
>> index cbd84f5..402e860 100644
>> --- a/notmuch-search.c
>> +++ b/notmuch-search.c
>> @@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int 
>> argc, char *argv[])
>>  int opt_index, ret;
>>  
>>  notmuch_opt_desc_t options[] = {
>> -{ NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o',
>> +{ NOTMUCH_OPT_KEYWORD, >output, "output", 'o',
>>(notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
>>{ "threads", OUTPUT_THREADS },
>>{ "messages", OUTPUT_MESSAGES },
>> @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int 
>> argc, char *argv[])
>>  { 0, 0, 0, 0, 0 }
>>  };
>>  
>> +ctx->output = OUTPUT_SUMMARY;
>>  opt_index = parse_arguments (argc, argv, options, 1);
>>  if (opt_index < 0)
>>  return EXIT_FAILURE;
>>  
>> -if (! ctx->output)
>> -ctx->output = OUTPUT_SUMMARY;
>> -
>>  if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
>>  ctx->dupe != -1) {
>>  fprintf (stderr, "Error: --duplicate=N is only supported with 
>> --output=files and --output=messages.\n");
>> @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int 
>> argc, char *argv[])
>>   argc - opt_index, argv + opt_index))
>>  return EXIT_FAILURE;
>>  
>> -if (ctx->output == OUTPUT_SUMMARY ||
>> -ctx->output == OUTPUT_THREADS)
>> +switch (ctx->output) {
>> +case OUTPUT_SUMMARY:
>> +case OUTPUT_THREADS:
>>  ret = do_search_threads (ctx);
>> -else if (ctx->output == OUTPUT_MESSAGES ||
>> - ctx->output == OUTPUT_FILES)
>> +break;
>> +case OUTPUT_MESSAGES:
>> +case OUTPUT_FILES:
>>  ret = do_search_messages (ctx);
>> -else if (ctx->output == OUTPUT_TAGS)
>> +break;
>> +case OUTPUT_TAGS:
>>  ret = do_search_tags (ctx);
>> -else {
>> -fprintf (stderr, "Error: the combination of outputs is not 
>> supported.\n");
>> -ret = 1;
>> +break;
>> +default:
>> +INTERNAL_ERROR ("Unexpected output");
>>  }
>>  
>>  _notmuch_search_cleanup (ctx);
>> -- 
>> 2.1.1
>>
>> ___
>> notmuch mailing list
>> notmuch at notmuchmail.org
>> http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 10/10] cli: address: Add --filter-by option to configure address filtering

2014-11-04 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  1 +
 doc/man1/notmuch-address.rst   | 36 ++-
 notmuch-search.c   | 48 +++--
 test/T097-address-filter-by.sh | 73 ++
 5 files changed, 160 insertions(+), 4 deletions(-)
 create mode 100755 test/T097-address-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index db152f3..2cb1586 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -310,7 +310,7 @@ _notmuch_search()
 ! $split &&
 case "${cur}" in
-*)
-   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate="
+   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by="
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
@@ -343,6 +343,10 @@ _notmuch_address()
COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W "nameaddr name addr addrfold 
nameaddrfold" -- "${cur}" ) )
+   return
+   ;;
 esac

 ! $split &&
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 8968562..3758f1a 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -62,6 +62,7 @@ _notmuch_address()
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
 '--output=[select what to output]:output:((sender recipients count))'
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both 
name and address part" name\:"name part" addr\:"address part" 
addrfold\:"case-insensitive address part" nameaddrfold\:"name and 
case-insensitive address part"))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 18473a7..524ab91 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,8 @@ DESCRIPTION
 ===

 Search for messages matching the given search terms, and display the
-addresses from them. Duplicate addresses are filtered out.
+addresses from them. Duplicate addresses are filtered out. Filtering
+can be configured with the --filter-by option.

 See **notmuch-search-terms(7)** for details of the supported syntax for
 .
@@ -85,6 +86,39 @@ Supported options for **address** include
 is the number of matching non-excluded messages in the thread,
 rather than the number of matching messages.

+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Controls how to filter out duplicate addresses. The filtering
+   algorithm receives a sequence of email addresses and outputs
+   the same sequence without the addresses that are considered a
+   duplicate of a previously output address. What is considered a
+   duplicate depends on how the two addresses are compared:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "John Doe " will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For example, the
+   addresses "John Doe " and "John Doe
+   " will be considered duplicate.
+
 EXIT STATUS
 ===

diff --git a/notmuch-search.c b/notmuch-search.c
index d99e530..04e33c6 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -43,6 +43,14 @@ typedef enum {
 NOTMUCH_FORMAT_SEXP
 } format_sel_t;

+typedef enum {
+FILTER_BY_NAMEADDR = 0,
+FILTER_BY_NAME,
+FILTER_BY_ADDR,
+FILTER_BY_ADDRFOLD,
+FILTER_BY_NAMEADDRFOLD,
+} filter_by_t;
+
 typedef struct {
 notmuch_database_t *notmuch;
 

[PATCH v2 09/10] cli: address: Add --output=count

2014-11-04 Thread Michal Sojka
This output prints how many times was each address encountered during
search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-address.rst   |  7 ++
 notmuch-search.c   | 49 --
 test/T095-address.sh   | 48 +
 5 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 94ea2d5..db152f3 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -332,7 +332,7 @@ _notmuch_address()
return
;;
--output)
-   COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+   COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index c606b75..8968562 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -61,7 +61,7 @@ _notmuch_address()
 {
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
-'--output=[select what to output]:output:((sender recipients))'
+'--output=[select what to output]:output:((sender recipients count))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 96512b7..18473a7 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -48,6 +48,13 @@ Supported options for **address** include
 Output all addresses from the *To*, *Cc* and *Bcc*
 headers.

+   **count**
+   Print the count of how many times was the address
+   encountered during search.
+
+   Note: With this option, addresses are printed only after
+   the whole search is finished. This may take long time.
+
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
diff --git a/notmuch-search.c b/notmuch-search.c
index 741702a..d99e530 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -33,6 +33,7 @@ typedef enum {
 /* Address command */
 OUTPUT_SENDER  = 1 << 5,
 OUTPUT_RECIPIENTS  = 1 << 6,
+OUTPUT_COUNT   = 1 << 7,
 } output_t;

 typedef enum {
@@ -59,6 +60,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;

 /* Return two stable query strings that identify exactly the matched
@@ -247,17 +249,24 @@ is_duplicate (const search_context_t *ctx, const char 
*name, const char *addr)
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;

 key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
 if (! key)
return FALSE;

-duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, 
(gpointer));

-if (! duplicate)
-   g_hash_table_insert (ctx->addresses, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (ctx->format, mailbox_t);
+   mailbox->name = talloc_strdup (mailbox, name);
+   mailbox->addr = talloc_strdup (mailbox, addr);
+   mailbox->count = 1;
+   g_hash_table_insert (ctx->addresses, key, mailbox);
+} else {
+   mailbox->count++;
talloc_free (key);
+}

 return duplicate;
 }
@@ -267,6 +276,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+int count = mailbox->count;
 sprinter_t *format = ctx->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
@@ -276,6 +286,10 @@ print_mailbox (const search_context_t *ctx, const 
mailbox_t *mailbox)
 name_addr = internet_address_to_string (ia, FALSE);

 if (format->is_text_printer) {
+   if (count > 0) {
+   format->integer (format, count);
+   format->string (format, "\t");
+   }
format->string (format, name_addr);
format->separator (format);
 } else {
@@ -286,6 +300,10 @@ print_mailbox (const search_context_t *ctx, const 
mailbox_t *mailbox)
format->string (format, addr);
format->map_key (format, "name-addr");
format->string (format, name_addr);
+   if (count > 0) {
+   format->map_key (format, "count");
+   format->integer (format, count);
+   }
format->end (format);
format->separator (format);
 }
@@ -294,7 +312,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)
 g_free (name_addr);
 }

-/* Print addresses from InternetAddressList.  */

[PATCH v2 08/10] cli: address: Do not output duplicate addresses

2014-11-04 Thread Michal Sojka
This filters out duplicate addresses from address command output.

It also also adds tests for the address command.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-address.rst |   2 +-
 notmuch-search.c |  40 -
 test/T095-address.sh | 100 +++
 3 files changed, 140 insertions(+), 2 deletions(-)
 create mode 100755 test/T095-address.sh

diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 8109f11..96512b7 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,7 @@ DESCRIPTION
 ===

 Search for messages matching the given search terms, and display the
-addresses from them.
+addresses from them. Duplicate addresses are filtered out.

 See **notmuch-search-terms(7)** for details of the supported syntax for
 .
diff --git a/notmuch-search.c b/notmuch-search.c
index 402e860..741702a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -53,6 +53,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
+GHashTable *addresses;
 } search_context_t;

 typedef struct {
@@ -240,6 +241,27 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }

+/* Returns TRUE iff name and addr is duplicate. */
+static notmuch_bool_t
+is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (ctx->addresses, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
@@ -274,7 +296,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_context_t *ctx, InternetAddressList *list)
+process_address_list (const search_context_t *ctx,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -298,6 +321,9 @@ process_address_list (const search_context_t *ctx, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

+   if (is_duplicate (ctx, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (ctx, );
}
 }
@@ -321,6 +347,12 @@ process_address_header (const search_context_t *ctx, const 
char *value)
 g_object_unref (list);
 }

+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
+}
+
 static int
 _count_filenames (notmuch_message_t *message)
 {
@@ -669,8 +701,14 @@ notmuch_address_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

+ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+   _my_talloc_free_for_g_hash, NULL);
+
 ret = do_search_messages (ctx);

+g_hash_table_unref (ctx->addresses);
+
+
 _notmuch_search_cleanup (ctx);

 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/test/T095-address.sh b/test/T095-address.sh
new file mode 100755
index 000..8a256d2
--- /dev/null
+++ b/test/T095-address.sh
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+test_description='"notmuch address" in several variants'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "--output=sender"
+notmuch address --output=sender '*' >OUTPUT
+cat OUTPUT
+cat 

[PATCH v2 07/10] cli: search: Convert --output to keyword argument

2014-11-04 Thread Michal Sojka
Now, when address related outputs are in a separate command, it makes
no sense to combine multiple --output options in search command line.
Using switch statement to handle different outputs is more readable
than a series of if statements.
---
 doc/man1/notmuch-search.rst |  3 ---
 notmuch-search.c| 25 +
 2 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 65df288..0cc2911 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -78,9 +78,6 @@ Supported options for **search** include
 by null characters (--format=text0), as a JSON array
 (--format=json), or as an S-Expression list (--format=sexp).

-   This option can be given multiple times to combine different
-   outputs.
-
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
diff --git a/notmuch-search.c b/notmuch-search.c
index cbd84f5..402e860 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 int opt_index, ret;

 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o',
+   { NOTMUCH_OPT_KEYWORD, >output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
{ 0, 0, 0, 0, 0 }
 };

+ctx->output = OUTPUT_SUMMARY;
 opt_index = parse_arguments (argc, argv, options, 1);
 if (opt_index < 0)
return EXIT_FAILURE;

-if (! ctx->output)
-   ctx->output = OUTPUT_SUMMARY;
-
 if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
ctx->dupe != -1) {
 fprintf (stderr, "Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n");
@@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

-if (ctx->output == OUTPUT_SUMMARY ||
-   ctx->output == OUTPUT_THREADS)
+switch (ctx->output) {
+case OUTPUT_SUMMARY:
+case OUTPUT_THREADS:
ret = do_search_threads (ctx);
-else if (ctx->output == OUTPUT_MESSAGES ||
-ctx->output == OUTPUT_FILES)
+   break;
+case OUTPUT_MESSAGES:
+case OUTPUT_FILES:
ret = do_search_messages (ctx);
-else if (ctx->output == OUTPUT_TAGS)
+   break;
+case OUTPUT_TAGS:
ret = do_search_tags (ctx);
-else {
-   fprintf (stderr, "Error: the combination of outputs is not 
supported.\n");
-   ret = 1;
+   break;
+default:
+   INTERNAL_ERROR ("Unexpected output");
 }

 _notmuch_search_cleanup (ctx);
-- 
2.1.1



[PATCH v2 06/10] cli: Introduce "notmuch address" command

2014-11-04 Thread Michal Sojka
This moves address-related functionality from search command to the
new address command. The implementation shares almost all code and
some command line options.

Options --offset and --limit were intentionally not included in the
address command, because they refer to messages numbers, which users
do not see in the output. This could confuse users because, for
example, they could see more addresses in the output that what was
specified with --limit. This functionality can be correctly
reimplemented for addresses later.

This was inspired by a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |  42 ++-
 completion/notmuch-completion.zsh  |  10 +++-
 doc/man1/notmuch-address.rst   |  99 
 doc/man1/notmuch-search.rst|  20 +---
 doc/man1/notmuch.rst   |   7 +--
 notmuch-client.h   |   3 ++
 notmuch-search.c   | 101 +
 notmuch.c  |   2 +
 8 files changed, 228 insertions(+), 56 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..94ea2d5 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -294,7 +294,7 @@ _notmuch_search()
return
;;
--output)
-   COMPREPLY=( $( compgen -W "summary threads messages files tags 
sender recipients" -- "${cur}" ) )
+   COMPREPLY=( $( compgen -W "summary threads messages files tags" -- 
"${cur}" ) )
return
;;
--sort)
@@ -320,6 +320,44 @@ _notmuch_search()
 esac
 }

+_notmuch_address()
+{
+local cur prev words cword split
+_init_completion -s || return
+
+$split &&
+case "${prev}" in
+   --format)
+   COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
+   return
+   ;;
+   --output)
+   COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+   return
+   ;;
+   --sort)
+   COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) 
)
+   return
+   ;;
+   --exclude)
+   COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
+   return
+   ;;
+esac
+
+! $split &&
+case "${cur}" in
+   -*)
+   local options="--format= --output= --sort= --exclude="
+   compopt -o nospace
+   COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
+   ;;
+   *)
+   _notmuch_search_terms
+   ;;
+esac
+}
+
 _notmuch_show()
 {
 local cur prev words cword split
@@ -393,7 +431,7 @@ _notmuch_tag()

 _notmuch()
 {
-local _notmuch_commands="compact config count dump help insert new reply 
restore search setup show tag"
+local _notmuch_commands="compact config count dump help insert new reply 
restore search address setup show tag"
 local arg cur prev words cword split

 # require bash-completion with _init_completion
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..c606b75 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -10,6 +10,7 @@ _notmuch_commands()
 'setup:interactively set up notmuch for first use'
 'new:find and import any new message to the database'
 'search:search for messages matching the search terms, display matching 
threads as results'
+'address:get addresses from messages matching the given search terms'
 'reply:constructs a reply template for a set of messages'
 'show:show all messages matching the search terms'
 'tag:add or remove tags for all messages matching the search terms'
@@ -53,7 +54,14 @@ _notmuch_search()
 '--max-threads=[display only the first x threads from the search 
results]:number of threads to show: ' \
 '--first=[omit the first x threads from the search results]:number of 
threads to omit: ' \
 '--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
-'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients))'
+'--output=[select what to output]:output:((summary threads messages files 
tags))'
+}
+
+_notmuch_address()
+{
+  _arguments -s : \
+'--sort=[sort results]:sorting:((newest-first\:"reverse chronological 
order" oldest-first\:"chronological order"))' \
+'--output=[select what to output]:output:((sender recipients))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
new file mode 100644
index 000..8109f11
--- /dev/null
+++ b/doc/man1/notmuch-address.rst
@@ -0,0 +1,99 @@
+===
+notmuch-address
+===
+
+SYNOPSIS
+
+
+**notmuch** **address** [*option* ...] <*search-term*> ...
+
+DESCRIPTION

[PATCH v2 05/10] cli: add support for hierarchical command line option arrays

2014-11-04 Thread Michal Sojka
From: Jani Nikula 

NOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in
output_var.

The "Unrecognized option" message was moved out of parse_option() to
not be emitted twice or when parsing a non-inherited option.
---
 command-line-arguments.c | 16 +---
 command-line-arguments.h |  1 +
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index c6f7269..de6b453 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int 
pos_arg_index,
  */

 notmuch_bool_t
-parse_option (const char *arg,
- const notmuch_opt_desc_t *options) {
-
-assert(arg);
+parse_option (const char *_arg, const notmuch_opt_desc_t *options)
+{
+assert(_arg);
 assert(options);

-arg += 2;
-
+const char *arg = _arg + 2; /* _arg starts with -- */
 const notmuch_opt_desc_t *try;
 for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) {
+   if (try->opt_type == NOTMUCH_OPT_INHERIT &&
+   parse_option (_arg, try->output_var))
+   return TRUE;
+
if (! try->name)
continue;

@@ -170,7 +172,6 @@ parse_option (const char *arg,
/*UNREACHED*/
}
 }
-fprintf (stderr, "Unrecognized option: --%s\n", arg);
 return FALSE;
 }

@@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv,
if (more_args) {
opt_index++;
} else {
+   fprintf (stderr, "Unrecognized option: %s\n", argv[opt_index]);
opt_index = -1;
}

diff --git a/command-line-arguments.h b/command-line-arguments.h
index 6444129..309aaf2 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -5,6 +5,7 @@

 enum notmuch_opt_type {
 NOTMUCH_OPT_END = 0,
+NOTMUCH_OPT_INHERIT,   /* another options table */
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
-- 
2.1.1



[PATCH v2 04/10] cli: search: Split notmuch_search_command to smaller functions

2014-11-04 Thread Michal Sojka
In the next commit, these functions will be used to share some
functionality between search and address commands.
---
 notmuch-search.c | 155 ++-
 1 file changed, 86 insertions(+), 69 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6765a16..f115359 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx)
 return 0;
 }

+static int
+_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int 
argc, char *argv[])
+{
+char *query_str;
+unsigned int i;
+
+switch (ctx->format_sel) {
+case NOTMUCH_FORMAT_TEXT:
+   ctx->format = sprinter_text_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_TEXT0:
+   if (ctx->output == OUTPUT_SUMMARY) {
+   fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
+   return EXIT_FAILURE;
+   }
+   ctx->format = sprinter_text0_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_JSON:
+   ctx->format = sprinter_json_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_SEXP:
+   ctx->format = sprinter_sexp_create (config, stdout);
+   break;
+default:
+   /* this should never happen */
+   INTERNAL_ERROR("no output format selected");
+}
+
+notmuch_exit_if_unsupported_format ();
+
+if (notmuch_database_open (notmuch_config_get_database_path (config),
+  NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch))
+   return EXIT_FAILURE;
+
+query_str = query_string_from_args (ctx->notmuch, argc, argv);
+if (query_str == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return EXIT_FAILURE;
+}
+if (*query_str == '\0') {
+   fprintf (stderr, "Error: notmuch search requires at least one search 
term.\n");
+   return EXIT_FAILURE;
+}
+
+ctx->query = notmuch_query_create (ctx->notmuch, query_str);
+if (ctx->query == NULL) {
+   fprintf (stderr, "Out of memory\n");
+   return EXIT_FAILURE;
+}
+
+notmuch_query_set_sort (ctx->query, ctx->sort);
+
+if (ctx->exclude == NOTMUCH_EXCLUDE_FLAG && ctx->output != OUTPUT_SUMMARY) 
{
+   /* If we are not doing summary output there is nowhere to
+* print the excluded flag so fall back on including the
+* excluded messages. */
+   fprintf (stderr, "Warning: this output format cannot flag excluded 
messages.\n");
+   ctx->exclude = NOTMUCH_EXCLUDE_FALSE;
+}
+
+if (ctx->exclude != NOTMUCH_EXCLUDE_FALSE) {
+   const char **search_exclude_tags;
+   size_t search_exclude_tags_length;
+
+   search_exclude_tags = notmuch_config_get_search_exclude_tags
+   (config, _exclude_tags_length);
+   for (i = 0; i < search_exclude_tags_length; i++)
+   notmuch_query_add_tag_exclude (ctx->query, search_exclude_tags[i]);
+   notmuch_query_set_omit_excluded (ctx->query, ctx->exclude);
+}
+
+return 0;
+}
+
+static void
+_notmuch_search_cleanup (search_context_t *ctx)
+{
+notmuch_query_destroy (ctx->query);
+notmuch_database_destroy (ctx->notmuch);
+
+talloc_free (ctx->format);
+}
+
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
@@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
.dupe = -1,
 };
 search_context_t *ctx = _context;
-char *query_str;
 int opt_index, ret;
-unsigned int i;

 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, >sort, "sort", 's',
@@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 return EXIT_FAILURE;
 }

-switch (ctx->format_sel) {
-case NOTMUCH_FORMAT_TEXT:
-   ctx->format = sprinter_text_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_TEXT0:
-   if (ctx->output == OUTPUT_SUMMARY) {
-   fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
-   return EXIT_FAILURE;
-   }
-   ctx->format = sprinter_text0_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_JSON:
-   ctx->format = sprinter_json_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_SEXP:
-   ctx->format = sprinter_sexp_create (config, stdout);
-   break;
-default:
-   /* this should never happen */
-   INTERNAL_ERROR("no output format selected");
-}
-
-notmuch_exit_if_unsupported_format ();
-
-if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, >notmuch))
+if (_notmuch_search_prepare (ctx, config,
+argc - opt_index, argv + opt_index))
return EXIT_FAILURE;

-query_str = query_string_from_args (ctx->notmuch, argc-opt_index, 
argv+opt_index);
-if 

[PATCH v2 03/10] cli: search: Convert ctx. to ctx->

2014-11-04 Thread Michal Sojka
Mostly text replacement.
---
 notmuch-search.c | 81 
 1 file changed, 41 insertions(+), 40 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3d2012b..6765a16 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx)
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-search_context_t ctx = {
+search_context_t search_context = {
.format_sel = NOTMUCH_FORMAT_TEXT,
.exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
@@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
.limit = -1, /* unlimited */
.dupe = -1,
 };
+search_context_t *ctx = _context;
 char *query_str;
 int opt_index, ret;
 unsigned int i;

 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD, , "sort", 's',
+   { NOTMUCH_OPT_KEYWORD, >sort, "sort", 's',
  (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
  { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
+   { NOTMUCH_OPT_KEYWORD, >format_sel, "format", 'f',
  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
  { "sexp", NOTMUCH_FORMAT_SEXP },
  { "text", NOTMUCH_FORMAT_TEXT },
  { "text0", NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, _format_version, "format-version", 0, 0 },
-   { NOTMUCH_OPT_KEYWORD_FLAGS, , "output", 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, >output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
  { "files", OUTPUT_FILES },
  { "tags", OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
+{ NOTMUCH_OPT_KEYWORD, >exclude, "exclude", 'x',
   (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
   { "false", NOTMUCH_EXCLUDE_FALSE },
   { "flag", NOTMUCH_EXCLUDE_FLAG },
   { "all", NOTMUCH_EXCLUDE_ALL },
   { 0, 0 } } },
-   { NOTMUCH_OPT_INT, , "offset", 'O', 0 },
-   { NOTMUCH_OPT_INT, , "limit", 'L', 0  },
-   { NOTMUCH_OPT_INT, , "duplicate", 'D', 0  },
+   { NOTMUCH_OPT_INT, >offset, "offset", 'O', 0 },
+   { NOTMUCH_OPT_INT, >limit, "limit", 'L', 0  },
+   { NOTMUCH_OPT_INT, >dupe, "duplicate", 'D', 0  },
{ 0, 0, 0, 0, 0 }
 };

@@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 if (opt_index < 0)
return EXIT_FAILURE;

-if (! ctx.output)
-   ctx.output = OUTPUT_SUMMARY;
+if (! ctx->output)
+   ctx->output = OUTPUT_SUMMARY;

-if (ctx.output != OUTPUT_FILES && ctx.output != OUTPUT_MESSAGES &&
-   ctx.dupe != -1) {
+if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
+   ctx->dupe != -1) {
 fprintf (stderr, "Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n");
 return EXIT_FAILURE;
 }

-switch (ctx.format_sel) {
+switch (ctx->format_sel) {
 case NOTMUCH_FORMAT_TEXT:
-   ctx.format = sprinter_text_create (config, stdout);
+   ctx->format = sprinter_text_create (config, stdout);
break;
 case NOTMUCH_FORMAT_TEXT0:
-   if (ctx.output == OUTPUT_SUMMARY) {
+   if (ctx->output == OUTPUT_SUMMARY) {
fprintf (stderr, "Error: --format=text0 is not compatible with 
--output=summary.\n");
return EXIT_FAILURE;
}
-   ctx.format = sprinter_text0_create (config, stdout);
+   ctx->format = sprinter_text0_create (config, stdout);
break;
 case NOTMUCH_FORMAT_JSON:
-   ctx.format = sprinter_json_create (config, stdout);
+   ctx->format = sprinter_json_create (config, stdout);
break;
 case NOTMUCH_FORMAT_SEXP:
-   ctx.format = sprinter_sexp_create (config, stdout);
+   ctx->format = sprinter_sexp_create (config, stdout);
break;
 default:
/* this should never happen */
@@ -558,10 +559,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 notmuch_exit_if_unsupported_format ();

 if (notmuch_database_open (notmuch_config_get_database_path (config),
-  

[PATCH v2 02/10] cli: search: Move more variables into search_context_t

2014-11-04 Thread Michal Sojka
Just refactoring, no functional changes.
---
 notmuch-search.c | 49 ++---
 1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 2c47b80..3d2012b 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -34,8 +34,18 @@ typedef enum {

 #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)

+typedef enum {
+NOTMUCH_FORMAT_JSON,
+NOTMUCH_FORMAT_TEXT,
+NOTMUCH_FORMAT_TEXT0,
+NOTMUCH_FORMAT_SEXP
+} format_sel_t;
+
 typedef struct {
+notmuch_database_t *notmuch;
+format_sel_t format_sel;
 sprinter_t *format;
+notmuch_exclude_t exclude;
 notmuch_query_t *query;
 notmuch_sort_t sort;
 output_t output;
@@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx)
 }

 static int
-do_search_tags (notmuch_database_t *notmuch,
-   const search_context_t *ctx)
+do_search_tags (const search_context_t *ctx)
 {
 notmuch_messages_t *messages = NULL;
 notmuch_tags_t *tags;
 const char *tag;
 sprinter_t *format = ctx->format;
 notmuch_query_t *query = ctx->query;
+notmuch_database_t *notmuch = ctx->notmuch;

 /* should the following only special case if no excluded terms
  * specified? */
@@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch,
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-notmuch_database_t *notmuch;
 search_context_t ctx = {
+   .format_sel = NOTMUCH_FORMAT_TEXT,
+   .exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
.output = 0,
.offset = 0,
@@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 };
 char *query_str;
 int opt_index, ret;
-notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE;
 unsigned int i;

-enum {
-   NOTMUCH_FORMAT_JSON,
-   NOTMUCH_FORMAT_TEXT,
-   NOTMUCH_FORMAT_TEXT0,
-   NOTMUCH_FORMAT_SEXP
-} format_sel = NOTMUCH_FORMAT_TEXT;
-
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, , "sort", 's',
  (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
  { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
+   { NOTMUCH_OPT_KEYWORD, _sel, "format", 'f',
  (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
  { "sexp", NOTMUCH_FORMAT_SEXP },
  { "text", NOTMUCH_FORMAT_TEXT },
@@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "files", OUTPUT_FILES },
  { "tags", OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
+{ NOTMUCH_OPT_KEYWORD, , "exclude", 'x',
   (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
   { "false", NOTMUCH_EXCLUDE_FALSE },
   { "flag", NOTMUCH_EXCLUDE_FLAG },
@@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 return EXIT_FAILURE;
 }

-switch (format_sel) {
+switch (ctx.format_sel) {
 case NOTMUCH_FORMAT_TEXT:
ctx.format = sprinter_text_create (config, stdout);
break;
@@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 notmuch_exit_if_unsupported_format ();

 if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, ))
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ))
return EXIT_FAILURE;

-query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
+query_str = query_string_from_args (ctx.notmuch, argc-opt_index, 
argv+opt_index);
 if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return EXIT_FAILURE;
@@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
return EXIT_FAILURE;
 }

-ctx.query = notmuch_query_create (notmuch, query_str);
+ctx.query = notmuch_query_create (ctx.notmuch, query_str);
 if (ctx.query == NULL) {
fprintf (stderr, "Out of memory\n");
return EXIT_FAILURE;
@@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])

 notmuch_query_set_sort (ctx.query, ctx.sort);

-if (exclude == NOTMUCH_EXCLUDE_FLAG && ctx.output != OUTPUT_SUMMARY) {
+if (ctx.exclude == NOTMUCH_EXCLUDE_FLAG && ctx.output != OUTPUT_SUMMARY) {
/* If we are not doing summary output there is nowhere to
 * print the excluded flag so fall back on including the
 

[PATCH v2 01/10] cli: search: Rename options to context

2014-11-04 Thread Michal Sojka
Just text replacement, no other changes.
---
 notmuch-search.c | 142 +++
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6345fb6..2c47b80 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -42,7 +42,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
-} search_options_t;
+} search_context_t;

 typedef struct {
 const char *name;
@@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread,
 }

 static int
-do_search_threads (search_options_t *opt)
+do_search_threads (search_context_t *ctx)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 time_t date;
 int i;

-if (opt->offset < 0) {
-   opt->offset += notmuch_query_count_threads (opt->query);
-   if (opt->offset < 0)
-   opt->offset = 0;
+if (ctx->offset < 0) {
+   ctx->offset += notmuch_query_count_threads (ctx->query);
+   if (ctx->offset < 0)
+   ctx->offset = 0;
 }

-threads = notmuch_query_search_threads (opt->query);
+threads = notmuch_query_search_threads (ctx->query);
 if (threads == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset 
+ opt->limit);
+notmuch_threads_valid (threads) && (ctx->limit < 0 || i < ctx->offset 
+ ctx->limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);

-   if (i < opt->offset) {
+   if (i < ctx->offset) {
notmuch_thread_destroy (thread);
continue;
}

-   if (opt->output == OUTPUT_THREADS) {
+   if (ctx->output == OUTPUT_THREADS) {
format->set_prefix (format, "thread");
format->string (format,
notmuch_thread_get_thread_id (thread));
@@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt)

format->begin_map (format);

-   if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (ctx->sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt)
 }

 static void
-print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
+print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;

@@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_context_t *ctx, InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;

-   process_address_list (opt, group_list);
+   process_address_list (ctx, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

-   print_mailbox (opt, );
+   print_mailbox (ctx, );
}
 }
 }

 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_context_t *ctx, const char *value)
 {
 InternetAddressList *list;

@@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const 
char *value)
 if (list == NULL)
return;

-process_address_list (opt, list);
+process_address_list (ctx, list);

 g_object_unref (list);
 }
@@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message)
 }

 static int
-do_search_messages (search_options_t *opt)
+do_search_messages (search_context_t *ctx)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
-sprinter_t *format = opt->format;
+sprinter_t *format = ctx->format;
 int i;

-if (opt->offset < 0) {
-   opt->offset += notmuch_query_count_messages (opt->query);
-   if (opt->offset < 0)
-   opt->offset = 0;
+if (ctx->offset < 0) {
+   ctx->offset += 

[PATCH v2 00/10] "notmuch address" command

2014-11-04 Thread Michal Sojka
Hi all,

this is v2 of "notmuch address" patchset. It obsoletes [1].

Don't be scared by the number of patches. Most of them are trivial
refactoring. Patches 1-4 refactor the code so that "notmuch search"
command is easier to split. Patch 5 is Jani's hierarchical command
line parsing patch. Patch 6 splits search functionality to new address
command. Patch 7 is minor refactoring. Patches 8-10 correspond to
patches 5-7 in the original "notmuch search
--output=sender/recipients" patch series [2].

Changes from v1:

- Rebased to current master (conflicted with Jani's "notmuch search
  --duplicate=N with --output=messages" patch)
- Fixed printing of false "Unrecognized option" error message in
  hierarchical command line parser.

Regards,
-Michal

[1] id:1414889400-30977-1-git-send-email-sojkam1 at fel.cvut.cz
[2] id:1414792441-29555-1-git-send-email-sojkam1 at fel.cvut.cz


Jani Nikula (1):
  cli: add support for hierarchical command line option arrays

Michal Sojka (9):
  cli: search: Rename options to context
  cli: search: Move more variables into search_context_t
  cli: search: Convert ctx. to ctx->
  cli: search: Split notmuch_search_command to smaller functions
  cli: Introduce "notmuch address" command
  cli: search: Convert --output to keyword argument
  cli: address: Do not output duplicate addresses
  cli: address: Add --output=count
  cli: address: Add --filter-by option to configure address filtering

 command-line-arguments.c   |  16 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |  48 +++-
 completion/notmuch-completion.zsh  |  11 +-
 doc/man1/notmuch-address.rst   | 140 
 doc/man1/notmuch-search.rst|  21 +-
 doc/man1/notmuch.rst   |   7 +-
 notmuch-client.h   |   3 +
 notmuch-search.c   | 454 +
 notmuch.c  |   2 +
 test/T095-address.sh   | 148 
 test/T097-address-filter-by.sh |  73 ++
 12 files changed, 751 insertions(+), 173 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst
 create mode 100755 test/T095-address.sh
 create mode 100755 test/T097-address-filter-by.sh

-- 
2.1.1



Re: [PATCH v2 07/10] cli: search: Convert --output to keyword argument

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, Mark Walters wrote:
 Hi

 On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 Now, when address related outputs are in a separate command, it makes
 no sense to combine multiple --output options in search command line.
 Using switch statement to handle different outputs is more readable
 than a series of if statements.

 I am not keen on this change: I think the user should always be able to
 force the default output by setting command line options (which should
 protect against future changes in the default). 

You can do this even with this patch.

 Thus I would like to continue to allow --output=sender
 --output=recipients.

This is allowed in notmuch address. This patch modifies only notmuch
search. Currently it makes no sense to run --output=files --output=tags
or other output combinations.

 I do approve of making the default do something useful but whether it
 should be both or just sender (which is much faster) is unclear to me.

Does this comment refer to 7/10 or to 6/10?

I'd say it refers to the following line from 6/10.

search_context.output = OUTPUT_SENDER | OUTPUT_RECIPIENTS;

Even there you can override the default by command line option. This get
executed only if no --output appears on the command line.

Thanks
-Michal



 Best wishes

 Mark

  
 ---
  doc/man1/notmuch-search.rst |  3 ---
  notmuch-search.c| 25 +
  2 files changed, 13 insertions(+), 15 deletions(-)

 diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
 index 65df288..0cc2911 100644
 --- a/doc/man1/notmuch-search.rst
 +++ b/doc/man1/notmuch-search.rst
 @@ -78,9 +78,6 @@ Supported options for **search** include
  by null characters (--format=text0), as a JSON array
  (--format=json), or as an S-Expression list (--format=sexp).
  
 -This option can be given multiple times to combine different
 -outputs.
 -
  ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
  This option can be used to present results in either
  chronological order (**oldest-first**) or reverse chronological
 diff --git a/notmuch-search.c b/notmuch-search.c
 index cbd84f5..402e860 100644
 --- a/notmuch-search.c
 +++ b/notmuch-search.c
 @@ -593,7 +593,7 @@ notmuch_search_command (notmuch_config_t *config, int 
 argc, char *argv[])
  int opt_index, ret;
  
  notmuch_opt_desc_t options[] = {
 -{ NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o',
 +{ NOTMUCH_OPT_KEYWORD, ctx-output, output, 'o',
(notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY },
{ threads, OUTPUT_THREADS },
{ messages, OUTPUT_MESSAGES },
 @@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int 
 argc, char *argv[])
  { 0, 0, 0, 0, 0 }
  };
  
 +ctx-output = OUTPUT_SUMMARY;
  opt_index = parse_arguments (argc, argv, options, 1);
  if (opt_index  0)
  return EXIT_FAILURE;
  
 -if (! ctx-output)
 -ctx-output = OUTPUT_SUMMARY;
 -
  if (ctx-output != OUTPUT_FILES  ctx-output != OUTPUT_MESSAGES 
  ctx-dupe != -1) {
  fprintf (stderr, Error: --duplicate=N is only supported with 
 --output=files and --output=messages.\n);
 @@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int 
 argc, char *argv[])
   argc - opt_index, argv + opt_index))
  return EXIT_FAILURE;
  
 -if (ctx-output == OUTPUT_SUMMARY ||
 -ctx-output == OUTPUT_THREADS)
 +switch (ctx-output) {
 +case OUTPUT_SUMMARY:
 +case OUTPUT_THREADS:
  ret = do_search_threads (ctx);
 -else if (ctx-output == OUTPUT_MESSAGES ||
 - ctx-output == OUTPUT_FILES)
 +break;
 +case OUTPUT_MESSAGES:
 +case OUTPUT_FILES:
  ret = do_search_messages (ctx);
 -else if (ctx-output == OUTPUT_TAGS)
 +break;
 +case OUTPUT_TAGS:
  ret = do_search_tags (ctx);
 -else {
 -fprintf (stderr, Error: the combination of outputs is not 
 supported.\n);
 -ret = 1;
 +break;
 +default:
 +INTERNAL_ERROR (Unexpected output);
  }
  
  _notmuch_search_cleanup (ctx);
 -- 
 2.1.1

 ___
 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 v2 08/10] cli: address: Do not output duplicate addresses

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, David Bremner wrote:
 Michal Sojka sojk...@fel.cvut.cz writes:

  
 +/* Returns TRUE iff name and addr is duplicate. */

 If you're revising this patch, it would be good to mention the side
 effect of this function.

 -process_address_list (const search_context_t *ctx, InternetAddressList 
 *list)
 +process_address_list (const search_context_t *ctx,
 +  InternetAddressList *list)

 It probably doesn't make any difference, but this looks like a needless
 whitespace change.

 This function definitely needs some comment / pointer to
 documention. And probably not to have _my in the name.

 +static void
 +_my_talloc_free_for_g_hash (void *ptr)
 +{
 +talloc_free (ptr);
 +}
 +

 I don't understand the name of the next subtest

 +test_begin_subtest No --output
 +notmuch address --output=sender --output=recipients '*' OUTPUT

This should be notmuch address '*' OUTPUT. I'll fix that.

 +# Use EXPECTED from previous subtest
 +test_expect_equal_file OUTPUT EXPECTED
 +
 +
 +test_done

 nitpick, extra blank lines

 So, AIUI, this is all of the series proposed for 0.19. 

Agreed.

 It looks close to OK to me, modulo some minor style nits. One
 anonymous commentator on IRC mentioned the use of module scope
 variables, I guess in patch 6/10. I'm not sure of a better solution,
 but it's true in a perfect world we wouldn't have module local state.

A possible solution would be fill in common_options structure
programmatically, but this would make the code much less readable. I can
think of a few other solutions but none of them would fit into perfect
world :)

I'll send updated patches in the evening (CET timezone).

Thanks
-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2 06/10] cli: Introduce notmuch address command

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, David Bremner wrote:
 Michal Sojka sojk...@fel.cvut.cz writes:

 +{
 +local cur prev words cword split
 +_init_completion -s || return
 +
 +$split 
 +case ${prev} in
 +--format)
 +COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) )
 +return
 +;;
 +--output)
 +COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) )
 +return
 +;;
 +--sort)
 +COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) 
 )
 +return
 +;;
 +--exclude)
 +COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) )
 +return
 +;;
 +esac
 +
 +! $split 
 +case ${cur} in
 +-*)
 +local options=--format= --output= --sort= --exclude=
 +compopt -o nospace
 +COMPREPLY=( $(compgen -W $options -- ${cur}) )
 +;;
 +*)
 +_notmuch_search_terms
 +;;
 +esac
 +}
 +

 I am reminded that we have no tests for shell completion stuff, which
 seems pretty fragile.

 +
 +``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**)
 +Presents the results in either JSON, S-Expressions, newline
 +character separated plain-text (default), or null character
 +separated plain-text (compatible with **xargs(1)** -0 option
 +where available).
 +
 +``--format-version=N``
 +Use the specified structured output format version. This is
 +intended for programs that invoke **notmuch(1)** internally. If
 +omitted, the latest supported version will be used.
 +


 I wonder if at some point we should have a notmuch-output-formats.7 page.


 +``--exclude=(true|false|all|flag)``
 +A message is called excluded if it matches at least one tag in
 +search.tag\_exclude that does not appear explicitly in the
 +search terms. This option specifies whether to omit excluded
 +messages in the search process.

 Similarly for excludes.  I'm ok with the duplication for now, and I can
 see an argument for not making the user chase references.

What about using RST include directive [1] to include shared parts into
more documents?

[1] 
http://docutils.sourceforge.net/docs/ref/rst/directives.html#including-an-external-document-fragment

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2 06/10] cli: Introduce notmuch address command

2014-11-04 Thread Michal Sojka
On Tue, Nov 04 2014, Mark Walters wrote:
 On Mon, 03 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 This moves address-related functionality from search command to the
 new address command. The implementation shares almost all code and
 some command line options.

 Options --offset and --limit were intentionally not included in the
 address command, because they refer to messages numbers, which users
 do not see in the output. This could confuse users because, for
 example, they could see more addresses in the output that what was
 specified with --limit. This functionality can be correctly
 reimplemented for addresses later.

 I am not sure about this: we already have this anomaly for output=files
 say. Also I can imagine calling notmuch address --limit=1000 ... to get
 a bunch of recent addresses quickly and I really am wanting to look at
 1000 messages, not collect 1000 addresses.

I think that one of the reasons for having the new address command is
to have cleaner user interface. And including anomalies doesn't sound
like a way to achieve this. I think that now you can use date: query
to limit the search.

I volunteer to implement address --limit properly after 0.19. This
should be easy.

-Michal

 Additionally, the 1000 message approach makes sense when we start
 deduping whereas 1000 authors becomes unclear.


 This was inspired by a patch from Jani Nikula.
 ---
  completion/notmuch-completion.bash |  42 ++-
  completion/notmuch-completion.zsh  |  10 +++-
  doc/man1/notmuch-address.rst   |  99 
 
  doc/man1/notmuch-search.rst|  20 +---
  doc/man1/notmuch.rst   |   7 +--
  notmuch-client.h   |   3 ++
  notmuch-search.c   | 101 
 +
  notmuch.c  |   2 +
  8 files changed, 228 insertions(+), 56 deletions(-)
  create mode 100644 doc/man1/notmuch-address.rst

 diff --git a/completion/notmuch-completion.bash 
 b/completion/notmuch-completion.bash
 index cfbd389..94ea2d5 100644
 --- a/completion/notmuch-completion.bash
 +++ b/completion/notmuch-completion.bash
 @@ -294,7 +294,7 @@ _notmuch_search()
  return
  ;;
  --output)
 -COMPREPLY=( $( compgen -W summary threads messages files tags 
 sender recipients -- ${cur} ) )
 +COMPREPLY=( $( compgen -W summary threads messages files tags -- 
 ${cur} ) )
  return
  ;;
  --sort)
 @@ -320,6 +320,44 @@ _notmuch_search()
  esac
  }
  
 +_notmuch_address()
 +{
 +local cur prev words cword split
 +_init_completion -s || return
 +
 +$split 
 +case ${prev} in
 +--format)
 +COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) )
 +return
 +;;
 +--output)
 +COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) )
 +return
 +;;
 +--sort)
 +COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) 
 )
 +return
 +;;
 +--exclude)
 +COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) )
 +return
 +;;
 +esac
 +
 +! $split 
 +case ${cur} in
 +-*)
 +local options=--format= --output= --sort= --exclude=
 +compopt -o nospace
 +COMPREPLY=( $(compgen -W $options -- ${cur}) )
 +;;
 +*)
 +_notmuch_search_terms
 +;;
 +esac
 +}
 +
  _notmuch_show()
  {
  local cur prev words cword split
 @@ -393,7 +431,7 @@ _notmuch_tag()
  
  _notmuch()
  {
 -local _notmuch_commands=compact config count dump help insert new 
 reply restore search setup show tag
 +local _notmuch_commands=compact config count dump help insert new 
 reply restore search address setup show tag
  local arg cur prev words cword split
  
  # require bash-completion with _init_completion
 diff --git a/completion/notmuch-completion.zsh 
 b/completion/notmuch-completion.zsh
 index 3e52a00..c606b75 100644
 --- a/completion/notmuch-completion.zsh
 +++ b/completion/notmuch-completion.zsh
 @@ -10,6 +10,7 @@ _notmuch_commands()
  'setup:interactively set up notmuch for first use'
  'new:find and import any new message to the database'
  'search:search for messages matching the search terms, display matching 
 threads as results'
 +'address:get addresses from messages matching the given search terms'
  'reply:constructs a reply template for a set of messages'
  'show:show all messages matching the search terms'
  'tag:add or remove tags for all messages matching the search terms'
 @@ -53,7 +54,14 @@ _notmuch_search()
  '--max-threads=[display only the first x threads from the search 
 results]:number of threads to show: ' \
  '--first=[omit the first x threads from the search results]:number of 
 threads to omit: ' \
  '--sort=[sort results]:sorting:((newest-first\:reverse chronological 
 order oldest-first\:chronological order

[PATCH v3 03/10] cli: search: Convert ctx. to ctx-

2014-11-04 Thread Michal Sojka
In the next commit, notmuch_search_command will be refactored to
several smaller functions. In order to simplify the next commit to
verbatim move of several lines to new functions with search_context_t*
argument, we convert all references to this structure to pointer
dereferences. To do so we rename the context variable and use the
original name ctx as the pointer to the renamed structure.
---
 notmuch-search.c | 81 
 1 file changed, 41 insertions(+), 40 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 3d2012b..6765a16 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -474,7 +474,7 @@ do_search_tags (const search_context_t *ctx)
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-search_context_t ctx = {
+search_context_t search_context = {
.format_sel = NOTMUCH_FORMAT_TEXT,
.exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
@@ -483,23 +483,24 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
.limit = -1, /* unlimited */
.dupe = -1,
 };
+search_context_t *ctx = search_context;
 char *query_str;
 int opt_index, ret;
 unsigned int i;
 
 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's',
+   { NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's',
  (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST },
  { newest-first, NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f',
+   { NOTMUCH_OPT_KEYWORD, ctx-format_sel, format, 'f',
  (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON },
  { sexp, NOTMUCH_FORMAT_SEXP },
  { text, NOTMUCH_FORMAT_TEXT },
  { text0, NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, notmuch_format_version, format-version, 0, 0 },
-   { NOTMUCH_OPT_KEYWORD_FLAGS, ctx.output, output, 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o',
  (notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY },
  { threads, OUTPUT_THREADS },
  { messages, OUTPUT_MESSAGES },
@@ -508,15 +509,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
  { files, OUTPUT_FILES },
  { tags, OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x',
+{ NOTMUCH_OPT_KEYWORD, ctx-exclude, exclude, 'x',
   (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE },
   { false, NOTMUCH_EXCLUDE_FALSE },
   { flag, NOTMUCH_EXCLUDE_FLAG },
   { all, NOTMUCH_EXCLUDE_ALL },
   { 0, 0 } } },
-   { NOTMUCH_OPT_INT, ctx.offset, offset, 'O', 0 },
-   { NOTMUCH_OPT_INT, ctx.limit, limit, 'L', 0  },
-   { NOTMUCH_OPT_INT, ctx.dupe, duplicate, 'D', 0  },
+   { NOTMUCH_OPT_INT, ctx-offset, offset, 'O', 0 },
+   { NOTMUCH_OPT_INT, ctx-limit, limit, 'L', 0  },
+   { NOTMUCH_OPT_INT, ctx-dupe, duplicate, 'D', 0  },
{ 0, 0, 0, 0, 0 }
 };
 
@@ -524,31 +525,31 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 if (opt_index  0)
return EXIT_FAILURE;
 
-if (! ctx.output)
-   ctx.output = OUTPUT_SUMMARY;
+if (! ctx-output)
+   ctx-output = OUTPUT_SUMMARY;
 
-if (ctx.output != OUTPUT_FILES  ctx.output != OUTPUT_MESSAGES 
-   ctx.dupe != -1) {
+if (ctx-output != OUTPUT_FILES  ctx-output != OUTPUT_MESSAGES 
+   ctx-dupe != -1) {
 fprintf (stderr, Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n);
 return EXIT_FAILURE;
 }
 
-switch (ctx.format_sel) {
+switch (ctx-format_sel) {
 case NOTMUCH_FORMAT_TEXT:
-   ctx.format = sprinter_text_create (config, stdout);
+   ctx-format = sprinter_text_create (config, stdout);
break;
 case NOTMUCH_FORMAT_TEXT0:
-   if (ctx.output == OUTPUT_SUMMARY) {
+   if (ctx-output == OUTPUT_SUMMARY) {
fprintf (stderr, Error: --format=text0 is not compatible with 
--output=summary.\n);
return EXIT_FAILURE;
}
-   ctx.format = sprinter_text0_create (config, stdout);
+   ctx-format = sprinter_text0_create (config, stdout);
break;
 case NOTMUCH_FORMAT_JSON:
-   ctx.format = sprinter_json_create (config, stdout);
+   ctx-format = sprinter_json_create (config, stdout);
break;
 case NOTMUCH_FORMAT_SEXP:
-   ctx.format = sprinter_sexp_create 

[PATCH v3 00/10] notmuch address command

2014-11-04 Thread Michal Sojka
Hi all,

this is v3 of notmuch address patchset. It obsoletes [1].

I think I addressed all comments made to v2. The diff between v2 and
v3 is below. Besides this, I improved commit messages.

I also tried to get rid of global variables in 6/10, but it looked
ugly, because the definition of keywords (e.g. text, json, ...)
had to be outside of option definition. So I kept the code as it was
in v2.

It seems that the agreement is to merge patches 1-9 for 0.19 and leave
patch 10 for further discussion.

Thanks
-Michal

[1] id:1415058622-21162-1-git-send-email-sojk...@fel.cvut.cz


Jani Nikula (1):
  cli: add support for hierarchical command line option arrays

Michal Sojka (9):
  cli: search: Rename options to context
  cli: search: Move more variables into search_context_t
  cli: search: Convert ctx. to ctx-
  cli: search: Split notmuch_search_command to smaller functions
  cli: Introduce notmuch address command
  cli: search: Convert --output to keyword argument
  cli: address: Do not output duplicate addresses
  cli: address: Add --output=count
  cli: address: Add --filter-by option to configure address filtering

 command-line-arguments.c   |  16 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |  48 +++-
 completion/notmuch-completion.zsh  |  11 +-
 doc/man1/notmuch-address.rst   | 132 +++
 doc/man1/notmuch-search.rst|  21 +-
 doc/man1/notmuch.rst   |   7 +-
 notmuch-client.h   |   3 +
 notmuch-search.c   | 461 ++---
 notmuch.c  |   2 +
 test/T095-address.sh   | 148 
 test/T097-address-filter-by.sh |  73 ++
 12 files changed, 750 insertions(+), 173 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst
 create mode 100755 test/T095-address.sh
 create mode 100755 test/T097-address-filter-by.sh

-- 
2.1.1

diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 524ab91..00582c3 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -30,7 +30,7 @@ Supported options for **address** include
 intended for programs that invoke **notmuch(1)** internally. If
 omitted, the latest supported version will be used.
 
-``--output=(sender|recipients)``
+``--output=(sender|recipients|count)``
 
 Controls which information appears in the output. This option
can be given multiple times to combine different outputs.
@@ -64,7 +64,9 @@ Supported options for **address** include
 By default, results will be displayed in reverse chronological
 order, (that is, the newest results will be displayed first).
 
-``--exclude=(true|false|all|flag)``
+   This option has no effect when used with --output=count.
+
+``--exclude=(true|false)``
 A message is called excluded if it matches at least one tag in
 search.tag\_exclude that does not appear explicitly in the
 search terms. This option specifies whether to omit excluded
@@ -73,18 +75,8 @@ Supported options for **address** include
 The default value, **true**, prevents excluded messages from
 matching the search terms.
 
-**all** additionally prevents excluded messages from appearing
-in displayed results, in effect behaving as though the excluded
-messages do not exist.
-
 **false** allows excluded messages to match search terms and
-appear in displayed results. Excluded messages are still marked
-in the relevant outputs.
-
-**flag** only has an effect when ``--output=summary``. The
-output is almost identical to **false**, but the match count
-is the number of matching non-excluded messages in the thread,
-rather than the number of matching messages.
+appear in displayed results.
 
 ``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
 
diff --git a/notmuch-search.c b/notmuch-search.c
index 04e33c6..246ec0a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -252,7 +252,9 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }
 
-/* Returns TRUE iff name and/or addr is considered duplicate. */
+/* Returns TRUE iff name and/or addr is considered duplicate. If not,
+ * stores the name/addr pair in order to detect subsequent
+ * duplicates. */
 static notmuch_bool_t
 is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
 {
@@ -405,8 +407,9 @@ process_address_header (const search_context_t *ctx, const 
char *value)
 g_object_unref (list);
 }
 
+/* Destructor for talloc-allocated GHashTable keys and values. */
 static void
-_my_talloc_free_for_g_hash (void *ptr)
+_talloc_free_for_g_hash (void *ptr)
 {
 talloc_free (ptr);
 }
@@ -679,12 +682,6 @@ static const notmuch_opt_desc_t common_options[] = {
  { text0, NOTMUCH_FORMAT_TEXT0

[PATCH v3 07/10] cli: search: Convert --output to keyword argument

2014-11-04 Thread Michal Sojka
Now, when address related outputs are in a separate command, it makes
no sense to combine multiple --output options in search command line.
Using switch statement to handle different outputs is more readable
than a series of if statements.
---
 doc/man1/notmuch-search.rst |  3 ---
 notmuch-search.c| 25 +
 2 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 65df288..0cc2911 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -78,9 +78,6 @@ Supported options for **search** include
 by null characters (--format=text0), as a JSON array
 (--format=json), or as an S-Expression list (--format=sexp).
 
-   This option can be given multiple times to combine different
-   outputs.
-
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
diff --git a/notmuch-search.c b/notmuch-search.c
index 69938d6..e084d25 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -587,7 +587,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 int opt_index, ret;
 
 notmuch_opt_desc_t options[] = {
-   { NOTMUCH_OPT_KEYWORD_FLAGS, ctx-output, output, 'o',
+   { NOTMUCH_OPT_KEYWORD, ctx-output, output, 'o',
  (notmuch_keyword_t []){ { summary, OUTPUT_SUMMARY },
  { threads, OUTPUT_THREADS },
  { messages, OUTPUT_MESSAGES },
@@ -607,13 +607,11 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
{ 0, 0, 0, 0, 0 }
 };
 
+ctx-output = OUTPUT_SUMMARY;
 opt_index = parse_arguments (argc, argv, options, 1);
 if (opt_index  0)
return EXIT_FAILURE;
 
-if (! ctx-output)
-   ctx-output = OUTPUT_SUMMARY;
-
 if (ctx-output != OUTPUT_FILES  ctx-output != OUTPUT_MESSAGES 
ctx-dupe != -1) {
 fprintf (stderr, Error: --duplicate=N is only supported with 
--output=files and --output=messages.\n);
@@ -624,17 +622,20 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;
 
-if (ctx-output == OUTPUT_SUMMARY ||
-   ctx-output == OUTPUT_THREADS)
+switch (ctx-output) {
+case OUTPUT_SUMMARY:
+case OUTPUT_THREADS:
ret = do_search_threads (ctx);
-else if (ctx-output == OUTPUT_MESSAGES ||
-ctx-output == OUTPUT_FILES)
+   break;
+case OUTPUT_MESSAGES:
+case OUTPUT_FILES:
ret = do_search_messages (ctx);
-else if (ctx-output == OUTPUT_TAGS)
+   break;
+case OUTPUT_TAGS:
ret = do_search_tags (ctx);
-else {
-   fprintf (stderr, Error: the combination of outputs is not 
supported.\n);
-   ret = 1;
+   break;
+default:
+   INTERNAL_ERROR (Unexpected output);
 }
 
 _notmuch_search_cleanup (ctx);
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3 05/10] cli: add support for hierarchical command line option arrays

2014-11-04 Thread Michal Sojka
From: Jani Nikula j...@nikula.org

NOTMUCH_OPT_INHERIT expects a notmuch_opt_desc_t * pointer in
output_var.

The Unrecognized option message was moved out of parse_option() to
not be emitted twice or when parsing a non-inherited option.
---
 command-line-arguments.c | 16 +---
 command-line-arguments.h |  1 +
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index c6f7269..de6b453 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -122,16 +122,18 @@ parse_position_arg (const char *arg_str, int 
pos_arg_index,
  */
 
 notmuch_bool_t
-parse_option (const char *arg,
- const notmuch_opt_desc_t *options) {
-
-assert(arg);
+parse_option (const char *_arg, const notmuch_opt_desc_t *options)
+{
+assert(_arg);
 assert(options);
 
-arg += 2;
-
+const char *arg = _arg + 2; /* _arg starts with -- */
 const notmuch_opt_desc_t *try;
 for (try = options; try-opt_type != NOTMUCH_OPT_END; try++) {
+   if (try-opt_type == NOTMUCH_OPT_INHERIT 
+   parse_option (_arg, try-output_var))
+   return TRUE;
+
if (! try-name)
continue;
 
@@ -170,7 +172,6 @@ parse_option (const char *arg,
/*UNREACHED*/
}
 }
-fprintf (stderr, Unrecognized option: --%s\n, arg);
 return FALSE;
 }
 
@@ -201,6 +202,7 @@ parse_arguments (int argc, char **argv,
if (more_args) {
opt_index++;
} else {
+   fprintf (stderr, Unrecognized option: %s\n, argv[opt_index]);
opt_index = -1;
}
 
diff --git a/command-line-arguments.h b/command-line-arguments.h
index 6444129..309aaf2 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -5,6 +5,7 @@
 
 enum notmuch_opt_type {
 NOTMUCH_OPT_END = 0,
+NOTMUCH_OPT_INHERIT,   /* another options table */
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v3 10/10] cli: address: Add --filter-by option to configure address filtering

2014-11-04 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  1 +
 doc/man1/notmuch-address.rst   | 36 ++-
 notmuch-search.c   | 51 --
 test/T097-address-filter-by.sh | 73 ++
 5 files changed, 162 insertions(+), 5 deletions(-)
 create mode 100755 test/T097-address-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index db152f3..2cb1586 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -310,7 +310,7 @@ _notmuch_search()
 ! $split 
 case ${cur} in
-*)
-   local options=--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate=
+   local options=--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by=
compopt -o nospace
COMPREPLY=( $(compgen -W $options -- ${cur}) )
;;
@@ -343,6 +343,10 @@ _notmuch_address()
COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W nameaddr name addr addrfold 
nameaddrfold -- ${cur} ) )
+   return
+   ;;
 esac
 
 ! $split 
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 8968562..3758f1a 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -62,6 +62,7 @@ _notmuch_address()
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
 '--output=[select what to output]:output:((sender recipients count))'
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:both 
name and address part name\:name part addr\:address part 
addrfold\:case-insensitive address part nameaddrfold\:name and 
case-insensitive address part))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 359616e..00582c3 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,8 @@ DESCRIPTION
 ===
 
 Search for messages matching the given search terms, and display the
-addresses from them. Duplicate addresses are filtered out.
+addresses from them. Duplicate addresses are filtered out. Filtering
+can be configured with the --filter-by option.
 
 See **notmuch-search-terms(7)** for details of the supported syntax for
 search-terms.
@@ -77,6 +78,39 @@ Supported options for **address** include
 **false** allows excluded messages to match search terms and
 appear in displayed results.
 
+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Controls how to filter out duplicate addresses. The filtering
+   algorithm receives a sequence of email addresses and outputs
+   the same sequence without the addresses that are considered a
+   duplicate of a previously output address. What is considered a
+   duplicate depends on how the two addresses are compared:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses John Doe
+   m...@example.com and John Doe j...@doe.name will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses John Doe
+   j...@example.com and Dr. John Doe j...@example.com will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses John Doe
+   j...@example.com and Dr. John Doe j...@example.com will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For example, the
+   addresses John Doe j...@example.com and John Doe
+   j...@example.com will be considered duplicate.
+
 EXIT STATUS
 ===
 
diff --git a/notmuch-search.c b/notmuch-search.c
index 5036d8e..246ec0a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -43,6 +43,14 @@ typedef enum {
 NOTMUCH_FORMAT_SEXP
 } format_sel_t;
 
+typedef enum {
+FILTER_BY_NAMEADDR = 0,
+FILTER_BY_NAME,
+FILTER_BY_ADDR,
+FILTER_BY_ADDRFOLD,
+FILTER_BY_NAMEADDRFOLD,
+} 

[PATCH v3 02/10] cli: search: Move more variables into search_context_t

2014-11-04 Thread Michal Sojka
In order to share some command line options between search and address
subcommands we need to add corresponding variables to the context
structure. While we are at it, we also add notmuch_database_t to unify
parameters of all do_search_* functions and to simplify subsequent
commits.

Otherwise, there are no functional changes.
---
 notmuch-search.c | 49 ++---
 1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 2c47b80..3d2012b 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -34,8 +34,18 @@ typedef enum {
 
 #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
 
+typedef enum {
+NOTMUCH_FORMAT_JSON,
+NOTMUCH_FORMAT_TEXT,
+NOTMUCH_FORMAT_TEXT0,
+NOTMUCH_FORMAT_SEXP
+} format_sel_t;
+
 typedef struct {
+notmuch_database_t *notmuch;
+format_sel_t format_sel;
 sprinter_t *format;
+notmuch_exclude_t exclude;
 notmuch_query_t *query;
 notmuch_sort_t sort;
 output_t output;
@@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx)
 }
 
 static int
-do_search_tags (notmuch_database_t *notmuch,
-   const search_context_t *ctx)
+do_search_tags (const search_context_t *ctx)
 {
 notmuch_messages_t *messages = NULL;
 notmuch_tags_t *tags;
 const char *tag;
 sprinter_t *format = ctx-format;
 notmuch_query_t *query = ctx-query;
+notmuch_database_t *notmuch = ctx-notmuch;
 
 /* should the following only special case if no excluded terms
  * specified? */
@@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch,
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-notmuch_database_t *notmuch;
 search_context_t ctx = {
+   .format_sel = NOTMUCH_FORMAT_TEXT,
+   .exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
.output = 0,
.offset = 0,
@@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 };
 char *query_str;
 int opt_index, ret;
-notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE;
 unsigned int i;
 
-enum {
-   NOTMUCH_FORMAT_JSON,
-   NOTMUCH_FORMAT_TEXT,
-   NOTMUCH_FORMAT_TEXT0,
-   NOTMUCH_FORMAT_SEXP
-} format_sel = NOTMUCH_FORMAT_TEXT;
-
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's',
  (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST },
  { newest-first, NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, format_sel, format, 'f',
+   { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f',
  (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON },
  { sexp, NOTMUCH_FORMAT_SEXP },
  { text, NOTMUCH_FORMAT_TEXT },
@@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { files, OUTPUT_FILES },
  { tags, OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, exclude, exclude, 'x',
+{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x',
   (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE },
   { false, NOTMUCH_EXCLUDE_FALSE },
   { flag, NOTMUCH_EXCLUDE_FLAG },
@@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 return EXIT_FAILURE;
 }
 
-switch (format_sel) {
+switch (ctx.format_sel) {
 case NOTMUCH_FORMAT_TEXT:
ctx.format = sprinter_text_create (config, stdout);
break;
@@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 notmuch_exit_if_unsupported_format ();
 
 if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, notmuch))
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx.notmuch))
return EXIT_FAILURE;
 
-query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
+query_str = query_string_from_args (ctx.notmuch, argc-opt_index, 
argv+opt_index);
 if (query_str == NULL) {
fprintf (stderr, Out of memory.\n);
return EXIT_FAILURE;
@@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
return EXIT_FAILURE;
 }
 
-ctx.query = notmuch_query_create (notmuch, query_str);
+ctx.query = notmuch_query_create (ctx.notmuch, query_str);
 if (ctx.query == NULL) {
fprintf (stderr, Out of memory\n);
return EXIT_FAILURE;
@@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 
 notmuch_query_set_sort 

[PATCH v3 08/10] cli: address: Do not output duplicate addresses

2014-11-04 Thread Michal Sojka
This filters out duplicate addresses from address command output.

It also also adds tests for the address command.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-address.rst |  2 +-
 notmuch-search.c | 42 ++-
 test/T095-address.sh | 99 
 3 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100755 test/T095-address.sh

diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index d349237..01eb811 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -11,7 +11,7 @@ DESCRIPTION
 ===
 
 Search for messages matching the given search terms, and display the
-addresses from them.
+addresses from them. Duplicate addresses are filtered out.
 
 See **notmuch-search-terms(7)** for details of the supported syntax for
 search-terms.
diff --git a/notmuch-search.c b/notmuch-search.c
index e084d25..86d54ba 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -53,6 +53,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
+GHashTable *addresses;
 } search_context_t;
 
 typedef struct {
@@ -240,6 +241,28 @@ do_search_threads (search_context_t *ctx)
 return 0;
 }
 
+/* Returns TRUE iff name and addr is duplicate. If not, stores the
+ * name/addr pair in order to detect subsequent duplicates. */
+static notmuch_bool_t
+is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (ctx-format, %s %s, name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (ctx-addresses, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
@@ -274,7 +297,8 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)
 
 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_context_t *ctx, InternetAddressList *list)
+process_address_list (const search_context_t *ctx,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -298,6 +322,9 @@ process_address_list (const search_context_t *ctx, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};
 
+   if (is_duplicate (ctx, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (ctx, mbx);
}
 }
@@ -321,6 +348,13 @@ process_address_header (const search_context_t *ctx, const 
char *value)
 g_object_unref (list);
 }
 
+/* Destructor for talloc-allocated GHashTable keys and values. */
+static void
+_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
+}
+
 static int
 _count_filenames (notmuch_message_t *message)
 {
@@ -673,8 +707,14 @@ notmuch_address_command (notmuch_config_t *config, int 
argc, char *argv[])
 argc - opt_index, argv + opt_index))
return EXIT_FAILURE;
 
+ctx-addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+   _talloc_free_for_g_hash, NULL);
+
 ret = do_search_messages (ctx);
 
+g_hash_table_unref (ctx-addresses);
+
+
 _notmuch_search_cleanup (ctx);
 
 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/test/T095-address.sh b/test/T095-address.sh
new file mode 100755
index 000..0d47c0d
--- /dev/null
+++ b/test/T095-address.sh
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+test_description='notmuch address in several variants'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest --output=sender
+notmuch address --output=sender '*' OUTPUT
+cat EOF EXPECTED
+François Boulogne boulogn...@gmail.com
+Olivier Berger olivier.ber...@it-sudparis.eu
+Chris Wilson ch...@chris-wilson.co.uk
+Carl Worth cwo...@cworth.org
+Alexander Botero-Lowry alex.boterolo...@gmail.com
+Keith Packard kei...@keithp.com
+Jjgod Jiang gzjj...@gmail.com
+Rolland Santimano rollandsantim...@yahoo.com
+Jan Janak j...@ryngle.com
+Stewart Smith stew...@flamingspork.com
+Lars Kellogg-Stedman l...@seas.harvard.edu
+Alex Botero-Lowry alex.boterolo...@gmail.com
+Ingmar Vanhassel ing...@exherbo.org
+Aron Griffis agrif...@n01se.net
+Adrian Perez de Castro ape...@igalia.com
+Israel Herraiz i...@herraiz.org
+Mikhail Gusarov dotted...@dottedmag.net
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest --output=sender --format=json
+notmuch address --output=sender --format=json '*' OUTPUT
+cat EOF EXPECTED
+[{name: François Boulogne, address: boulogn...@gmail.com, name-addr: 
François Boulogne boulogn...@gmail.com},
+{name: Olivier Berger, address: olivier.ber...@it-sudparis.eu, 
name-addr: Olivier Berger olivier.ber...@it-sudparis.eu},
+{name: Chris Wilson, 

[PATCH v3 09/10] cli: address: Add --output=count

2014-11-04 Thread Michal Sojka
This output prints how many times was each address encountered during
search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-address.rst   | 11 -
 notmuch-search.c   | 49 --
 test/T095-address.sh   | 49 ++
 5 files changed, 103 insertions(+), 10 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 94ea2d5..db152f3 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -332,7 +332,7 @@ _notmuch_address()
return
;;
--output)
-   COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) )
+   COMPREPLY=( $( compgen -W sender recipients count -- ${cur} ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index c606b75..8968562 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -61,7 +61,7 @@ _notmuch_address()
 {
   _arguments -s : \
 '--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
-'--output=[select what to output]:output:((sender recipients))'
+'--output=[select what to output]:output:((sender recipients count))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
index 01eb811..359616e 100644
--- a/doc/man1/notmuch-address.rst
+++ b/doc/man1/notmuch-address.rst
@@ -29,7 +29,7 @@ Supported options for **address** include
 intended for programs that invoke **notmuch(1)** internally. If
 omitted, the latest supported version will be used.
 
-``--output=(sender|recipients)``
+``--output=(sender|recipients|count)``
 
 Controls which information appears in the output. This option
can be given multiple times to combine different outputs.
@@ -48,6 +48,13 @@ Supported options for **address** include
 Output all addresses from the *To*, *Cc* and *Bcc*
 headers.
 
+   **count**
+   Print the count of how many times was the address
+   encountered during search.
+
+   Note: With this option, addresses are printed only after
+   the whole search is finished. This may take long time.
+
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
 chronological order (**oldest-first**) or reverse chronological
@@ -56,6 +63,8 @@ Supported options for **address** include
 By default, results will be displayed in reverse chronological
 order, (that is, the newest results will be displayed first).
 
+   This option has no effect when used with --output=count.
+
 ``--exclude=(true|false)``
 A message is called excluded if it matches at least one tag in
 search.tag\_exclude that does not appear explicitly in the
diff --git a/notmuch-search.c b/notmuch-search.c
index 86d54ba..5036d8e 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -33,6 +33,7 @@ typedef enum {
 /* Address command */
 OUTPUT_SENDER  = 1  5,
 OUTPUT_RECIPIENTS  = 1  6,
+OUTPUT_COUNT   = 1  7,
 } output_t;
 
 typedef enum {
@@ -59,6 +60,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;
 
 /* Return two stable query strings that identify exactly the matched
@@ -248,17 +250,24 @@ is_duplicate (const search_context_t *ctx, const char 
*name, const char *addr)
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;
 
 key = talloc_asprintf (ctx-format, %s %s, name, addr);
 if (! key)
return FALSE;
 
-duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (ctx-addresses, key, NULL, 
(gpointer)mailbox);
 
-if (! duplicate)
-   g_hash_table_insert (ctx-addresses, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (ctx-format, mailbox_t);
+   mailbox-name = talloc_strdup (mailbox, name);
+   mailbox-addr = talloc_strdup (mailbox, addr);
+   mailbox-count = 1;
+   g_hash_table_insert (ctx-addresses, key, mailbox);
+} else {
+   mailbox-count++;
talloc_free (key);
+}
 
 return duplicate;
 }
@@ -268,6 +277,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox-name;
 const char *addr = mailbox-addr;
+int count = mailbox-count;
 sprinter_t *format = ctx-format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
@@ -277,6 +287,10 @@ print_mailbox (const search_context_t *ctx, const 
mailbox_t *mailbox)
 name_addr = internet_address_to_string (ia, FALSE);
 

[PATCH v3 04/10] cli: search: Split notmuch_search_command to smaller functions

2014-11-04 Thread Michal Sojka
In a subsequent commit, these functions will be used to share some
functionality between search and address commands.
---
 notmuch-search.c | 155 ++-
 1 file changed, 86 insertions(+), 69 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6765a16..f115359 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx)
 return 0;
 }
 
+static int
+_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int 
argc, char *argv[])
+{
+char *query_str;
+unsigned int i;
+
+switch (ctx-format_sel) {
+case NOTMUCH_FORMAT_TEXT:
+   ctx-format = sprinter_text_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_TEXT0:
+   if (ctx-output == OUTPUT_SUMMARY) {
+   fprintf (stderr, Error: --format=text0 is not compatible with 
--output=summary.\n);
+   return EXIT_FAILURE;
+   }
+   ctx-format = sprinter_text0_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_JSON:
+   ctx-format = sprinter_json_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_SEXP:
+   ctx-format = sprinter_sexp_create (config, stdout);
+   break;
+default:
+   /* this should never happen */
+   INTERNAL_ERROR(no output format selected);
+}
+
+notmuch_exit_if_unsupported_format ();
+
+if (notmuch_database_open (notmuch_config_get_database_path (config),
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch))
+   return EXIT_FAILURE;
+
+query_str = query_string_from_args (ctx-notmuch, argc, argv);
+if (query_str == NULL) {
+   fprintf (stderr, Out of memory.\n);
+   return EXIT_FAILURE;
+}
+if (*query_str == '\0') {
+   fprintf (stderr, Error: notmuch search requires at least one search 
term.\n);
+   return EXIT_FAILURE;
+}
+
+ctx-query = notmuch_query_create (ctx-notmuch, query_str);
+if (ctx-query == NULL) {
+   fprintf (stderr, Out of memory\n);
+   return EXIT_FAILURE;
+}
+
+notmuch_query_set_sort (ctx-query, ctx-sort);
+
+if (ctx-exclude == NOTMUCH_EXCLUDE_FLAG  ctx-output != OUTPUT_SUMMARY) 
{
+   /* If we are not doing summary output there is nowhere to
+* print the excluded flag so fall back on including the
+* excluded messages. */
+   fprintf (stderr, Warning: this output format cannot flag excluded 
messages.\n);
+   ctx-exclude = NOTMUCH_EXCLUDE_FALSE;
+}
+
+if (ctx-exclude != NOTMUCH_EXCLUDE_FALSE) {
+   const char **search_exclude_tags;
+   size_t search_exclude_tags_length;
+
+   search_exclude_tags = notmuch_config_get_search_exclude_tags
+   (config, search_exclude_tags_length);
+   for (i = 0; i  search_exclude_tags_length; i++)
+   notmuch_query_add_tag_exclude (ctx-query, search_exclude_tags[i]);
+   notmuch_query_set_omit_excluded (ctx-query, ctx-exclude);
+}
+
+return 0;
+}
+
+static void
+_notmuch_search_cleanup (search_context_t *ctx)
+{
+notmuch_query_destroy (ctx-query);
+notmuch_database_destroy (ctx-notmuch);
+
+talloc_free (ctx-format);
+}
+
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
@@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
.dupe = -1,
 };
 search_context_t *ctx = search_context;
-char *query_str;
 int opt_index, ret;
-unsigned int i;
 
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's',
@@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 return EXIT_FAILURE;
 }
 
-switch (ctx-format_sel) {
-case NOTMUCH_FORMAT_TEXT:
-   ctx-format = sprinter_text_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_TEXT0:
-   if (ctx-output == OUTPUT_SUMMARY) {
-   fprintf (stderr, Error: --format=text0 is not compatible with 
--output=summary.\n);
-   return EXIT_FAILURE;
-   }
-   ctx-format = sprinter_text0_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_JSON:
-   ctx-format = sprinter_json_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_SEXP:
-   ctx-format = sprinter_sexp_create (config, stdout);
-   break;
-default:
-   /* this should never happen */
-   INTERNAL_ERROR(no output format selected);
-}
-
-notmuch_exit_if_unsupported_format ();
-
-if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch))
+if (_notmuch_search_prepare (ctx, config,
+argc - opt_index, argv + opt_index))
return EXIT_FAILURE;
 
-query_str = query_string_from_args (ctx-notmuch, argc-opt_index, 
argv+opt_index);
-if (query_str == NULL) {
-   

[PATCH v3 01/10] cli: search: Rename options to context

2014-11-04 Thread Michal Sojka
In the next commit the options structure will be extended by
non-option variables. Therefore we need a more generic name.

Just text replacement, no other changes.
---
 notmuch-search.c | 142 +++
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6345fb6..2c47b80 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -42,7 +42,7 @@ typedef struct {
 int offset;
 int limit;
 int dupe;
-} search_options_t;
+} search_context_t;
 
 typedef struct {
 const char *name;
@@ -89,39 +89,39 @@ get_thread_query (notmuch_thread_t *thread,
 }
 
 static int
-do_search_threads (search_options_t *opt)
+do_search_threads (search_context_t *ctx)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
-sprinter_t *format = opt-format;
+sprinter_t *format = ctx-format;
 time_t date;
 int i;
 
-if (opt-offset  0) {
-   opt-offset += notmuch_query_count_threads (opt-query);
-   if (opt-offset  0)
-   opt-offset = 0;
+if (ctx-offset  0) {
+   ctx-offset += notmuch_query_count_threads (ctx-query);
+   if (ctx-offset  0)
+   ctx-offset = 0;
 }
 
-threads = notmuch_query_search_threads (opt-query);
+threads = notmuch_query_search_threads (ctx-query);
 if (threads == NULL)
return 1;
 
 format-begin_list (format);
 
 for (i = 0;
-notmuch_threads_valid (threads)  (opt-limit  0 || i  opt-offset 
+ opt-limit);
+notmuch_threads_valid (threads)  (ctx-limit  0 || i  ctx-offset 
+ ctx-limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);
 
-   if (i  opt-offset) {
+   if (i  ctx-offset) {
notmuch_thread_destroy (thread);
continue;
}
 
-   if (opt-output == OUTPUT_THREADS) {
+   if (ctx-output == OUTPUT_THREADS) {
format-set_prefix (format, thread);
format-string (format,
notmuch_thread_get_thread_id (thread));
@@ -138,7 +138,7 @@ do_search_threads (search_options_t *opt)
 
format-begin_map (format);
 
-   if (opt-sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (ctx-sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -230,11 +230,11 @@ do_search_threads (search_options_t *opt)
 }
 
 static void
-print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
+print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
 {
 const char *name = mailbox-name;
 const char *addr = mailbox-addr;
-sprinter_t *format = opt-format;
+sprinter_t *format = ctx-format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
 
@@ -263,7 +263,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)
 
 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_context_t *ctx, InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -279,7 +279,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;
 
-   process_address_list (opt, group_list);
+   process_address_list (ctx, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -287,14 +287,14 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};
 
-   print_mailbox (opt, mbx);
+   print_mailbox (ctx, mbx);
}
 }
 }
 
 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_context_t *ctx, const char *value)
 {
 InternetAddressList *list;
 
@@ -305,7 +305,7 @@ process_address_header (const search_options_t *opt, const 
char *value)
 if (list == NULL)
return;
 
-process_address_list (opt, list);
+process_address_list (ctx, list);
 
 g_object_unref (list);
 }
@@ -329,36 +329,36 @@ _count_filenames (notmuch_message_t *message)
 }
 
 static int
-do_search_messages (search_options_t *opt)
+do_search_messages (search_context_t *ctx)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
-sprinter_t *format = opt-format;
+sprinter_t *format = ctx-format;
 int i;
 
-if (opt-offset  0) {
-   opt-offset += notmuch_query_count_messages (opt-query);
-   if (opt-offset  0)
-   

[PATCH v3 06/10] cli: Introduce notmuch address command

2014-11-04 Thread Michal Sojka
This moves address-related functionality from search command to the
new address command. The implementation shares almost all code and
some command line options.

Options --offset and --limit were intentionally not included in the
address command, because they refer to messages numbers, which users
do not see in the output. This could confuse users because, for
example, they could see more addresses in the output that what was
specified with --limit. This functionality can be correctly
reimplemented for address subcommand later.

Also useless values of --exclude flag were not included in the address
command.

This was inspired by a patch from Jani Nikula.
---
 completion/notmuch-completion.bash | 42 -
 completion/notmuch-completion.zsh  | 10 +++-
 doc/man1/notmuch-address.rst   | 89 
 doc/man1/notmuch-search.rst| 20 +---
 doc/man1/notmuch.rst   |  7 +--
 notmuch-client.h   |  3 ++
 notmuch-search.c   | 93 +++---
 notmuch.c  |  2 +
 8 files changed, 216 insertions(+), 50 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..94ea2d5 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -294,7 +294,7 @@ _notmuch_search()
return
;;
--output)
-   COMPREPLY=( $( compgen -W summary threads messages files tags 
sender recipients -- ${cur} ) )
+   COMPREPLY=( $( compgen -W summary threads messages files tags -- 
${cur} ) )
return
;;
--sort)
@@ -320,6 +320,44 @@ _notmuch_search()
 esac
 }
 
+_notmuch_address()
+{
+local cur prev words cword split
+_init_completion -s || return
+
+$split 
+case ${prev} in
+   --format)
+   COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) )
+   return
+   ;;
+   --output)
+   COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) )
+   return
+   ;;
+   --sort)
+   COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) 
)
+   return
+   ;;
+   --exclude)
+   COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) )
+   return
+   ;;
+esac
+
+! $split 
+case ${cur} in
+   -*)
+   local options=--format= --output= --sort= --exclude=
+   compopt -o nospace
+   COMPREPLY=( $(compgen -W $options -- ${cur}) )
+   ;;
+   *)
+   _notmuch_search_terms
+   ;;
+esac
+}
+
 _notmuch_show()
 {
 local cur prev words cword split
@@ -393,7 +431,7 @@ _notmuch_tag()
 
 _notmuch()
 {
-local _notmuch_commands=compact config count dump help insert new reply 
restore search setup show tag
+local _notmuch_commands=compact config count dump help insert new reply 
restore search address setup show tag
 local arg cur prev words cword split
 
 # require bash-completion with _init_completion
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..c606b75 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -10,6 +10,7 @@ _notmuch_commands()
 'setup:interactively set up notmuch for first use'
 'new:find and import any new message to the database'
 'search:search for messages matching the search terms, display matching 
threads as results'
+'address:get addresses from messages matching the given search terms'
 'reply:constructs a reply template for a set of messages'
 'show:show all messages matching the search terms'
 'tag:add or remove tags for all messages matching the search terms'
@@ -53,7 +54,14 @@ _notmuch_search()
 '--max-threads=[display only the first x threads from the search 
results]:number of threads to show: ' \
 '--first=[omit the first x threads from the search results]:number of 
threads to omit: ' \
 '--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
-'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients))'
+'--output=[select what to output]:output:((summary threads messages files 
tags))'
+}
+
+_notmuch_address()
+{
+  _arguments -s : \
+'--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
+'--output=[select what to output]:output:((sender recipients))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
new file mode 100644
index 000..d349237
--- /dev/null
+++ b/doc/man1/notmuch-address.rst
@@ -0,0 +1,89 @@
+===
+notmuch-address
+===
+
+SYNOPSIS
+
+
+**notmuch** **address** [*option* ...] 

[PATCH v6 6/7] cli: search: Add --output=count

2014-11-03 Thread Michal Sojka
Hi Tomi,

On Mon, Nov 03 2014, Tomi Ollila wrote:
> On Sun, Nov 02 2014, Michal Sojka  wrote:
>
>> On 2. listopadu 2014 10:29:28 CET, Mark Walters > gmail.com> wrote:
>>>
>>>Hi
>>>
>>>On Sun, 02 Nov 2014, Michal Sojka  wrote:
>>>> On Sat, Nov 01 2014, Mark Walters wrote:
>>>>> On Fri, 31 Oct 2014, Michal Sojka  wrote:
>>>>>> This output can be used with --output=recipients or --output=sender
>>>>>> and in addition to the addresses, it prints how many times was each
>>>>>> address encountered during search.
>>>>>
>>>>> Hi
>>>>>
>>>>> I have a couple comments on this patch.
>>>>>
>>>>>> ---
>>>>>>  completion/notmuch-completion.bash |  2 +-
>>>>>>  completion/notmuch-completion.zsh  |  2 +-
>>>>>>  doc/man1/notmuch-search.rst|  9 +--
>>>>>>  notmuch-search.c   | 51
>>>--
>>>>>>  4 files changed, 52 insertions(+), 12 deletions(-)
>>>>>>
>>>>>> diff --git a/completion/notmuch-completion.bash
>>>b/completion/notmuch-completion.bash
>>>>>> index cfbd389..39cd829 100644
>>>>>> --- a/completion/notmuch-completion.bash
>>>>>> +++ b/completion/notmuch-completion.bash
>>>>>> @@ -294,7 +294,7 @@ _notmuch_search()
>>>>>>  return
>>>>>>  ;;
>>>>>>  --output)
>>>>>> -COMPREPLY=( $( compgen -W "summary threads messages files
>>>tags sender recipients" -- "${cur}" ) )
>>>>>> +COMPREPLY=( $( compgen -W "summary threads messages files
>>>tags sender recipients count" -- "${cur}" ) )
>>>>>>  return
>>>>>>  ;;
>>>>>>  --sort)
>>>>>> diff --git a/completion/notmuch-completion.zsh
>>>b/completion/notmuch-completion.zsh
>>>>>> index 3e52a00..d7e5a5e 100644
>>>>>> --- a/completion/notmuch-completion.zsh
>>>>>> +++ b/completion/notmuch-completion.zsh
>>>>>> @@ -53,7 +53,7 @@ _notmuch_search()
>>>>>>  '--max-threads=[display only the first x threads from the
>>>search results]:number of threads to show: ' \
>>>>>>  '--first=[omit the first x threads from the search
>>>results]:number of threads to omit: ' \
>>>>>>  '--sort=[sort results]:sorting:((newest-first\:"reverse
>>>chronological order" oldest-first\:"chronological order"))' \
>>>>>> -'--output=[select what to output]:output:((summary threads
>>>messages files tags sender recipients))'
>>>>>> +'--output=[select what to output]:output:((summary threads
>>>messages files tags sender recipients count))'
>>>>>>  }
>>>>>>  
>>>>>>  _notmuch()
>>>>>> diff --git a/doc/man1/notmuch-search.rst
>>>b/doc/man1/notmuch-search.rst
>>>>>> index 42f17e4..ec89200 100644
>>>>>> --- a/doc/man1/notmuch-search.rst
>>>>>> +++ b/doc/man1/notmuch-search.rst
>>>>>> @@ -96,9 +96,14 @@ Supported options for **search** include
>>>>>>  Like **sender** but for addresses from *To*, *Cc* and
>>>>>>  *Bcc* headers.
>>>>>>  
>>>>>> +**count**
>>>>>> +Can be used in combination with **sender** or
>>>>>> +**recipients** to print the count of how many times was
>>>>>> +the address encountered during search.
>>>>>> +
>>>>>>  This option can be given multiple times to combine different
>>>>>> -outputs. Currently, this is only supported for **sender** and
>>>>>> -**recipients** outputs.
>>>>>> +outputs. Currently, this is only supported for **sender**,
>>>>>> +**recipients** and **count** outputs.
>>>>>
>>>>> It might be worth saying that the results will be slower if count is
>>>>> specified.
>>>>
>>>> I wrote something like this in
>>>> id:1414889400-30977-10-git-send-email-sojkam1 at fel.cvut.cz.
>>>>
>>>>>
>>>>>>  ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
>>>>>
>>>>> I think sort works as expected if count is not specified, but does
>>>not
>>>>> with count. 
>>>>
>>>> Agreed.
>>>>
>>>>> Maybe count can be done by doing two passes? 
>>>>
>>>> What do you mean by pass?
>>>
>>>I mean iterating through the messages twice: so the first time would
>>>fill in the count in the hash map, and the second iteration would print
>>>the addresses.
>>
>> That's what the patch does. Only the second time it iterates the hash.
>
> Sorry 
>
> And we loose the order ? (or not ?) do we have "orderedhash" there...
> I'd like to save the order

What would be the use case?

If you really want to preserve the order, we could save a sequence
number in mailbox_t and sort the hash table entries before printing. But
this could be implemented later and enabled by a special command line
option. notmuch search --output=tags does not preserve order either.

> (but that is not so important) -- but more importantly, are tests
> robusts --- ah there is sort -n in the test...

Yes.

-Michal


Re: [PATCH v6 6/7] cli: search: Add --output=count

2014-11-03 Thread Michal Sojka
Hi Tomi,

On Mon, Nov 03 2014, Tomi Ollila wrote:
 On Sun, Nov 02 2014, Michal Sojka sojk...@fel.cvut.cz wrote:

 On 2. listopadu 2014 10:29:28 CET, Mark Walters markwalters1...@gmail.com 
 wrote:

Hi

On Sun, 02 Nov 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 On Sat, Nov 01 2014, Mark Walters wrote:
 On Fri, 31 Oct 2014, Michal Sojka sojk...@fel.cvut.cz wrote:
 This output can be used with --output=recipients or --output=sender
 and in addition to the addresses, it prints how many times was each
 address encountered during search.

 Hi

 I have a couple comments on this patch.

 ---
  completion/notmuch-completion.bash |  2 +-
  completion/notmuch-completion.zsh  |  2 +-
  doc/man1/notmuch-search.rst|  9 +--
  notmuch-search.c   | 51
--
  4 files changed, 52 insertions(+), 12 deletions(-)

 diff --git a/completion/notmuch-completion.bash
b/completion/notmuch-completion.bash
 index cfbd389..39cd829 100644
 --- a/completion/notmuch-completion.bash
 +++ b/completion/notmuch-completion.bash
 @@ -294,7 +294,7 @@ _notmuch_search()
  return
  ;;
  --output)
 -COMPREPLY=( $( compgen -W summary threads messages files
tags sender recipients -- ${cur} ) )
 +COMPREPLY=( $( compgen -W summary threads messages files
tags sender recipients count -- ${cur} ) )
  return
  ;;
  --sort)
 diff --git a/completion/notmuch-completion.zsh
b/completion/notmuch-completion.zsh
 index 3e52a00..d7e5a5e 100644
 --- a/completion/notmuch-completion.zsh
 +++ b/completion/notmuch-completion.zsh
 @@ -53,7 +53,7 @@ _notmuch_search()
  '--max-threads=[display only the first x threads from the
search results]:number of threads to show: ' \
  '--first=[omit the first x threads from the search
results]:number of threads to omit: ' \
  '--sort=[sort results]:sorting:((newest-first\:reverse
chronological order oldest-first\:chronological order))' \
 -'--output=[select what to output]:output:((summary threads
messages files tags sender recipients))'
 +'--output=[select what to output]:output:((summary threads
messages files tags sender recipients count))'
  }
  
  _notmuch()
 diff --git a/doc/man1/notmuch-search.rst
b/doc/man1/notmuch-search.rst
 index 42f17e4..ec89200 100644
 --- a/doc/man1/notmuch-search.rst
 +++ b/doc/man1/notmuch-search.rst
 @@ -96,9 +96,14 @@ Supported options for **search** include
  Like **sender** but for addresses from *To*, *Cc* and
  *Bcc* headers.
  
 +**count**
 +Can be used in combination with **sender** or
 +**recipients** to print the count of how many times was
 +the address encountered during search.
 +
  This option can be given multiple times to combine different
 -outputs. Currently, this is only supported for **sender** and
 -**recipients** outputs.
 +outputs. Currently, this is only supported for **sender**,
 +**recipients** and **count** outputs.

 It might be worth saying that the results will be slower if count is
 specified.

 I wrote something like this in
 id:1414889400-30977-10-git-send-email-sojk...@fel.cvut.cz.


  ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)

 I think sort works as expected if count is not specified, but does
not
 with count. 

 Agreed.

 Maybe count can be done by doing two passes? 

 What do you mean by pass?

I mean iterating through the messages twice: so the first time would
fill in the count in the hash map, and the second iteration would print
the addresses.

 That's what the patch does. Only the second time it iterates the hash.

 Sorry 

 And we loose the order ? (or not ?) do we have orderedhash there...
 I'd like to save the order

What would be the use case?

If you really want to preserve the order, we could save a sequence
number in mailbox_t and sort the hash table entries before printing. But
this could be implemented later and enabled by a special command line
option. notmuch search --output=tags does not preserve order either.

 (but that is not so important) -- but more importantly, are tests
 robusts --- ah there is sort -n in the test...

Yes.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v2 02/10] cli: search: Move more variables into search_context_t

2014-11-03 Thread Michal Sojka
Just refactoring, no functional changes.
---
 notmuch-search.c | 49 ++---
 1 file changed, 26 insertions(+), 23 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 2c47b80..3d2012b 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -34,8 +34,18 @@ typedef enum {
 
 #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
 
+typedef enum {
+NOTMUCH_FORMAT_JSON,
+NOTMUCH_FORMAT_TEXT,
+NOTMUCH_FORMAT_TEXT0,
+NOTMUCH_FORMAT_SEXP
+} format_sel_t;
+
 typedef struct {
+notmuch_database_t *notmuch;
+format_sel_t format_sel;
 sprinter_t *format;
+notmuch_exclude_t exclude;
 notmuch_query_t *query;
 notmuch_sort_t sort;
 output_t output;
@@ -413,14 +423,14 @@ do_search_messages (search_context_t *ctx)
 }
 
 static int
-do_search_tags (notmuch_database_t *notmuch,
-   const search_context_t *ctx)
+do_search_tags (const search_context_t *ctx)
 {
 notmuch_messages_t *messages = NULL;
 notmuch_tags_t *tags;
 const char *tag;
 sprinter_t *format = ctx-format;
 notmuch_query_t *query = ctx-query;
+notmuch_database_t *notmuch = ctx-notmuch;
 
 /* should the following only special case if no excluded terms
  * specified? */
@@ -464,8 +474,9 @@ do_search_tags (notmuch_database_t *notmuch,
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-notmuch_database_t *notmuch;
 search_context_t ctx = {
+   .format_sel = NOTMUCH_FORMAT_TEXT,
+   .exclude = NOTMUCH_EXCLUDE_TRUE,
.sort = NOTMUCH_SORT_NEWEST_FIRST,
.output = 0,
.offset = 0,
@@ -474,22 +485,14 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 };
 char *query_str;
 int opt_index, ret;
-notmuch_exclude_t exclude = NOTMUCH_EXCLUDE_TRUE;
 unsigned int i;
 
-enum {
-   NOTMUCH_FORMAT_JSON,
-   NOTMUCH_FORMAT_TEXT,
-   NOTMUCH_FORMAT_TEXT0,
-   NOTMUCH_FORMAT_SEXP
-} format_sel = NOTMUCH_FORMAT_TEXT;
-
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, ctx.sort, sort, 's',
  (notmuch_keyword_t []){ { oldest-first, NOTMUCH_SORT_OLDEST_FIRST },
  { newest-first, NOTMUCH_SORT_NEWEST_FIRST },
  { 0, 0 } } },
-   { NOTMUCH_OPT_KEYWORD, format_sel, format, 'f',
+   { NOTMUCH_OPT_KEYWORD, ctx.format_sel, format, 'f',
  (notmuch_keyword_t []){ { json, NOTMUCH_FORMAT_JSON },
  { sexp, NOTMUCH_FORMAT_SEXP },
  { text, NOTMUCH_FORMAT_TEXT },
@@ -505,7 +508,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { files, OUTPUT_FILES },
  { tags, OUTPUT_TAGS },
  { 0, 0 } } },
-{ NOTMUCH_OPT_KEYWORD, exclude, exclude, 'x',
+{ NOTMUCH_OPT_KEYWORD, ctx.exclude, exclude, 'x',
   (notmuch_keyword_t []){ { true, NOTMUCH_EXCLUDE_TRUE },
   { false, NOTMUCH_EXCLUDE_FALSE },
   { flag, NOTMUCH_EXCLUDE_FLAG },
@@ -530,7 +533,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 return EXIT_FAILURE;
 }
 
-switch (format_sel) {
+switch (ctx.format_sel) {
 case NOTMUCH_FORMAT_TEXT:
ctx.format = sprinter_text_create (config, stdout);
break;
@@ -555,10 +558,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 notmuch_exit_if_unsupported_format ();
 
 if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, notmuch))
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx.notmuch))
return EXIT_FAILURE;
 
-query_str = query_string_from_args (notmuch, argc-opt_index, 
argv+opt_index);
+query_str = query_string_from_args (ctx.notmuch, argc-opt_index, 
argv+opt_index);
 if (query_str == NULL) {
fprintf (stderr, Out of memory.\n);
return EXIT_FAILURE;
@@ -568,7 +571,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
return EXIT_FAILURE;
 }
 
-ctx.query = notmuch_query_create (notmuch, query_str);
+ctx.query = notmuch_query_create (ctx.notmuch, query_str);
 if (ctx.query == NULL) {
fprintf (stderr, Out of memory\n);
return EXIT_FAILURE;
@@ -576,15 +579,15 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 
 notmuch_query_set_sort (ctx.query, ctx.sort);
 
-if (exclude == NOTMUCH_EXCLUDE_FLAG  ctx.output != OUTPUT_SUMMARY) {
+if (ctx.exclude == NOTMUCH_EXCLUDE_FLAG  ctx.output != OUTPUT_SUMMARY) {
/* If we are not doing summary output there is nowhere to
 * print the excluded flag so fall 

[PATCH v2 06/10] cli: Introduce notmuch address command

2014-11-03 Thread Michal Sojka
This moves address-related functionality from search command to the
new address command. The implementation shares almost all code and
some command line options.

Options --offset and --limit were intentionally not included in the
address command, because they refer to messages numbers, which users
do not see in the output. This could confuse users because, for
example, they could see more addresses in the output that what was
specified with --limit. This functionality can be correctly
reimplemented for addresses later.

This was inspired by a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |  42 ++-
 completion/notmuch-completion.zsh  |  10 +++-
 doc/man1/notmuch-address.rst   |  99 
 doc/man1/notmuch-search.rst|  20 +---
 doc/man1/notmuch.rst   |   7 +--
 notmuch-client.h   |   3 ++
 notmuch-search.c   | 101 +
 notmuch.c  |   2 +
 8 files changed, 228 insertions(+), 56 deletions(-)
 create mode 100644 doc/man1/notmuch-address.rst

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..94ea2d5 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -294,7 +294,7 @@ _notmuch_search()
return
;;
--output)
-   COMPREPLY=( $( compgen -W summary threads messages files tags 
sender recipients -- ${cur} ) )
+   COMPREPLY=( $( compgen -W summary threads messages files tags -- 
${cur} ) )
return
;;
--sort)
@@ -320,6 +320,44 @@ _notmuch_search()
 esac
 }
 
+_notmuch_address()
+{
+local cur prev words cword split
+_init_completion -s || return
+
+$split 
+case ${prev} in
+   --format)
+   COMPREPLY=( $( compgen -W json sexp text text0 -- ${cur} ) )
+   return
+   ;;
+   --output)
+   COMPREPLY=( $( compgen -W sender recipients -- ${cur} ) )
+   return
+   ;;
+   --sort)
+   COMPREPLY=( $( compgen -W newest-first oldest-first -- ${cur} ) 
)
+   return
+   ;;
+   --exclude)
+   COMPREPLY=( $( compgen -W true false flag all -- ${cur} ) )
+   return
+   ;;
+esac
+
+! $split 
+case ${cur} in
+   -*)
+   local options=--format= --output= --sort= --exclude=
+   compopt -o nospace
+   COMPREPLY=( $(compgen -W $options -- ${cur}) )
+   ;;
+   *)
+   _notmuch_search_terms
+   ;;
+esac
+}
+
 _notmuch_show()
 {
 local cur prev words cword split
@@ -393,7 +431,7 @@ _notmuch_tag()
 
 _notmuch()
 {
-local _notmuch_commands=compact config count dump help insert new reply 
restore search setup show tag
+local _notmuch_commands=compact config count dump help insert new reply 
restore search address setup show tag
 local arg cur prev words cword split
 
 # require bash-completion with _init_completion
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..c606b75 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -10,6 +10,7 @@ _notmuch_commands()
 'setup:interactively set up notmuch for first use'
 'new:find and import any new message to the database'
 'search:search for messages matching the search terms, display matching 
threads as results'
+'address:get addresses from messages matching the given search terms'
 'reply:constructs a reply template for a set of messages'
 'show:show all messages matching the search terms'
 'tag:add or remove tags for all messages matching the search terms'
@@ -53,7 +54,14 @@ _notmuch_search()
 '--max-threads=[display only the first x threads from the search 
results]:number of threads to show: ' \
 '--first=[omit the first x threads from the search results]:number of 
threads to omit: ' \
 '--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
-'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients))'
+'--output=[select what to output]:output:((summary threads messages files 
tags))'
+}
+
+_notmuch_address()
+{
+  _arguments -s : \
+'--sort=[sort results]:sorting:((newest-first\:reverse chronological 
order oldest-first\:chronological order))' \
+'--output=[select what to output]:output:((sender recipients))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
new file mode 100644
index 000..8109f11
--- /dev/null
+++ b/doc/man1/notmuch-address.rst
@@ -0,0 +1,99 @@
+===
+notmuch-address
+===
+
+SYNOPSIS
+
+
+**notmuch** **address** [*option* ...] *search-term* ...
+
+DESCRIPTION
+===
+
+Search for messages matching the given 

[PATCH v2 04/10] cli: search: Split notmuch_search_command to smaller functions

2014-11-03 Thread Michal Sojka
In the next commit, these functions will be used to share some
functionality between search and address commands.
---
 notmuch-search.c | 155 ++-
 1 file changed, 86 insertions(+), 69 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 6765a16..f115359 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -471,6 +471,89 @@ do_search_tags (const search_context_t *ctx)
 return 0;
 }
 
+static int
+_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int 
argc, char *argv[])
+{
+char *query_str;
+unsigned int i;
+
+switch (ctx-format_sel) {
+case NOTMUCH_FORMAT_TEXT:
+   ctx-format = sprinter_text_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_TEXT0:
+   if (ctx-output == OUTPUT_SUMMARY) {
+   fprintf (stderr, Error: --format=text0 is not compatible with 
--output=summary.\n);
+   return EXIT_FAILURE;
+   }
+   ctx-format = sprinter_text0_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_JSON:
+   ctx-format = sprinter_json_create (config, stdout);
+   break;
+case NOTMUCH_FORMAT_SEXP:
+   ctx-format = sprinter_sexp_create (config, stdout);
+   break;
+default:
+   /* this should never happen */
+   INTERNAL_ERROR(no output format selected);
+}
+
+notmuch_exit_if_unsupported_format ();
+
+if (notmuch_database_open (notmuch_config_get_database_path (config),
+  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch))
+   return EXIT_FAILURE;
+
+query_str = query_string_from_args (ctx-notmuch, argc, argv);
+if (query_str == NULL) {
+   fprintf (stderr, Out of memory.\n);
+   return EXIT_FAILURE;
+}
+if (*query_str == '\0') {
+   fprintf (stderr, Error: notmuch search requires at least one search 
term.\n);
+   return EXIT_FAILURE;
+}
+
+ctx-query = notmuch_query_create (ctx-notmuch, query_str);
+if (ctx-query == NULL) {
+   fprintf (stderr, Out of memory\n);
+   return EXIT_FAILURE;
+}
+
+notmuch_query_set_sort (ctx-query, ctx-sort);
+
+if (ctx-exclude == NOTMUCH_EXCLUDE_FLAG  ctx-output != OUTPUT_SUMMARY) 
{
+   /* If we are not doing summary output there is nowhere to
+* print the excluded flag so fall back on including the
+* excluded messages. */
+   fprintf (stderr, Warning: this output format cannot flag excluded 
messages.\n);
+   ctx-exclude = NOTMUCH_EXCLUDE_FALSE;
+}
+
+if (ctx-exclude != NOTMUCH_EXCLUDE_FALSE) {
+   const char **search_exclude_tags;
+   size_t search_exclude_tags_length;
+
+   search_exclude_tags = notmuch_config_get_search_exclude_tags
+   (config, search_exclude_tags_length);
+   for (i = 0; i  search_exclude_tags_length; i++)
+   notmuch_query_add_tag_exclude (ctx-query, search_exclude_tags[i]);
+   notmuch_query_set_omit_excluded (ctx-query, ctx-exclude);
+}
+
+return 0;
+}
+
+static void
+_notmuch_search_cleanup (search_context_t *ctx)
+{
+notmuch_query_destroy (ctx-query);
+notmuch_database_destroy (ctx-notmuch);
+
+talloc_free (ctx-format);
+}
+
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
@@ -484,9 +567,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
.dupe = -1,
 };
 search_context_t *ctx = search_context;
-char *query_str;
 int opt_index, ret;
-unsigned int i;
 
 notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, ctx-sort, sort, 's',
@@ -534,71 +615,10 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
 return EXIT_FAILURE;
 }
 
-switch (ctx-format_sel) {
-case NOTMUCH_FORMAT_TEXT:
-   ctx-format = sprinter_text_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_TEXT0:
-   if (ctx-output == OUTPUT_SUMMARY) {
-   fprintf (stderr, Error: --format=text0 is not compatible with 
--output=summary.\n);
-   return EXIT_FAILURE;
-   }
-   ctx-format = sprinter_text0_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_JSON:
-   ctx-format = sprinter_json_create (config, stdout);
-   break;
-case NOTMUCH_FORMAT_SEXP:
-   ctx-format = sprinter_sexp_create (config, stdout);
-   break;
-default:
-   /* this should never happen */
-   INTERNAL_ERROR(no output format selected);
-}
-
-notmuch_exit_if_unsupported_format ();
-
-if (notmuch_database_open (notmuch_config_get_database_path (config),
-  NOTMUCH_DATABASE_MODE_READ_ONLY, ctx-notmuch))
+if (_notmuch_search_prepare (ctx, config,
+argc - opt_index, argv + opt_index))
return EXIT_FAILURE;
 
-query_str = query_string_from_args (ctx-notmuch, argc-opt_index, 
argv+opt_index);
-if (query_str == NULL) {
-   

  1   2   3   4   5   6   7   8   9   >