Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 55 / 053ebbc3187c3646040b751671a0d996b2359b
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 2677C431FDF\r
6         for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 17:00:12 -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 NcJ1EEB2t2CV for <notmuch@notmuchmail.org>;\r
16         Thu, 30 Oct 2014 17:00:06 -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 0192A431E84\r
19         for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 16:59:49 -0700 (PDT)\r
20 Received: from localhost (unknown [192.168.200.7])\r
21         by max.feld.cvut.cz (Postfix) with ESMTP id 654CB5CCFF6\r
22         for <notmuch@notmuchmail.org>; Fri, 31 Oct 2014 00:59:48 +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 BU1HWu43zUQ7 for <notmuch@notmuchmail.org>;\r
27         Fri, 31 Oct 2014 00:59:44 +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 2E5B35CCFFC\r
30         for <notmuch@notmuchmail.org>; Fri, 31 Oct 2014 00:59:41 +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-0005bg-Th; 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 7/7] cli: search: Add --filter-by option to configure\r
37         address filtering\r
38 Date: Fri, 31 Oct 2014 00:59:33 +0100\r
39 Message-Id: <1414713573-21461-8-git-send-email-sojkam1@fel.cvut.cz>\r
40 X-Mailer: git-send-email 2.1.1\r
41 In-Reply-To: <1414713573-21461-1-git-send-email-sojkam1@fel.cvut.cz>\r
42 References: <1414713573-21461-1-git-send-email-sojkam1@fel.cvut.cz>\r
43 X-BeenThere: notmuch@notmuchmail.org\r
44 X-Mailman-Version: 2.1.13\r
45 Precedence: list\r
46 List-Id: "Use and development of the notmuch mail system."\r
47         <notmuch.notmuchmail.org>\r
48 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
49         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
50 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
51 List-Post: <mailto:notmuch@notmuchmail.org>\r
52 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
53 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
54         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
55 X-List-Received-Date: Fri, 31 Oct 2014 00:00:12 -0000\r
56 \r
57 This option allows to configure the criterion for duplicate address\r
58 filtering. Without this option, all unique combinations of name and\r
59 address parts are printed. This option allows to filter the output\r
60 more, for example to only contain unique address parts.\r
61 ---\r
62  completion/notmuch-completion.bash |  6 +++-\r
63  completion/notmuch-completion.zsh  |  3 +-\r
64  doc/man1/notmuch-search.rst        | 39 ++++++++++++++++++++++-\r
65  notmuch-search.c                   | 51 ++++++++++++++++++++++++++++--\r
66  test/T095-search-filter-by.sh      | 64 ++++++++++++++++++++++++++++++++++++++\r
67  5 files changed, 158 insertions(+), 5 deletions(-)\r
68  create mode 100755 test/T095-search-filter-by.sh\r
69 \r
70 diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash\r
71 index 39cd829..b625b02 100644\r
72 --- a/completion/notmuch-completion.bash\r
73 +++ b/completion/notmuch-completion.bash\r
74 @@ -305,12 +305,16 @@ _notmuch_search()\r
75             COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )\r
76             return\r
77             ;;\r
78 +       --filter-by)\r
79 +           COMPREPLY=( $( compgen -W "nameaddr name addr addrfold nameaddrfold" -- "${cur}" ) )\r
80 +           return\r
81 +           ;;\r
82      esac\r
83  \r
84      ! $split &&\r
85      case "${cur}" in\r
86         -*)\r
87 -           local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate="\r
88 +           local options="--format= --output= --sort= --offset= --limit= --exclude= --duplicate= --filter-by="\r
89             compopt -o nospace\r
90             COMPREPLY=( $(compgen -W "$options" -- ${cur}) )\r
91             ;;\r
92 diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh\r
93 index d7e5a5e..c1ccc32 100644\r
94 --- a/completion/notmuch-completion.zsh\r
95 +++ b/completion/notmuch-completion.zsh\r
96 @@ -53,7 +53,8 @@ _notmuch_search()\r
97      '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \\r
98      '--first=[omit the first x threads from the search results]:number of threads to omit: ' \\r
99      '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \\r
100 -    '--output=[select what to output]:output:((summary threads messages files tags sender recipients count))'\r
101 +    '--output=[select what to output]:output:((summary threads messages files tags sender recipients count))' \\r
102 +    '--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"))'\r
103  }\r
104  \r
105  _notmuch()\r
106 diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst\r
107 index ec89200..3a5556b 100644\r
108 --- a/doc/man1/notmuch-search.rst\r
109 +++ b/doc/man1/notmuch-search.rst\r
110 @@ -85,7 +85,8 @@ Supported options for **search** include\r
111              (--format=text0), as a JSON array (--format=json), or as\r
112              an S-Expression list (--format=sexp).\r
113  \r
114 -            Duplicate addresses are filtered out.\r
115 +            Duplicate addresses are filtered out. Filtering can be\r
116 +            configured with the --filter-by option.\r
117  \r
118             Note: Searching for **sender** should be much faster than\r
119             searching for **recipients**, because sender addresses are\r
120 @@ -158,6 +159,42 @@ Supported options for **search** include\r
121          prefix. The prefix matches messages based on filenames. This\r
122          option filters filenames of the matching messages.\r
123  \r
124 +    ``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ **addrfold**\ \|\ **nameaddrfold**\)\r
125 +\r
126 +       Can be used with ``--output=sender`` or\r
127 +       ``--output=recipients`` to filter out duplicate addresses. The\r
128 +       filtering algorithm receives a sequence of email addresses and\r
129 +       outputs the same sequence without the addresses that are\r
130 +       considered a duplicate of a previously output address. What is\r
131 +       considered a duplicate depends on how the two addresses are\r
132 +       compared and this can be controlled with the following\r
133 +       keywords:\r
134 +\r
135 +       **nameaddr** means that both name and address parts are\r
136 +       compared in case-sensitive manner. Therefore, all same looking\r
137 +       addresses strings are considered duplicate. This is the\r
138 +       default.\r
139 +\r
140 +       **name** means that only the name part is compared (in\r
141 +       case-sensitive manner). For example, the addresses "John Doe\r
142 +       <me@example.com>" and "John Doe <john@doe.name>" will be\r
143 +       considered duplicate.\r
144 +\r
145 +       **addr** means that only the address part is compared (in\r
146 +       case-sensitive manner). For example, the addresses "John Doe\r
147 +       <john@example.com>" and "Dr. John Doe <john@example.com>" will\r
148 +       be considered duplicate.\r
149 +\r
150 +       **addrfold** is like **addr**, but comparison is done in\r
151 +       canse-insensitive manner. For example, the addresses "John Doe\r
152 +       <john@example.com>" and "Dr. John Doe <JOHN@EXAMPLE.COM>" will\r
153 +       be considered duplicate.\r
154 +\r
155 +       **nameaddrfold** is like **nameaddr**, but address comparison\r
156 +       is done in canse-insensitive manner. For example, the\r
157 +       addresses "John Doe <john@example.com>" and "John Doe\r
158 +       <JOHN@EXAMPLE.COM>" will be considered duplicate.\r
159 +\r
160  EXIT STATUS\r
161  ===========\r
162  \r
163 diff --git a/notmuch-search.c b/notmuch-search.c\r
164 index 15527c4..8bc80d3 100644\r
165 --- a/notmuch-search.c\r
166 +++ b/notmuch-search.c\r
167 @@ -35,6 +35,14 @@ typedef enum {\r
168  \r
169  #define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS | OUTPUT_COUNT)\r
170  \r
171 +typedef enum {\r
172 +    FILTER_BY_NAMEADDR = 0,\r
173 +    FILTER_BY_NAME,\r
174 +    FILTER_BY_ADDR,\r
175 +    FILTER_BY_ADDRFOLD,\r
176 +    FILTER_BY_NAMEADDRFOLD,\r
177 +} filter_by_t;\r
178 +\r
179  typedef struct {\r
180      sprinter_t *format;\r
181      notmuch_query_t *query;\r
182 @@ -43,6 +51,7 @@ typedef struct {\r
183      int offset;\r
184      int limit;\r
185      int dupe;\r
186 +    filter_by_t filter_by;\r
187  } search_options_t;\r
188  \r
189  typedef struct {\r
190 @@ -231,7 +240,7 @@ do_search_threads (search_options_t *opt)\r
191      return 0;\r
192  }\r
193  \r
194 -/* Returns TRUE iff name and addr is duplicate. */\r
195 +/* Returns TRUE iff name and/or addr is considered duplicate. */\r
196  static notmuch_bool_t\r
197  is_duplicate (const search_options_t *opt, GHashTable *addrs, const char *name, const char *addr)\r
198  {\r
199 @@ -239,7 +248,32 @@ is_duplicate (const search_options_t *opt, GHashTable *addrs, const char *name,\r
200      char *key;\r
201      mailbox_t *mailbox;\r
202  \r
203 -    key = talloc_asprintf (opt->format, "%s <%s>", name, addr);\r
204 +    if (opt->filter_by == FILTER_BY_ADDRFOLD ||\r
205 +       opt->filter_by == FILTER_BY_NAMEADDRFOLD) {\r
206 +       gchar *folded = g_utf8_casefold (addr, -1);\r
207 +       addr = talloc_strdup (opt->format, folded);\r
208 +       g_free (folded);\r
209 +    }\r
210 +    switch (opt->filter_by) {\r
211 +    case FILTER_BY_NAMEADDR:\r
212 +    case FILTER_BY_NAMEADDRFOLD:\r
213 +       key = talloc_asprintf (opt->format, "%s <%s>", name, addr);\r
214 +       break;\r
215 +    case FILTER_BY_NAME:\r
216 +       key = talloc_strdup (opt->format, name); /* !name results in !key */\r
217 +       break;\r
218 +    case FILTER_BY_ADDR:\r
219 +    case FILTER_BY_ADDRFOLD:\r
220 +       key = talloc_strdup (opt->format, addr);\r
221 +       break;\r
222 +    default:\r
223 +       INTERNAL_ERROR("invalid --filter-by flags");\r
224 +    }\r
225 +\r
226 +    if (opt->filter_by == FILTER_BY_ADDRFOLD ||\r
227 +       opt->filter_by == FILTER_BY_NAMEADDRFOLD)\r
228 +       talloc_free ((char*)addr);\r
229 +\r
230      if (! key)\r
231         return FALSE;\r
232  \r
233 @@ -523,6 +557,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
234         .offset = 0,\r
235         .limit = -1, /* unlimited */\r
236         .dupe = -1,\r
237 +       .filter_by = FILTER_BY_NAMEADDR,\r
238      };\r
239      char *query_str;\r
240      int opt_index, ret;\r
241 @@ -567,6 +602,13 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
242         { NOTMUCH_OPT_INT, &opt.offset, "offset", 'O', 0 },\r
243         { NOTMUCH_OPT_INT, &opt.limit, "limit", 'L', 0  },\r
244         { NOTMUCH_OPT_INT, &opt.dupe, "duplicate", 'D', 0  },\r
245 +       { NOTMUCH_OPT_KEYWORD, &opt.filter_by, "filter-by", 'b',\r
246 +         (notmuch_keyword_t []){ { "nameaddr", FILTER_BY_NAMEADDR },\r
247 +                                 { "name", FILTER_BY_NAME },\r
248 +                                 { "addr", FILTER_BY_ADDR },\r
249 +                                 { "addrfold", FILTER_BY_ADDRFOLD },\r
250 +                                 { "nameaddrfold", FILTER_BY_NAMEADDRFOLD },\r
251 +                                 { 0, 0 } } },\r
252         { 0, 0, 0, 0, 0 }\r
253      };\r
254  \r
255 @@ -577,6 +619,11 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
256      if (! opt.output)\r
257         opt.output = OUTPUT_SUMMARY;\r
258  \r
259 +    if (opt.filter_by && !(opt.output & OUTPUT_ADDRESS_FLAGS)) {\r
260 +       fprintf (stderr, "Error: --filter-by can only be used with address output.\n");\r
261 +       return EXIT_FAILURE;\r
262 +    }\r
263 +\r
264      switch (format_sel) {\r
265      case NOTMUCH_FORMAT_TEXT:\r
266         opt.format = sprinter_text_create (config, stdout);\r
267 diff --git a/test/T095-search-filter-by.sh b/test/T095-search-filter-by.sh\r
268 new file mode 100755\r
269 index 0000000..97d9a9b\r
270 --- /dev/null\r
271 +++ b/test/T095-search-filter-by.sh\r
272 @@ -0,0 +1,64 @@\r
273 +#!/usr/bin/env bash\r
274 +test_description='duplicite address filtering in "notmuch search --output=recipients"'\r
275 +. ./test-lib.sh\r
276 +\r
277 +add_message '[to]="Real Name <foo@example.com>, Real Name <bar@example.com>"'\r
278 +add_message '[to]="Nickname <foo@example.com>"' '[cc]="Real Name <Bar@Example.COM>"'\r
279 +add_message '[to]="Nickname <foo@example.com>"' '[bcc]="Real Name <Bar@Example.COM>"'\r
280 +\r
281 +test_begin_subtest "--output=recipients"\r
282 +notmuch search --output=recipients "*" >OUTPUT\r
283 +cat <<EOF >EXPECTED\r
284 +Real Name <foo@example.com>\r
285 +Real Name <bar@example.com>\r
286 +Nickname <foo@example.com>\r
287 +Real Name <Bar@Example.COM>\r
288 +EOF\r
289 +test_expect_equal_file OUTPUT EXPECTED\r
290 +\r
291 +test_begin_subtest "--output=recipients --filter-by=nameaddr"\r
292 +notmuch search --output=recipients --filter-by=nameaddr "*" >OUTPUT\r
293 +# The same as above\r
294 +cat <<EOF >EXPECTED\r
295 +Real Name <foo@example.com>\r
296 +Real Name <bar@example.com>\r
297 +Nickname <foo@example.com>\r
298 +Real Name <Bar@Example.COM>\r
299 +EOF\r
300 +test_expect_equal_file OUTPUT EXPECTED\r
301 +\r
302 +test_begin_subtest "--output=recipients --filter-by=name"\r
303 +notmuch search --output=recipients --filter-by=name "*" >OUTPUT\r
304 +cat <<EOF >EXPECTED\r
305 +Real Name <foo@example.com>\r
306 +Nickname <foo@example.com>\r
307 +EOF\r
308 +test_expect_equal_file OUTPUT EXPECTED\r
309 +\r
310 +test_begin_subtest "--output=recipients --filter-by=addr"\r
311 +notmuch search --output=recipients --filter-by=addr "*" >OUTPUT\r
312 +cat <<EOF >EXPECTED\r
313 +Real Name <foo@example.com>\r
314 +Real Name <bar@example.com>\r
315 +Real Name <Bar@Example.COM>\r
316 +EOF\r
317 +test_expect_equal_file OUTPUT EXPECTED\r
318 +\r
319 +test_begin_subtest "--output=recipients --filter-by=addrfold"\r
320 +notmuch search --output=recipients --filter-by=addrfold "*" >OUTPUT\r
321 +cat <<EOF >EXPECTED\r
322 +Real Name <foo@example.com>\r
323 +Real Name <bar@example.com>\r
324 +EOF\r
325 +test_expect_equal_file OUTPUT EXPECTED\r
326 +\r
327 +test_begin_subtest "--output=recipients --filter-by=nameaddrfold"\r
328 +notmuch search --output=recipients --filter-by=nameaddrfold "*" >OUTPUT\r
329 +cat <<EOF >EXPECTED\r
330 +Real Name <foo@example.com>\r
331 +Real Name <bar@example.com>\r
332 +Nickname <foo@example.com>\r
333 +EOF\r
334 +test_expect_equal_file OUTPUT EXPECTED\r
335 +\r
336 +test_done\r
337 -- \r
338 2.1.1\r
339 \r