1 Return-Path: <sojkam1@fel.cvut.cz>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 9C03E431FC2
\r
6 for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 17:00:01 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-2.3 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id WrXDYXUEyIl4 for <notmuch@notmuchmail.org>;
\r
16 Thu, 30 Oct 2014 16:59:57 -0700 (PDT)
\r
17 Received: from max.feld.cvut.cz (max.feld.cvut.cz [147.32.192.36])
\r
18 by olra.theworths.org (Postfix) with ESMTP id 0A4AF431FDD
\r
19 for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 16:59:46 -0700 (PDT)
\r
20 Received: from localhost (unknown [192.168.200.7])
\r
21 by max.feld.cvut.cz (Postfix) with ESMTP id 6F2105CCFF4
\r
22 for <notmuch@notmuchmail.org>; Fri, 31 Oct 2014 00:59:45 +0100 (CET)
\r
23 X-Virus-Scanned: IMAP STYX AMAVIS
\r
24 Received: from max.feld.cvut.cz ([192.168.200.1])
\r
25 by localhost (styx.feld.cvut.cz [192.168.200.7]) (amavisd-new,
\r
26 port 10044) with ESMTP id JMar2SnvSiS3 for <notmuch@notmuchmail.org>;
\r
27 Fri, 31 Oct 2014 00:59:40 +0100 (CET)
\r
28 Received: from imap.feld.cvut.cz (imap.feld.cvut.cz [147.32.192.34])
\r
29 by max.feld.cvut.cz (Postfix) with ESMTP id 9C7065CCFF9
\r
30 for <notmuch@notmuchmail.org>; Fri, 31 Oct 2014 00:59:40 +0100 (CET)
\r
31 Received: from wsh by steelpick.2x.cz with local (Exim 4.84)
\r
32 (envelope-from <sojkam1@fel.cvut.cz>)
\r
33 id 1Xjzcs-0005bS-RQ; Fri, 31 Oct 2014 00:59:34 +0100
\r
34 From: Michal Sojka <sojkam1@fel.cvut.cz>
\r
35 To: notmuch@notmuchmail.org
\r
36 Subject: [PATCH v5 4/7] cli: search: Add --output={sender,recipients}
\r
37 Date: Fri, 31 Oct 2014 00:59:30 +0100
\r
38 Message-Id: <1414713573-21461-5-git-send-email-sojkam1@fel.cvut.cz>
\r
39 X-Mailer: git-send-email 2.1.1
\r
40 In-Reply-To: <1414713573-21461-1-git-send-email-sojkam1@fel.cvut.cz>
\r
41 References: <1414713573-21461-1-git-send-email-sojkam1@fel.cvut.cz>
\r
42 X-BeenThere: notmuch@notmuchmail.org
\r
43 X-Mailman-Version: 2.1.13
\r
45 List-Id: "Use and development of the notmuch mail system."
\r
46 <notmuch.notmuchmail.org>
\r
47 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
48 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
49 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
50 List-Post: <mailto:notmuch@notmuchmail.org>
\r
51 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
52 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
53 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
54 X-List-Received-Date: Fri, 31 Oct 2014 00:00:01 -0000
\r
56 The new outputs allow printing senders, recipients or both of matching
\r
57 messages. To print both, the user can use --output=sender and
\r
58 --output=recipients simultaneously.
\r
60 Currently, the same address can appear multiple times in the output.
\r
61 The next commit will change this. For this reason, tests are
\r
64 We use mailbox_t rather than InternetAddressMailbox because we will
\r
65 need to extend it in a following commit.
\r
67 This code is based on a patch from Jani Nikula.
\r
69 completion/notmuch-completion.bash | 2 +-
\r
70 completion/notmuch-completion.zsh | 3 +-
\r
71 doc/man1/notmuch-search.rst | 22 ++++++-
\r
72 notmuch-search.c | 115 ++++++++++++++++++++++++++++++++++++-
\r
73 4 files changed, 137 insertions(+), 5 deletions(-)
\r
75 diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
\r
76 index 0571dc9..cfbd389 100644
\r
77 --- a/completion/notmuch-completion.bash
\r
78 +++ b/completion/notmuch-completion.bash
\r
79 @@ -294,7 +294,7 @@ _notmuch_search()
\r
83 - COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )
\r
84 + COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) )
\r
88 diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh
\r
89 index 67a9aba..3e52a00 100644
\r
90 --- a/completion/notmuch-completion.zsh
\r
91 +++ b/completion/notmuch-completion.zsh
\r
92 @@ -52,7 +52,8 @@ _notmuch_search()
\r
94 '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
\r
95 '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
\r
96 - '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))'
\r
97 + '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
\r
98 + '--output=[select what to output]:output:((summary threads messages files tags sender recipients))'
\r
102 diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
\r
103 index 90160f2..b6607c9 100644
\r
104 --- a/doc/man1/notmuch-search.rst
\r
105 +++ b/doc/man1/notmuch-search.rst
\r
106 @@ -35,7 +35,7 @@ Supported options for **search** include
\r
107 intended for programs that invoke **notmuch(1)** internally. If
\r
108 omitted, the latest supported version will be used.
\r
110 - ``--output=(summary|threads|messages|files|tags)``
\r
111 + ``--output=(summary|threads|messages|files|tags|sender|recipients)``
\r
114 Output a summary of each thread with any message matching
\r
115 @@ -78,6 +78,26 @@ Supported options for **search** include
\r
116 by null characters (--format=text0), as a JSON array
\r
117 (--format=json), or as an S-Expression list (--format=sexp).
\r
120 + Output all addresses from the *From* header that appear on
\r
121 + any message matching the search terms, either one per line
\r
122 + (--format=text), separated by null characters
\r
123 + (--format=text0), as a JSON array (--format=json), or as
\r
124 + an S-Expression list (--format=sexp).
\r
126 + Note: Searching for **sender** should be much faster than
\r
127 + searching for **recipients**, because sender addresses are
\r
128 + cached directly in the database whereas other addresses
\r
129 + need to be fetched from message files.
\r
132 + Like **sender** but for addresses from *To*, *Cc* and
\r
135 + This option can be given multiple times to combine different
\r
136 + outputs. Currently, this is only supported for **sender** and
\r
137 + **recipients** outputs.
\r
139 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
\r
140 This option can be used to present results in either
\r
141 chronological order (**oldest-first**) or reverse chronological
\r
142 diff --git a/notmuch-search.c b/notmuch-search.c
\r
143 index ce46877..31e4221 100644
\r
144 --- a/notmuch-search.c
\r
145 +++ b/notmuch-search.c
\r
146 @@ -28,8 +28,12 @@ typedef enum {
\r
147 OUTPUT_MESSAGES = 1 << 2,
\r
148 OUTPUT_FILES = 1 << 3,
\r
149 OUTPUT_TAGS = 1 << 4,
\r
150 + OUTPUT_SENDER = 1 << 5,
\r
151 + OUTPUT_RECIPIENTS = 1 << 6,
\r
154 +#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
\r
157 sprinter_t *format;
\r
158 notmuch_query_t *query;
\r
159 @@ -40,6 +44,11 @@ typedef struct {
\r
161 } search_options_t;
\r
164 + const char *name;
\r
165 + const char *addr;
\r
168 /* Return two stable query strings that identify exactly the matched
\r
169 * and unmatched messages currently in thread. If there are no
\r
170 * matched or unmatched messages, the returned buffers will be
\r
171 @@ -220,6 +229,87 @@ do_search_threads (search_options_t *opt)
\r
176 +print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
\r
178 + const char *name = mailbox->name;
\r
179 + const char *addr = mailbox->addr;
\r
180 + sprinter_t *format = opt->format;
\r
182 + if (format->is_text_printer) {
\r
183 + char *mailbox_str;
\r
185 + if (name && *name)
\r
186 + mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
\r
188 + mailbox_str = talloc_strdup (format, addr);
\r
190 + if (! mailbox_str) {
\r
191 + fprintf (stderr, "Error: out of memory\n");
\r
194 + format->string (format, mailbox_str);
\r
195 + format->separator (format);
\r
197 + talloc_free (mailbox_str);
\r
199 + format->begin_map (format);
\r
200 + format->map_key (format, "name");
\r
201 + format->string (format, name);
\r
202 + format->map_key (format, "address");
\r
203 + format->string (format, addr);
\r
204 + format->end (format);
\r
205 + format->separator (format);
\r
209 +/* Print addresses from InternetAddressList. */
\r
211 +process_address_list (const search_options_t *opt, InternetAddressList *list)
\r
213 + InternetAddress *address;
\r
216 + for (i = 0; i < internet_address_list_length (list); i++) {
\r
217 + address = internet_address_list_get_address (list, i);
\r
218 + if (INTERNET_ADDRESS_IS_GROUP (address)) {
\r
219 + InternetAddressGroup *group;
\r
220 + InternetAddressList *group_list;
\r
222 + group = INTERNET_ADDRESS_GROUP (address);
\r
223 + group_list = internet_address_group_get_members (group);
\r
224 + if (group_list == NULL)
\r
227 + process_address_list (opt, group_list);
\r
229 + InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);
\r
230 + mailbox_t mbx = {
\r
231 + .name = internet_address_get_name (address),
\r
232 + .addr = internet_address_mailbox_get_addr (mailbox),
\r
235 + print_mailbox (opt, &mbx);
\r
240 +/* Print addresses from a message header. */
\r
242 +process_address_header (const search_options_t *opt, const char *value)
\r
244 + InternetAddressList *list;
\r
246 + if (value == NULL)
\r
249 + list = internet_address_list_parse_string (value);
\r
250 + if (list == NULL)
\r
253 + process_address_list (opt, list);
\r
257 do_search_messages (search_options_t *opt)
\r
259 @@ -266,11 +356,29 @@ do_search_messages (search_options_t *opt)
\r
261 notmuch_filenames_destroy( filenames );
\r
263 - } else { /* output == OUTPUT_MESSAGES */
\r
264 + } else if (opt->output == OUTPUT_MESSAGES) {
\r
265 format->set_prefix (format, "id");
\r
266 format->string (format,
\r
267 notmuch_message_get_message_id (message));
\r
268 format->separator (format);
\r
270 + if (opt->output & OUTPUT_SENDER) {
\r
271 + const char *addrs;
\r
273 + addrs = notmuch_message_get_header (message, "from");
\r
274 + process_address_header (opt, addrs);
\r
277 + if (opt->output & OUTPUT_RECIPIENTS) {
\r
278 + const char *hdrs[] = { "to", "cc", "bcc" };
\r
279 + const char *addrs;
\r
282 + for (j = 0; j < ARRAY_SIZE (hdrs); j++) {
\r
283 + addrs = notmuch_message_get_header (message, hdrs[j]);
\r
284 + process_address_header (opt, addrs);
\r
289 notmuch_message_destroy (message);
\r
290 @@ -371,6 +479,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
\r
291 (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
\r
292 { "threads", OUTPUT_THREADS },
\r
293 { "messages", OUTPUT_MESSAGES },
\r
294 + { "sender", OUTPUT_SENDER },
\r
295 + { "recipients", OUTPUT_RECIPIENTS },
\r
296 { "files", OUTPUT_FILES },
\r
297 { "tags", OUTPUT_TAGS },
\r
299 @@ -462,7 +572,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
\r
300 opt.output == OUTPUT_THREADS)
\r
301 ret = do_search_threads (&opt);
\r
302 else if (opt.output == OUTPUT_MESSAGES ||
\r
303 - opt.output == OUTPUT_FILES)
\r
304 + opt.output == OUTPUT_FILES ||
\r
305 + (opt.output & OUTPUT_ADDRESS_FLAGS && !(opt.output & ~OUTPUT_ADDRESS_FLAGS)))
\r
306 ret = do_search_messages (&opt);
\r
307 else if (opt.output == OUTPUT_TAGS)
\r
308 ret = do_search_tags (notmuch, &opt);
\r