From 6764dd0d79f63c78bc00afe7e236d9229f8ae221 Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Fri, 24 Oct 2014 12:57:16 +0200 Subject: [PATCH] Re: [PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients} --- 42/400b4129e44b714ca4b9b2f9494af94bbce106 | 366 ++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 42/400b4129e44b714ca4b9b2f9494af94bbce106 diff --git a/42/400b4129e44b714ca4b9b2f9494af94bbce106 b/42/400b4129e44b714ca4b9b2f9494af94bbce106 new file mode 100644 index 000000000..e59fa7681 --- /dev/null +++ b/42/400b4129e44b714ca4b9b2f9494af94bbce106 @@ -0,0 +1,366 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id A0849431FDD + for ; Fri, 24 Oct 2014 03:57:30 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -2.3 +X-Spam-Level: +X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id ftc+qzG-5rJh for ; + Fri, 24 Oct 2014 03:57:23 -0700 (PDT) +Received: from max.feld.cvut.cz (max.feld.cvut.cz [147.32.192.36]) + by olra.theworths.org (Postfix) with ESMTP id 11DC9431FBD + for ; Fri, 24 Oct 2014 03:57:23 -0700 (PDT) +Received: from localhost (unknown [192.168.200.7]) + by max.feld.cvut.cz (Postfix) with ESMTP id B8B3B3CFEE6; + Fri, 24 Oct 2014 12:57:21 +0200 (CEST) +X-Virus-Scanned: IMAP STYX AMAVIS +Received: from max.feld.cvut.cz ([192.168.200.1]) + by localhost (styx.feld.cvut.cz [192.168.200.7]) (amavisd-new, + port 10044) + with ESMTP id 1IznQ5zjB6qN; Fri, 24 Oct 2014 12:57:16 +0200 (CEST) +Received: from imap.feld.cvut.cz (imap.feld.cvut.cz [147.32.192.34]) + by max.feld.cvut.cz (Postfix) with ESMTP id CE5325CCF00; + Fri, 24 Oct 2014 12:57:16 +0200 (CEST) +Received: from wsh by steelpick.2x.cz with local (Exim 4.84) + (envelope-from ) + id 1XhcYW-0004pj-PB; Fri, 24 Oct 2014 12:57:16 +0200 +From: Michal Sojka +To: Mark Walters , notmuch@notmuchmail.org +Subject: Re: [PATCH v3 3/4] cli: Extend the search command for + --output={sender, recipients} +In-Reply-To: <87mw8obcob.fsf@qmul.ac.uk> +References: <87zjd51phx.fsf@steelpick.2x.cz> + <1413150093-8383-1-git-send-email-sojkam1@fel.cvut.cz> + <1413150093-8383-4-git-send-email-sojkam1@fel.cvut.cz> + <87mw8obcob.fsf@qmul.ac.uk> +User-Agent: Notmuch/0.18.1+101~g56b0ff0 (http://notmuchmail.org) Emacs/24.3.1 + (x86_64-pc-linux-gnu) +Date: Fri, 24 Oct 2014 12:57:16 +0200 +Message-ID: <87fvedag6r.fsf@steelpick.2x.cz> +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Fri, 24 Oct 2014 10:57:30 -0000 + +Hi Mark, + +I mostly agree with your points mentioned in this and other your emails. +I'll prepare v4 based on that. + +On Wed, Oct 22 2014, Mark Walters wrote: +> On Sun, 12 Oct 2014, Michal Sojka wrote: +>> The new outputs allow printing senders, recipients or both of matching +>> messages. The --output option is converted from "keyword" argument to +>> "flags" argument, which means that the user can use --output=3Dsender and +>> --output=3Drecipients simultaneously, to print both. Other combinations +>> produce an error. +>> +>> This code based on a patch from Jani Nikula. +>> --- +>> completion/notmuch-completion.bash | 2 +- +>> completion/notmuch-completion.zsh | 3 +- +>> doc/man1/notmuch-search.rst | 22 +++++++- +>> notmuch-search.c | 110 ++++++++++++++++++++++++++++++= +++++--- +>> test/T090-search-output.sh | 64 +++++++++++++++++++++ +>> 5 files changed, 189 insertions(+), 12 deletions(-) +>> +>> diff --git a/completion/notmuch-completion.bash b/completion/notmuch-com= +pletion.bash +>> index 0571dc9..cfbd389 100644 +>> --- a/completion/notmuch-completion.bash +>> +++ b/completion/notmuch-completion.bash +>> @@ -294,7 +294,7 @@ _notmuch_search() +>> return +>> ;; +>> --output) +>> - COMPREPLY=3D( $( compgen -W "summary threads messages files tags" = +-- "${cur}" ) ) +>> + COMPREPLY=3D( $( compgen -W "summary threads messages files tags s= +ender recipients" -- "${cur}" ) ) +>> return +>> ;; +>> --sort) +>> diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-comp= +letion.zsh +>> index 67a9aba..3e52a00 100644 +>> --- a/completion/notmuch-completion.zsh +>> +++ b/completion/notmuch-completion.zsh +>> @@ -52,7 +52,8 @@ _notmuch_search() +>> _arguments -s : \ +>> '--max-threads=3D[display only the first x threads from the search = +results]:number of threads to show: ' \ +>> '--first=3D[omit the first x threads from the search results]:numbe= +r of threads to omit: ' \ +>> - '--sort=3D[sort results]:sorting:((newest-first\:"reverse chronolog= +ical order" oldest-first\:"chronological order"))' +>> + '--sort=3D[sort results]:sorting:((newest-first\:"reverse chronolog= +ical order" oldest-first\:"chronological order"))' \ +>> + '--output=3D[select what to output]:output:((summary threads messag= +es files tags sender recipients))' +>> } +>> +>> _notmuch() +>> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst +>> index 90160f2..c9d38b1 100644 +>> --- a/doc/man1/notmuch-search.rst +>> +++ b/doc/man1/notmuch-search.rst +>> @@ -35,7 +35,7 @@ Supported options for **search** include +>> intended for programs that invoke **notmuch(1)** internally. If +>> omitted, the latest supported version will be used. +>> +>> - ``--output=3D(summary|threads|messages|files|tags)`` +>> + ``--output=3D(summary|threads|messages|files|tags|sender|recipients= +)`` +>> +>> **summary** +>> Output a summary of each thread with any message matching +>> @@ -78,6 +78,26 @@ Supported options for **search** include +>> by null characters (--format=3Dtext0), as a JSON array +>> (--format=3Djson), or as an S-Expression list (--format=3Ds= +exp). +>> +>> + **sender** +>> + Output all addresses from the *From* header that appear on +>> + any message matching the search terms, either one per line +>> + (--format=3Dtext), separated by null characters +>> + (--format=3Dtext0), as a JSON array (--format=3Djson), or as +>> + an S-Expression list (--format=3Dsexp). +>> + +>> + Note: Searching for **sender** should be much faster than +>> + searching for **recipients**, because sender addresses are +>> + cached directly in the database whereas other addresses +>> + need to be fetched from message files. +>> + +>> + **recipients** +>> + Like **sender** but for addresses from *To*, *Cc* and +>> + *Bcc* headers. +>> + +>> + This option can be given multiple times to combine different +>> + outputs. Curently, this is only supported for **sender** and +>> + **recipients** outputs. +>> + +>> ``--sort=3D``\ (**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 5ac2a26..74588f8 100644 +>> --- a/notmuch-search.c +>> +++ b/notmuch-search.c +>> @@ -23,11 +23,14 @@ +>> #include "string-util.h" +>> +>> typedef enum { +>> - OUTPUT_SUMMARY, +>> - OUTPUT_THREADS, +>> - OUTPUT_MESSAGES, +>> - OUTPUT_FILES, +>> - OUTPUT_TAGS +>> + OUTPUT_SUMMARY =3D 1 << 0, +>> + OUTPUT_THREADS =3D 1 << 1, +>> + OUTPUT_MESSAGES =3D 1 << 2, +>> + OUTPUT_FILES =3D 1 << 3, +>> + OUTPUT_TAGS =3D 1 << 4, +>> + OUTPUT_SENDER =3D 1 << 5, +>> + OUTPUT_RECIPIENTS =3D 1 << 6, +>> + OUTPUT_ADDRESSES =3D OUTPUT_SENDER | OUTPUT_RECIPIENTS, +> +> I think I would drop the OUTPUT_ADDRESSES enum as the parser no longer +> uses it (and replace the one use by OUTPUT_SENDER | OUTPUT_RECIPIENTS +> below). + +As mentioned elsewhere, this is required to suppress the following warning. + +notmuch-search.c:634:5: warning: case value =E2=80=9896=E2=80=99 not in enu= +merated type =E2=80=98output_t=E2=80=99 [-Wswitch] + +> +>> } output_t; +>> +>> typedef struct { +>> @@ -220,6 +223,67 @@ do_search_threads (search_options_t *o) +>> return 0; +>> } +>> +>> +static void +>> +print_address_list (const search_options_t *o, InternetAddressList *lis= +t) +>> +{ +>> + InternetAddress *address; +>> + int i; +>> + +>> + for (i =3D 0; i < internet_address_list_length (list); i++) { +>> + address =3D internet_address_list_get_address (list, i); +>> + if (INTERNET_ADDRESS_IS_GROUP (address)) { +>> + InternetAddressGroup *group; +>> + InternetAddressList *group_list; +>> + +>> + group =3D INTERNET_ADDRESS_GROUP (address); +>> + group_list =3D internet_address_group_get_members (group); +>> + if (group_list =3D=3D NULL) +>> + continue; +>> + +>> + print_address_list (o, group_list); +>> + } else { +>> + InternetAddressMailbox *mailbox; +>> + const char *name; +>> + const char *addr; +>> + char *full_address; +>> + +>> + mailbox =3D INTERNET_ADDRESS_MAILBOX (address); +>> + +>> + name =3D internet_address_get_name (address); +>> + addr =3D internet_address_mailbox_get_addr (mailbox); +>> + +>> + if (name && *name) +>> + full_address =3D talloc_asprintf (o->format, "%s <%s>", name, addr); +>> + else +>> + full_address =3D talloc_strdup (o->format, addr); +>> + +>> + if (!full_address) { +>> + fprintf (stderr, "Error: out of memory\n"); +>> + break; +>> + } +>> + o->format->string (o->format, full_address); +>> + o->format->separator (o->format); +>> + +>> + talloc_free (full_address); +>> + } +>> + } +>> +} +>> + +>> +static void +>> +print_address_string (const search_options_t *o, const char *recipients) +>> +{ +>> + InternetAddressList *list; +>> + +>> + if (recipients =3D=3D NULL) +>> + return; +>> + +>> + list =3D internet_address_list_parse_string (recipients); +>> + if (list =3D=3D NULL) +>> + return; +>> + +>> + print_address_list (o, list); +>> +} +>> + +>> static int +>> do_search_messages (search_options_t *o) +>> { +>> @@ -266,11 +330,29 @@ do_search_messages (search_options_t *o) +>> +>> notmuch_filenames_destroy( filenames ); +>> +>> - } else { /* output =3D=3D OUTPUT_MESSAGES */ +>> + } else if (o->output =3D=3D OUTPUT_MESSAGES) { +>> format->set_prefix (format, "id"); +>> format->string (format, +>> notmuch_message_get_message_id (message)); +>> format->separator (format); +>> + } else { +>> + if (o->output & OUTPUT_SENDER) { +>> + const char *addrs; +>> + +>> + addrs =3D notmuch_message_get_header (message, "from"); +>> + print_address_string (o, addrs); +>> + } +>> + +>> + if (o->output & OUTPUT_RECIPIENTS) { +>> + const char *hdrs[] =3D { "to", "cc", "bcc" }; +>> + const char *addrs; +>> + size_t j; +>> + +>> + for (j =3D 0; j < ARRAY_SIZE (hdrs); j++) { +>> + addrs =3D notmuch_message_get_header (message, hdrs[j]); +>> + print_address_string (o, addrs); +>> + } +>> + } +>> } +>> +>> notmuch_message_destroy (message); +>> @@ -337,7 +419,7 @@ notmuch_search_command (notmuch_config_t *config, in= +t argc, char *argv[]) +>> notmuch_database_t *notmuch; +>> search_options_t o =3D { +>> .sort =3D NOTMUCH_SORT_NEWEST_FIRST, +>> - .output =3D OUTPUT_SUMMARY, +>> + .output =3D 0, +>> .offset =3D 0, +>> .limit =3D -1, /* unlimited */ +>> .dupe =3D -1, +> +> It is slightly unfortunate that all the other defaults are defined here +> but the default output is after the option parsing but I can't see a +> nice way round that. I only mention it in case you (or anyone else) can +> see a nice way round this. + +Solution would to have --output as NOTMUCH_OPT_KEYWORD rather than +NOTMUCH_OPT_KEYWORD_FLAGS as it was in v2. + +> +>> @@ -366,10 +448,12 @@ notmuch_search_command (notmuch_config_t *config, = +int argc, char *argv[]) +>> { "text0", NOTMUCH_FORMAT_TEXT0 }, +>> { 0, 0 } } }, +>> { NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 }, +>> - { NOTMUCH_OPT_KEYWORD, &o.output, "output", 'o', +>> + { NOTMUCH_OPT_KEYWORD_FLAGS, &o.output, "output", 'o', +>> (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, +>> { "threads", OUTPUT_THREADS }, +>> { "messages", OUTPUT_MESSAGES }, +>> + { "sender", OUTPUT_SENDER }, +>> + { "recipients", OUTPUT_RECIPIENTS }, +>> { "files", OUTPUT_FILES }, +>> { "tags", OUTPUT_TAGS }, +>> { 0, 0 } } }, +>> @@ -389,6 +473,9 @@ notmuch_search_command (notmuch_config_t *config, in= +t argc, char *argv[]) +>> if (opt_index < 0) +>> return EXIT_FAILURE; +>> +>> + if (! o.output) +>> + o.output =3D OUTPUT_SUMMARY; +>> + +>> switch (format_sel) { +>> case NOTMUCH_FORMAT_TEXT: +>> o.format =3D sprinter_text_create (config, stdout); +>> @@ -455,18 +542,23 @@ notmuch_search_command (notmuch_config_t *config, = +int argc, char *argv[]) +>> } +>> +>> switch (o.output) { +>> - default: +>> case OUTPUT_SUMMARY: +>> case OUTPUT_THREADS: +>> ret =3D do_search_threads (&o); +>> break; +>> case OUTPUT_MESSAGES: +>> + case OUTPUT_SENDER: +>> + case OUTPUT_RECIPIENTS: +>> + case OUTPUT_ADDRESSES: +> +> This is the one use of OUTPUT_ADDRESSES that I would replace with +> OUTPUT_SENDER | OUTPUT_RECIPIENTS + +See above. + +-Michal -- 2.26.2