--- /dev/null
+Return-Path: <sojkam1@fel.cvut.cz>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 5EB54431FDB\r
+ for <notmuch@notmuchmail.org>; Fri, 9 Jan 2015 08:31:37 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.138\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.138 tagged_above=-999 required=5\r
+ tests=[DNS_FROM_AHBL_RHSBL=2.438, RCVD_IN_DNSWL_MED=-2.3]\r
+ autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id v-FuTaOBJyc4 for <notmuch@notmuchmail.org>;\r
+ Fri, 9 Jan 2015 08:31:32 -0800 (PST)\r
+Received: from max.feld.cvut.cz (max.feld.cvut.cz [147.32.192.36])\r
+ by olra.theworths.org (Postfix) with ESMTP id A6A66431FD8\r
+ for <notmuch@notmuchmail.org>; Fri, 9 Jan 2015 08:31:32 -0800 (PST)\r
+Received: from localhost (unknown [192.168.200.7])\r
+ by max.feld.cvut.cz (Postfix) with ESMTP id 841B73CFEF8;\r
+ Fri, 9 Jan 2015 17:31:27 +0100 (CET)\r
+X-Virus-Scanned: IMAP STYX AMAVIS\r
+Received: from max.feld.cvut.cz ([192.168.200.1])\r
+ by localhost (styx.feld.cvut.cz [192.168.200.7]) (amavisd-new,\r
+ port 10044)\r
+ with ESMTP id h_s2mZ4PUkMI; Fri, 9 Jan 2015 17:31:23 +0100 (CET)\r
+Received: from imap.feld.cvut.cz (imap.feld.cvut.cz [147.32.192.34])\r
+ by max.feld.cvut.cz (Postfix) with ESMTP id 87A453CFEAD;\r
+ Fri, 9 Jan 2015 17:31:23 +0100 (CET)\r
+Received: from wsh by steelpick.2x.cz with local (Exim 4.84)\r
+ (envelope-from <sojkam1@fel.cvut.cz>)\r
+ id 1Y9cT4-0006RW-Di; Fri, 09 Jan 2015 17:31:22 +0100\r
+From: Michal Sojka <sojkam1@fel.cvut.cz>\r
+To: Tomi Ollila <tomi.ollila@iki.fi>, David Bremner <david@tethera.net>,\r
+ notmuch@notmuchmail.org\r
+Subject: Re: [PATCH v3 10/10] cli: address: Add --filter-by\r
+ option to configure address filtering\r
+In-Reply-To: <871tn46khe.fsf@steelpick.2x.cz>\r
+References: <1415147159-19946-1-git-send-email-sojkam1@fel.cvut.cz>\r
+ <1415147159-19946-11-git-send-email-sojkam1@fel.cvut.cz>\r
+ <87vbkrfs66.fsf@maritornes.cs.unb.ca>\r
+ <m2d26yhfmk.fsf@guru.guru-group.fi>\r
+ <87egr46qcs.fsf@steelpick.2x.cz>\r
+ <m2h9w0ujo3.fsf@guru.guru-group.fi>\r
+ <871tn46khe.fsf@steelpick.2x.cz>\r
+User-Agent: Notmuch/0.18.2+178~g6e9e8bb (http://notmuchmail.org) Emacs/24.4.1\r
+ (x86_64-pc-linux-gnu)\r
+Date: Fri, 09 Jan 2015 17:31:22 +0100\r
+Message-ID: <87y4pb6hlx.fsf@steelpick.2x.cz>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 09 Jan 2015 16:31:37 -0000\r
+\r
+On Fri, Jan 09 2015, Michal Sojka wrote:\r
+> On Fri, Jan 09 2015, Tomi Ollila wrote:\r
+>> On Fri, Jan 09 2015, Michal Sojka <sojkam1@fel.cvut.cz> wrote:\r
+>>\r
+>>> Hi,\r
+>>>\r
+>>> sorry for longer response time :)\r
+>>>\r
+>>> On Thu, Jan 01 2015, Tomi Ollila wrote:\r
+>>>> On Wed, Dec 31 2014, David Bremner <david@tethera.net> wrote:\r
+>>>>\r
+>>>>> Michal Sojka <sojkam1@fel.cvut.cz> writes:\r
+>>>>>\r
+>>>>>> This option allows to configure the criterion for duplicate address\r
+>>>>>> filtering. Without this option, all unique combinations of name and\r
+>>>>>> address parts are printed. This option allows to filter the output\r
+>>>>>> more, for example to only contain unique address parts.\r
+>>>>>\r
+>>>>> I had the feeling there was some "controversy" about the UI here, but\r
+>>>>> following back the 3 versions of the series I didn't see it. Does that\r
+>>>>> mean we just need to sanity check the code, or are there outstanding\r
+>>>>> bikes to shed?\r
+>>>\r
+>>> I'd tend to rename this option to --unique as it was in some previous\r
+>>> version of the patch. Another thing in my mind is the implementation of\r
+>>> the --complete option mentioned in id:878uid9qjl.fsf@nautilus.nautilus.\r
+>>> This would also involve some kind of address filtering. I'll look into\r
+>>> this and send patches later.\r
+>>>\r
+>>>> I have intentionally been guiet on this during the review process of the\r
+>>>> other patches to not slow down the acceptance of the others. I have not\r
+>>>> got enough time to look the implemenentation or think this last patch\r
+>>>> further -- from the user interface point of view I recall seeing there\r
+>>>> both useless features (but which might be warranted by implementation\r
+>>>> simplicity) and missing features (but which might not be there due to\r
+>>>> difficulty in implementation). Also, I am not sure whether the --filter-by\r
+>>>> is good option (and options descriptive...)...\r
+>>>\r
+>>> I'd be interested in what are these "missing features".\r
+>>\r
+>> Last night when I tried to catch sleep I was also thinking of this...\r
+>> ... let's see what I remember...\r
+>>\r
+>> First, Currently if we have addresses:\r
+>>\r
+>> "Uni Que" <unique@example.org>\r
+>> "Uni Que" <Unique@Example.Org>\r
+>>\r
+>> I presume these are thought as a separate addresses -- and an option to\r
+>> thought these as the same would be useful.\r
+>\r
+> Yes, this would correspond to --unique=addrfold or --unique=nameaddrfold\r
+> from my patch.\r
+>\r
+>> but let's consider second set of addresses:\r
+>>\r
+>> "Uni Que" <unique@example.org>\r
+>> "Uni Keko" <unique@example.org>\r
+>>\r
+>> Now, if there were an option to consider these 2 as the same, that would\r
+>> hide user from one of the names -- It is clear that "Uni Que" is the right\r
+>> one but if only "Uni Keko" (sleepyhead, that is) is shown user don't have\r
+>> a choice to select the right one. I am not sure what the use case for\r
+>> "uniquing" these 2 were.\r
+>\r
+> For example, when you are interested in the number of people involved in\r
+> a discussion. You care only about the address and not about the names.\r
+> Perhaps you'd like to see only the addresses in the output and not the\r
+> names in this case, wouldn't you?\r
+\r
+I meant something like the patch bellow. Unique options would be\r
+no/yes/yes-but-case-insensitive and the type of uniqueness would be\r
+determined by the --output flags. I don't like one thing on this\r
+approach: --output flags now determine visibility of both rows and\r
+columns in the output. But this is already present in master, because we\r
+have count column there.\r
+\r
+-Michal\r
+\r
+diff --git a/notmuch-search.c b/notmuch-search.c\r
+index 14b9f01..760f59a 100644\r
+--- a/notmuch-search.c\r
++++ b/notmuch-search.c\r
+@@ -34,6 +34,8 @@ typedef enum {\r
+ OUTPUT_SENDER = 1 << 5,\r
+ OUTPUT_RECIPIENTS = 1 << 6,\r
+ OUTPUT_COUNT = 1 << 7,\r
++ OUTPUT_NAME = 1 << 8,\r
++ OUTPUT_ADDR = 1 << 9,\r
+ } output_t;\r
+ \r
+ typedef enum {\r
+@@ -43,6 +45,12 @@ typedef enum {\r
+ NOTMUCH_FORMAT_SEXP\r
+ } format_sel_t;\r
+ \r
++typedef enum {\r
++ UNIQUE_NO = 0,\r
++ UNIQUE_YES,\r
++ UNIQUE_CASEFOLD,\r
++} unique_t;\r
++\r
+ typedef struct {\r
+ notmuch_database_t *notmuch;\r
+ format_sel_t format_sel;\r
+@@ -55,6 +63,7 @@ typedef struct {\r
+ int limit;\r
+ int dupe;\r
+ GHashTable *addresses;\r
++ unique_t unique;\r
+ } search_context_t;\r
+ \r
+ typedef struct {\r
+@@ -243,18 +252,45 @@ do_search_threads (search_context_t *ctx)\r
+ return 0;\r
+ }\r
+ \r
+-/* Returns TRUE iff name and addr is duplicate. If not, stores the\r
+- * name/addr pair in order to detect subsequent duplicates. */\r
++/* Returns TRUE iff name and/or addr is considered duplicate. If not,\r
++ * stores the name/addr pair in order to detect subsequent\r
++ * duplicates. */\r
+ static notmuch_bool_t\r
+ is_duplicate (const search_context_t *ctx, const char *name, const char *addr)\r
+ {\r
+ notmuch_bool_t duplicate;\r
+ char *key;\r
++ gchar *addrfold = NULL;\r
++ gchar *namefold = NULL;\r
+ mailbox_t *mailbox;\r
+ \r
+- key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);\r
++ if (ctx->unique == UNIQUE_CASEFOLD) {\r
++ addrfold = g_utf8_casefold (addr, -1);\r
++ namefold = g_utf8_casefold (name, -1);\r
++ }\r
++\r
++ switch (ctx->output & (OUTPUT_NAME | OUTPUT_ADDR)) {\r
++ case OUTPUT_NAME | OUTPUT_ADDR:\r
++ key = talloc_asprintf (ctx->format, "%s <%s>", namefold ? namefold : name,\r
++ addrfold ? addrfold : addr);\r
++ break;\r
++ case OUTPUT_NAME:\r
++ key = talloc_strdup (ctx->format, namefold ? namefold : name); /* !name results in !key */\r
++ break;\r
++ case OUTPUT_ADDR:\r
++ key = talloc_strdup (ctx->format, addrfold ? addrfold : addr);\r
++ break;\r
++ default:\r
++ INTERNAL_ERROR("invalid --output flags");\r
++ }\r
++\r
++ if (addrfold) {\r
++ g_free (addrfold);\r
++ g_free (namefold);\r
++ }\r
++\r
+ if (! key)\r
+- return FALSE;\r
++ return TRUE;\r
+ \r
+ duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, (gpointer)&mailbox);\r
+ \r
+@@ -281,6 +317,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)\r
+ sprinter_t *format = ctx->format;\r
+ InternetAddress *ia = internet_address_mailbox_new (name, addr);\r
+ char *name_addr;\r
++ notmuch_bool_t no_name_and_addr = !(ctx->output & OUTPUT_NAME || ctx->output & OUTPUT_ADDR); /* Backward compatible behavior */\r
+ \r
+ /* name_addr has the name part quoted if necessary. Compare\r
+ * 'John Doe <john@doe.com>' vs. '"Doe, John" <john@doe.com>' */\r
+@@ -291,16 +328,27 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)\r
+ format->integer (format, count);\r
+ format->string (format, "\t");\r
+ }\r
+- format->string (format, name_addr);\r
++ if ((ctx->output & OUTPUT_NAME && ctx->output & OUTPUT_ADDR) || no_name_and_addr)\r
++ format->string (format, name_addr);\r
++ else if (ctx->output & OUTPUT_NAME)\r
++ format->string (format, name);\r
++ else if (ctx->output & OUTPUT_ADDR)\r
++ format->string (format, addr);\r
+ format->separator (format);\r
+ } else {\r
+ format->begin_map (format);\r
+- format->map_key (format, "name");\r
+- format->string (format, name);\r
+- format->map_key (format, "address");\r
+- format->string (format, addr);\r
+- format->map_key (format, "name-addr");\r
+- format->string (format, name_addr);\r
++ if (ctx->output & OUTPUT_NAME || no_name_and_addr) {\r
++ format->map_key (format, "name");\r
++ format->string (format, name);\r
++ }\r
++ if (ctx->output & OUTPUT_ADDR || no_name_and_addr) {\r
++ format->map_key (format, "address");\r
++ format->string (format, addr);\r
++ }\r
++ if ((ctx->output & OUTPUT_NAME && ctx->output & OUTPUT_ADDR) || no_name_and_addr) {\r
++ format->map_key (format, "name-addr");\r
++ format->string (format, name_addr);\r
++ }\r
+ if (count > 0) {\r
+ format->map_key (format, "count");\r
+ format->integer (format, count);\r
+@@ -631,6 +679,7 @@ static search_context_t search_context = {\r
+ .offset = 0,\r
+ .limit = -1, /* unlimited */\r
+ .dupe = -1,\r
++ .unique = UNIQUE_NO,\r
+ };\r
+ \r
+ static const notmuch_opt_desc_t common_options[] = {\r
+@@ -722,11 +771,18 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[])\r
+ (notmuch_keyword_t []){ { "sender", OUTPUT_SENDER },\r
+ { "recipients", OUTPUT_RECIPIENTS },\r
+ { "count", OUTPUT_COUNT },\r
++ { "name", OUTPUT_NAME },\r
++ { "addr", OUTPUT_ADDR },\r
+ { 0, 0 } } },\r
+ { NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x',\r
+ (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },\r
+ { "false", NOTMUCH_EXCLUDE_FALSE },\r
+ { 0, 0 } } },\r
++ { NOTMUCH_OPT_KEYWORD, &ctx->unique, "unique", 'b',\r
++ (notmuch_keyword_t []){ { "no", UNIQUE_NO },\r
++ { "yes", UNIQUE_YES },\r
++ { "casefold", UNIQUE_CASEFOLD },\r
++ { 0, 0 } } },\r
+ { NOTMUCH_OPT_INHERIT, &common_options, NULL, 0, 0 },\r
+ { 0, 0, 0, 0, 0 }\r
+ };\r
+\r