Re: Applying patches directly from emails?
[notmuch-archives.git] / 28 / e0cecbf67889850074408b3fc2c2cc4bbf4334
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 F3627431E84\r
6         for <notmuch@notmuchmail.org>; Mon, 27 Oct 2014 07:51:18 -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 wOZjTa-O--B1 for <notmuch@notmuchmail.org>;\r
16         Mon, 27 Oct 2014 07:51:14 -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 6071F431FC3\r
19         for <notmuch@notmuchmail.org>; Mon, 27 Oct 2014 07:51:14 -0700 (PDT)\r
20 Received: from localhost (unknown [192.168.200.7])\r
21         by max.feld.cvut.cz (Postfix) with ESMTP id 7F6625CCF9F\r
22         for <notmuch@notmuchmail.org>; Mon, 27 Oct 2014 15:51:13 +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 dZ6NSsgk-BVZ for <notmuch@notmuchmail.org>;\r
27         Mon, 27 Oct 2014 15:51:08 +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 9A5225CCF93\r
30         for <notmuch@notmuchmail.org>; Mon, 27 Oct 2014 15:51:07 +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 1XildT-0000om-Ic; Mon, 27 Oct 2014 15:51:07 +0100\r
34 From: Michal Sojka <sojkam1@fel.cvut.cz>\r
35 To: notmuch@notmuchmail.org\r
36 Subject: [PATCH v4 4/6] cli: search: Add --output={sender,recipients}\r
37 Date: Mon, 27 Oct 2014 15:50:53 +0100\r
38 Message-Id: <1414421455-3037-5-git-send-email-sojkam1@fel.cvut.cz>\r
39 X-Mailer: git-send-email 2.1.1\r
40 In-Reply-To: <1414421455-3037-1-git-send-email-sojkam1@fel.cvut.cz>\r
41 References: <1414421455-3037-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: Mon, 27 Oct 2014 14:51:19 -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. The\r
61 next patch will change this. For this reason, the test are introduced in\r
62 the next patch as well.\r
63 \r
64 We use mailbox_t rather than InternetAddressMailbox because we will need\r
65 to extend it in a following patch.\r
66 \r
67 This code 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                   | 112 ++++++++++++++++++++++++++++++++++++-\r
73  4 files changed, 134 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..ce3bfb2 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,84 @@ 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 +\r
181 +    if (opt->format->is_text_printer) {\r
182 +       char *mailbox_str;\r
183 +\r
184 +       if (name && *name)\r
185 +           mailbox_str = talloc_asprintf (opt->format, "%s <%s>", name, addr);\r
186 +       else\r
187 +           mailbox_str = talloc_strdup (opt->format, addr);\r
188 +\r
189 +       if (! mailbox_str) {\r
190 +           fprintf (stderr, "Error: out of memory\n");\r
191 +           return;\r
192 +       }\r
193 +       opt->format->string (opt->format, mailbox_str);\r
194 +       opt->format->separator (opt->format);\r
195 +\r
196 +       talloc_free (mailbox_str);\r
197 +    } else {\r
198 +       opt->format->begin_map (opt->format);\r
199 +       opt->format->map_key (opt->format, "name");\r
200 +       opt->format->string (opt->format, name);\r
201 +       opt->format->map_key (opt->format, "address");\r
202 +       opt->format->string (opt->format, addr);\r
203 +       opt->format->end (opt->format);\r
204 +       opt->format->separator (opt->format);\r
205 +    }\r
206 +}\r
207 +\r
208 +static void\r
209 +process_address_list (const search_options_t *opt, InternetAddressList *list)\r
210 +{\r
211 +    InternetAddress *address;\r
212 +    int i;\r
213 +\r
214 +    for (i = 0; i < internet_address_list_length (list); i++) {\r
215 +       address = internet_address_list_get_address (list, i);\r
216 +       if (INTERNET_ADDRESS_IS_GROUP (address)) {\r
217 +           InternetAddressGroup *group;\r
218 +           InternetAddressList *group_list;\r
219 +\r
220 +           group = INTERNET_ADDRESS_GROUP (address);\r
221 +           group_list = internet_address_group_get_members (group);\r
222 +           if (group_list == NULL)\r
223 +               continue;\r
224 +\r
225 +           process_address_list (opt, group_list);\r
226 +       } else {\r
227 +           InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);\r
228 +           mailbox_t mbx = {\r
229 +               .name = internet_address_get_name (address),\r
230 +               .addr = internet_address_mailbox_get_addr (mailbox),\r
231 +           };\r
232 +\r
233 +           print_mailbox (opt, &mbx);\r
234 +       }\r
235 +    }\r
236 +}\r
237 +\r
238 +static void\r
239 +process_address_header (const search_options_t *opt, const char *value)\r
240 +{\r
241 +    InternetAddressList *list;\r
242 +\r
243 +    if (value == NULL)\r
244 +       return;\r
245 +\r
246 +    list = internet_address_list_parse_string (value);\r
247 +    if (list == NULL)\r
248 +       return;\r
249 +\r
250 +    process_address_list (opt, list);\r
251 +}\r
252 +\r
253  static int\r
254  do_search_messages (search_options_t *opt)\r
255  {\r
256 @@ -266,11 +353,29 @@ do_search_messages (search_options_t *opt)\r
257             \r
258             notmuch_filenames_destroy( filenames );\r
259  \r
260 -       } else { /* output == OUTPUT_MESSAGES */\r
261 +       } else if (opt->output == OUTPUT_MESSAGES) {\r
262             format->set_prefix (format, "id");\r
263             format->string (format,\r
264                             notmuch_message_get_message_id (message));\r
265             format->separator (format);\r
266 +       } else {\r
267 +           if (opt->output & OUTPUT_SENDER) {\r
268 +               const char *addrs;\r
269 +\r
270 +               addrs = notmuch_message_get_header (message, "from");\r
271 +               process_address_header (opt, addrs);\r
272 +           }\r
273 +\r
274 +           if (opt->output & OUTPUT_RECIPIENTS) {\r
275 +               const char *hdrs[] = { "to", "cc", "bcc" };\r
276 +               const char *addrs;\r
277 +               size_t j;\r
278 +\r
279 +               for (j = 0; j < ARRAY_SIZE (hdrs); j++) {\r
280 +                   addrs = notmuch_message_get_header (message, hdrs[j]);\r
281 +                   process_address_header (opt, addrs);\r
282 +               }\r
283 +           }\r
284         }\r
285  \r
286         notmuch_message_destroy (message);\r
287 @@ -371,6 +476,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
288           (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },\r
289                                   { "threads", OUTPUT_THREADS },\r
290                                   { "messages", OUTPUT_MESSAGES },\r
291 +                                 { "sender", OUTPUT_SENDER },\r
292 +                                 { "recipients", OUTPUT_RECIPIENTS },\r
293                                   { "files", OUTPUT_FILES },\r
294                                   { "tags", OUTPUT_TAGS },\r
295                                   { 0, 0 } } },\r
296 @@ -462,7 +569,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
297         opt.output == OUTPUT_THREADS)\r
298         ret = do_search_threads (&opt);\r
299      else if (opt.output == OUTPUT_MESSAGES ||\r
300 -            opt.output == OUTPUT_FILES)\r
301 +            opt.output == OUTPUT_FILES ||\r
302 +            (opt.output & OUTPUT_ADDRESS_FLAGS && !(opt.output & ~OUTPUT_ADDRESS_FLAGS)))\r
303         ret = do_search_messages (&opt);\r
304      else if (opt.output == OUTPUT_TAGS)\r
305         ret = do_search_tags (notmuch, &opt);\r
306 -- \r
307 2.1.1\r
308 \r