Re: notmuch and "mute" -- useful to anyone?
[notmuch-archives.git] / 26 / 276fe8c31a4de8f429b08529cb6d86c5ec89aa
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
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -2.3\r
10 X-Spam-Level: \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
44 Precedence: list\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
55 \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
59 \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
62 introduced there.\r
63 \r
64 We use mailbox_t rather than InternetAddressMailbox because we will\r
65 need to extend it in a following commit.\r
66 \r
67 This code is based on a patch from Jani Nikula.\r
68 ---\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
74 \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
80             return\r
81             ;;\r
82         --output)\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
85             return\r
86             ;;\r
87         --sort)\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
93    _arguments -s : \\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
99  }\r
100  \r
101  _notmuch()\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
109  \r
110 -    ``--output=(summary|threads|messages|files|tags)``\r
111 +    ``--output=(summary|threads|messages|files|tags|sender|recipients)``\r
112  \r
113          **summary**\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
118  \r
119 +       **sender**\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
125 +\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
130 +\r
131 +       **recipients**\r
132 +            Like **sender** but for addresses from *To*, *Cc* and\r
133 +           *Bcc* headers.\r
134 +\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
138 +\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
152  } output_t;\r
153  \r
154 +#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)\r
155 +\r
156  typedef struct {\r
157      sprinter_t *format;\r
158      notmuch_query_t *query;\r
159 @@ -40,6 +44,11 @@ typedef struct {\r
160      int dupe;\r
161  } search_options_t;\r
162  \r
163 +typedef struct {\r
164 +    const char *name;\r
165 +    const char *addr;\r
166 +} mailbox_t;\r
167 +\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
172      return 0;\r
173  }\r
174  \r
175 +static void\r
176 +print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)\r
177 +{\r
178 +    const char *name = mailbox->name;\r
179 +    const char *addr = mailbox->addr;\r
180 +    sprinter_t *format = opt->format;\r
181 +\r
182 +    if (format->is_text_printer) {\r
183 +       char *mailbox_str;\r
184 +\r
185 +       if (name && *name)\r
186 +           mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);\r
187 +       else\r
188 +           mailbox_str = talloc_strdup (format, addr);\r
189 +\r
190 +       if (! mailbox_str) {\r
191 +           fprintf (stderr, "Error: out of memory\n");\r
192 +           return;\r
193 +       }\r
194 +       format->string (format, mailbox_str);\r
195 +       format->separator (format);\r
196 +\r
197 +       talloc_free (mailbox_str);\r
198 +    } else {\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
206 +    }\r
207 +}\r
208 +\r
209 +/* Print addresses from InternetAddressList.  */\r
210 +static void\r
211 +process_address_list (const search_options_t *opt, InternetAddressList *list)\r
212 +{\r
213 +    InternetAddress *address;\r
214 +    int i;\r
215 +\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
221 +\r
222 +           group = INTERNET_ADDRESS_GROUP (address);\r
223 +           group_list = internet_address_group_get_members (group);\r
224 +           if (group_list == NULL)\r
225 +               continue;\r
226 +\r
227 +           process_address_list (opt, group_list);\r
228 +       } else {\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
233 +           };\r
234 +\r
235 +           print_mailbox (opt, &mbx);\r
236 +       }\r
237 +    }\r
238 +}\r
239 +\r
240 +/* Print addresses from a message header.  */\r
241 +static void\r
242 +process_address_header (const search_options_t *opt, const char *value)\r
243 +{\r
244 +    InternetAddressList *list;\r
245 +\r
246 +    if (value == NULL)\r
247 +       return;\r
248 +\r
249 +    list = internet_address_list_parse_string (value);\r
250 +    if (list == NULL)\r
251 +       return;\r
252 +\r
253 +    process_address_list (opt, list);\r
254 +}\r
255 +\r
256  static int\r
257  do_search_messages (search_options_t *opt)\r
258  {\r
259 @@ -266,11 +356,29 @@ do_search_messages (search_options_t *opt)\r
260             \r
261             notmuch_filenames_destroy( filenames );\r
262  \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
269 +       } else {\r
270 +           if (opt->output & OUTPUT_SENDER) {\r
271 +               const char *addrs;\r
272 +\r
273 +               addrs = notmuch_message_get_header (message, "from");\r
274 +               process_address_header (opt, addrs);\r
275 +           }\r
276 +\r
277 +           if (opt->output & OUTPUT_RECIPIENTS) {\r
278 +               const char *hdrs[] = { "to", "cc", "bcc" };\r
279 +               const char *addrs;\r
280 +               size_t j;\r
281 +\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
285 +               }\r
286 +           }\r
287         }\r
288  \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
298                                   { 0, 0 } } },\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
309 -- \r
310 2.1.1\r
311 \r