Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id D209D4196F3 for ; Wed, 21 Apr 2010 07:11:22 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -1.9 X-Spam-Level: X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5 tests=[BAYES_00=-1.9] autolearn=ham Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id svf-4VNHV8ST for ; Wed, 21 Apr 2010 07:11:17 -0700 (PDT) Received: from homiemail-a13.g.dreamhost.com (caiajhbdcagg.dreamhost.com [208.97.132.66]) by olra.theworths.org (Postfix) with ESMTP id 7F2CB431FC1 for ; Wed, 21 Apr 2010 07:11:17 -0700 (PDT) Received: from sspaeth.de (unknown [195.190.188.219]) by homiemail-a13.g.dreamhost.com (Postfix) with ESMTPA id B3EA06A806B; Wed, 21 Apr 2010 07:11:13 -0700 (PDT) Received: by sspaeth.de (sSMTP sendmail emulation); Wed, 21 Apr 2010 16:11:09 +0200 From: "Sebastian Spaeth" To: Dirk Hohndel , Notmuch development list Subject: Re: Address completion In-Reply-To: <871ve9jkhy.fsf@SSpaeth.de> References: <87bpddg67c.fsf@SSpaeth.de> <871ve9jkhy.fsf@SSpaeth.de> Date: Wed, 21 Apr 2010 16:11:09 +0200 Message-ID: <878w8gj3pe.fsf@SSpaeth.de> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 21 Apr 2010 14:11:23 -0000 --=-=-= David improved the notmuch-address.el glue today, and I improved my addrlookup tool also. It is much more intelligent now: We parse the notmuch db in up to 3 passes now where each find from the previous pass is sorted with heigher weight, ie we will first output addresses in our "address book" then addresses we have mailed to previously and only than at mails we have received. 1st pass: Find all 'from' addresses that are in messages tagged as 'addressbook' (this tag is configurable in .notmuch-config under [user] addrbook_tag=foo but uses addressbook by default) 2nd pass: Find all 'to','cc','bcc' addresses that match our criteria and that we ever sent a mail from our primary mail address 3rd pass: If pass1 + 2 lead to less than 10 message hits, perform a pass 3. Look at all email 'from' headers that match our criteria and use those. We limit this, because notmuch opens all mail files that might match, to read the header out of them, potentially hurting performance a lot. So don't do pass 3 if pass 1 & 2 lead to likely results already. Using the address book feature, you can implement simple 'blacklisting' of emails. If you have friends at Sun for example, you might want to 'notmuch tag +addressbook from:"oracle.com"' to give those addresses priority over the sun.com addresses you have previously used. Performance is still good enough. Doing a "addrlookup" which returns everyone in my "addressbook" and all mail addresses I ever sent mail to (just 165), takes real 0m0.095s with a warm file cache. If the file cache is cold, a search for e.g. "he" can take real 0m2.385s. The reason is that notmuch opens all possibly matching mail files in order to retrieve the headers. Compile directly from vala or the attached c source. Compiling the c source works with glib2.0-dev installed: cc -o addrlookup addrlookup.c `pkg-config --cflags --libs gobject-2.0` -lnotmuch --=-=-= Content-Type: text/x-csrc Content-Disposition: attachment; filename=addrlookup.c Content-Description: c source file /* addrlookup.c generated by valac, the Vala compiler * generated from addrlookup.vala, do not modify */ #include #include #include #include #include #include #include #define TYPE_ADDRESS_MATCHER (address_matcher_get_type ()) #define ADDRESS_MATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ADDRESS_MATCHER, AddressMatcher)) #define ADDRESS_MATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ADDRESS_MATCHER, AddressMatcherClass)) #define IS_ADDRESS_MATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ADDRESS_MATCHER)) #define IS_ADDRESS_MATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_ADDRESS_MATCHER)) #define ADDRESS_MATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ADDRESS_MATCHER, AddressMatcherClass)) typedef struct _AddressMatcher AddressMatcher; typedef struct _AddressMatcherClass AddressMatcherClass; typedef struct _AddressMatcherPrivate AddressMatcherPrivate; #define _notmuch_database_close0(var) ((var == NULL) ? NULL : (var = (notmuch_database_close (var), NULL))) #define _g_free0(var) (var = (g_free (var), NULL)) #define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL))) #define _g_key_file_free0(var) ((var == NULL) ? NULL : (var = (g_key_file_free (var), NULL))) #define ADDRESS_MATCHER_TYPE_MAILADDRESS_FREQ (address_matcher_mailaddress_freq_get_type ()) typedef struct _AddressMatcherMailAddress_freq AddressMatcherMailAddress_freq; #define _g_list_free0(var) ((var == NULL) ? NULL : (var = (g_list_free (var), NULL))) #define _g_hash_table_unref0(var) ((var == NULL) ? NULL : (var = (g_hash_table_unref (var), NULL))) #define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL))) #define _g_match_info_free0(var) ((var == NULL) ? NULL : (var = (g_match_info_free (var), NULL))) #define _0(var) ((var == NULL) ? NULL : (var = ( (var), NULL))) #define _address_matcher_mailaddress_freq_free0(var) ((var == NULL) ? NULL : (var = (address_matcher_mailaddress_freq_free (var), NULL))) #define __g_list_free_address_matcher_mailaddress_freq_free0(var) ((var == NULL) ? NULL : (var = (_g_list_free_address_matcher_mailaddress_freq_free (var), NULL))) #define _g_string_free0(var) ((var == NULL) ? NULL : (var = (g_string_free (var, TRUE), NULL))) typedef struct _ParamSpecAddressMatcher ParamSpecAddressMatcher; #define _address_matcher_unref0(var) ((var == NULL) ? NULL : (var = (address_matcher_unref (var), NULL))) struct _AddressMatcher { GTypeInstance parent_instance; volatile int ref_count; AddressMatcherPrivate * priv; }; struct _AddressMatcherClass { GTypeClass parent_class; void (*finalize) (AddressMatcher *self); }; struct _AddressMatcherPrivate { notmuch_database_t* db; char* user_db_path; char* user_primary_email; char* user_addrbook_tag; }; struct _AddressMatcherMailAddress_freq { char* address; guint* occurances; gint occurances_length1; gint _occurances_size_; }; struct _ParamSpecAddressMatcher { GParamSpec parent_instance; }; static gpointer address_matcher_parent_class = NULL; gpointer address_matcher_ref (gpointer instance); void address_matcher_unref (gpointer instance); GParamSpec* param_spec_address_matcher (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags); void value_set_address_matcher (GValue* value, gpointer v_object); void value_take_address_matcher (GValue* value, gpointer v_object); gpointer value_get_address_matcher (const GValue* value); GType address_matcher_get_type (void); #define ADDRESS_MATCHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ADDRESS_MATCHER, AddressMatcherPrivate)) enum { ADDRESS_MATCHER_DUMMY_PROPERTY }; AddressMatcher* address_matcher_new (void); AddressMatcher* address_matcher_construct (GType object_type); static GType address_matcher_mailaddress_freq_get_type (void) G_GNUC_UNUSED; static AddressMatcherMailAddress_freq* address_matcher_mailaddress_freq_dup (const AddressMatcherMailAddress_freq* self); static void address_matcher_mailaddress_freq_free (AddressMatcherMailAddress_freq* self); static void address_matcher_mailaddress_freq_copy (const AddressMatcherMailAddress_freq* self, AddressMatcherMailAddress_freq* dest); static void address_matcher_mailaddress_freq_destroy (AddressMatcherMailAddress_freq* self); static gint address_matcher_sort_by_freq (AddressMatcherMailAddress_freq* mail1, AddressMatcherMailAddress_freq* mail2); char* address_matcher_frequent_fullname (AddressMatcher* self, GHashTable* frequencies); GHashTable* address_matcher_addresses_by_frequency (AddressMatcher* self, notmuch_messages_t* msgs, const char* name, guint pass, GHashTable** addr2realname); static void _vala_array_add1 (char*** array, int* length, int* size, char* value); static void _g_list_free_address_matcher_mailaddress_freq_free (GList* self); char** address_matcher_search_address_passes (AddressMatcher* self, notmuch_query_t** queries, int queries_length1, const char* name, int* result_length1); static void _vala_array_add2 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value); static void _vala_array_add3 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value); static void _vala_array_add4 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value); void address_matcher_run (AddressMatcher* self, const char* name); static guint* _vala_array_dup1 (guint* self, int length); static void address_matcher_finalize (AddressMatcher* obj); gint _vala_main (char** args, int args_length1); static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func); static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func); AddressMatcher* address_matcher_construct (GType object_type) { GError * _inner_error_; AddressMatcher* self; GKeyFile* config; char* home; _inner_error_ = NULL; self = (AddressMatcher*) g_type_create_instance (object_type); config = g_key_file_new (); home = g_strdup (g_getenv ("NOTMUCH_CONFIG")); if (home == NULL) { char* _tmp0_; home = (_tmp0_ = g_strdup (g_get_home_dir ()), _g_free0 (home), _tmp0_); } { char* _tmp1_; char* _tmp2_; char* _tmp3_; g_key_file_load_from_file (config, _tmp1_ = g_strconcat (home, "/.notmuch-config", NULL), G_KEY_FILE_NONE, &_inner_error_); _g_free0 (_tmp1_); if (_inner_error_ != NULL) { goto __catch0_g_error; } _tmp2_ = g_key_file_get_string (config, "database", "path", &_inner_error_); if (_inner_error_ != NULL) { goto __catch0_g_error; } self->priv->user_db_path = (_tmp3_ = _tmp2_, _g_free0 (self->priv->user_db_path), _tmp3_); } goto __finally0; __catch0_g_error: { GError * ex; ex = _inner_error_; _inner_error_ = NULL; { _g_error_free0 (ex); } } __finally0: if (_inner_error_ != NULL) { _g_key_file_free0 (config); _g_free0 (home); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } { char* _tmp4_; char* _tmp5_; _tmp4_ = g_key_file_get_string (config, "user", "primary_email", &_inner_error_); if (_inner_error_ != NULL) { goto __catch1_g_error; } self->priv->user_primary_email = (_tmp5_ = _tmp4_, _g_free0 (self->priv->user_primary_email), _tmp5_); } goto __finally1; __catch1_g_error: { GError * ex; ex = _inner_error_; _inner_error_ = NULL; { _g_error_free0 (ex); } } __finally1: if (_inner_error_ != NULL) { _g_key_file_free0 (config); _g_free0 (home); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } { char* _tmp6_; char* _tmp7_; _tmp6_ = g_key_file_get_string (config, "user", "addrbook_tag", &_inner_error_); if (_inner_error_ != NULL) { goto __catch2_g_error; } self->priv->user_addrbook_tag = (_tmp7_ = _tmp6_, _g_free0 (self->priv->user_addrbook_tag), _tmp7_); } goto __finally2; __catch2_g_error: { GError * ex; ex = _inner_error_; _inner_error_ = NULL; { char* _tmp8_; self->priv->user_addrbook_tag = (_tmp8_ = g_strdup ("addressbook"), _g_free0 (self->priv->user_addrbook_tag), _tmp8_); _g_error_free0 (ex); } } __finally2: if (_inner_error_ != NULL) { _g_key_file_free0 (config); _g_free0 (home); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } _g_key_file_free0 (config); _g_free0 (home); return self; } AddressMatcher* address_matcher_new (void) { return address_matcher_construct (TYPE_ADDRESS_MATCHER); } static gint address_matcher_sort_by_freq (AddressMatcherMailAddress_freq* mail1, AddressMatcherMailAddress_freq* mail2) { gint result = 0; gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; gboolean _tmp2_ = FALSE; gboolean _tmp3_ = FALSE; if ((*mail1).occurances[0] == (*mail2).occurances[0]) { _tmp1_ = (*mail1).occurances[1] == (*mail2).occurances[1]; } else { _tmp1_ = FALSE; } if (_tmp1_) { _tmp0_ = (*mail1).occurances[2] == (*mail2).occurances[2]; } else { _tmp0_ = FALSE; } if (_tmp0_) { result = 0; return result; } if ((*mail1).occurances[0] > (*mail2).occurances[0]) { _tmp3_ = TRUE; } else { gboolean _tmp4_ = FALSE; if ((*mail1).occurances[0] == (*mail2).occurances[0]) { _tmp4_ = (*mail1).occurances[1] > (*mail2).occurances[1]; } else { _tmp4_ = FALSE; } _tmp3_ = _tmp4_; } if (_tmp3_) { _tmp2_ = TRUE; } else { gboolean _tmp5_ = FALSE; gboolean _tmp6_ = FALSE; if ((*mail1).occurances[0] == (*mail2).occurances[0]) { _tmp6_ = (*mail1).occurances[1] == (*mail2).occurances[1]; } else { _tmp6_ = FALSE; } if (_tmp6_) { _tmp5_ = (*mail1).occurances[2] > (*mail2).occurances[2]; } else { _tmp5_ = FALSE; } _tmp2_ = _tmp5_; } if (_tmp2_) { result = -1; return result; } result = 1; return result; } static gboolean string_contains (const char* self, const char* needle) { gboolean result = FALSE; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (needle != NULL, FALSE); result = strstr (self, needle) != NULL; return result; } char* address_matcher_frequent_fullname (AddressMatcher* self, GHashTable* frequencies) { char* result = NULL; guint maxfreq; char* fullname; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (frequencies != NULL, NULL); maxfreq = (guint) 0; fullname = NULL; { GList* mail_collection; GList* mail_it; mail_collection = g_hash_table_get_keys (frequencies); for (mail_it = mail_collection; mail_it != NULL; mail_it = mail_it->next) { const char* mail; mail = (const char*) mail_it->data; { guint freq; gboolean _tmp0_ = FALSE; gboolean _tmp1_ = FALSE; freq = GPOINTER_TO_UINT (g_hash_table_lookup (frequencies, mail)); if (freq > maxfreq) { _tmp1_ = string_contains (mail, " "); } else { _tmp1_ = FALSE; } if (_tmp1_) { _tmp0_ = TRUE; } else { _tmp0_ = g_hash_table_size (frequencies) == 1; } if (_tmp0_) { char* _tmp2_; maxfreq = freq; fullname = (_tmp2_ = g_strdup (mail), _g_free0 (fullname), _tmp2_); } } } _g_list_free0 (mail_collection); } result = fullname; return result; } static gpointer _g_hash_table_ref0 (gpointer self) { return self ? g_hash_table_ref (self) : NULL; } GHashTable* address_matcher_addresses_by_frequency (AddressMatcher* self, notmuch_messages_t* msgs, const char* name, guint pass, GHashTable** addr2realname) { GHashTable* result = NULL; GError * _inner_error_; GHashTable* ht; GRegex* re; char** _tmp3_; gint _headers_size_; gint headers_length1; char** _tmp2_ = NULL; char** headers; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (msgs != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (addr2realname != NULL, NULL); _inner_error_ = NULL; ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); re = NULL; { GRegex* _tmp0_; GRegex* _tmp1_; _tmp0_ = g_regex_new ("\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" "\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)" \ ">?)", 0, 0, &_inner_error_); if (_inner_error_ != NULL) { if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch3_g_regex_error; } _g_hash_table_unref0 (ht); _g_regex_unref0 (re); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } re = (_tmp1_ = _tmp0_, _g_regex_unref0 (re), _tmp1_); } goto __finally3; __catch3_g_regex_error: { GError * ex; ex = _inner_error_; _inner_error_ = NULL; { _g_error_free0 (ex); } } __finally3: if (_inner_error_ != NULL) { _g_hash_table_unref0 (ht); _g_regex_unref0 (re); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } headers = (_tmp3_ = (_tmp2_ = g_new0 (char*, 1 + 1), _tmp2_[0] = g_strdup ("from"), _tmp2_), headers_length1 = 1, _headers_size_ = headers_length1, _tmp3_); if (pass == 1) { char** _tmp5_; char** _tmp4_ = NULL; headers = (_tmp5_ = (_tmp4_ = g_new0 (char*, 3 + 1), _tmp4_[0] = g_strdup ("to"), _tmp4_[1] = g_strdup ("cc"), _tmp4_[2] = g_strdup ("bcc"), _tmp4_), headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL), headers_length1 = 3, _headers_size_ = headers_length1, _tmp5_); } while (TRUE) { GMatchInfo* matches; notmuch_message_t* msg; if (!notmuch_messages_valid (msgs)) { break; } matches = NULL; msg = notmuch_messages_get (msgs); { char** header_collection; int header_collection_length1; int header_it; header_collection = headers; header_collection_length1 = headers_length1; for (header_it = 0; header_it < headers_length1; header_it = header_it + 1) { char* header; header = g_strdup (header_collection[header_it]); { char* froms; GMatchInfo* _tmp8_; gboolean _tmp7_; GMatchInfo* _tmp6_ = NULL; gboolean found; froms = g_strdup ((const char*) notmuch_message_get_header (msg, header)); found = (_tmp7_ = g_regex_match (re, froms, 0, &_tmp6_), matches = (_tmp8_ = _tmp6_, _g_match_info_free0 (matches), _tmp8_), _tmp7_); while (TRUE) { char* from; char* addr; char* _tmp9_; char* _tmp11_; gboolean _tmp12_; gboolean is_match; guint occurs; GHashTable* realname_freq; if (!found) { break; } from = g_match_info_fetch (matches, 1); addr = g_match_info_fetch_named (matches, "mail"); addr = (_tmp9_ = g_utf8_strdown (addr, -1), _g_free0 (addr), _tmp9_); { gboolean _tmp10_; _tmp10_ = g_match_info_next (matches, &_inner_error_); if (_inner_error_ != NULL) { if (_inner_error_->domain == G_REGEX_ERROR) { goto __catch4_g_regex_error; } _g_free0 (from); _g_free0 (addr); _g_free0 (header); _g_free0 (froms); _g_match_info_free0 (matches); _0 (msg); _g_hash_table_unref0 (ht); _g_regex_unref0 (re); headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL); g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } found = _tmp10_; } goto __finally4; __catch4_g_regex_error: { GError * ex; ex = _inner_error_; _inner_error_ = NULL; { _g_error_free0 (ex); } } __finally4: if (_inner_error_ != NULL) { _g_free0 (from); _g_free0 (addr); _g_free0 (header); _g_free0 (froms); _g_match_info_free0 (matches); _0 (msg); _g_hash_table_unref0 (ht); _g_regex_unref0 (re); headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL); g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return NULL; } is_match = (_tmp12_ = g_regex_match_simple (_tmp11_ = g_strconcat ("\\b", name, NULL), from, G_REGEX_CASELESS, 0), _g_free0 (_tmp11_), _tmp12_); if (!is_match) { _g_free0 (from); _g_free0 (addr); continue; } occurs = GPOINTER_TO_UINT (g_hash_table_lookup (ht, addr)) + 1; g_hash_table_replace (ht, g_strdup (addr), GUINT_TO_POINTER (occurs)); realname_freq = _g_hash_table_ref0 ((GHashTable*) g_hash_table_lookup (*addr2realname, addr)); if (realname_freq == NULL) { GHashTable* _tmp13_; realname_freq = (_tmp13_ = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL), _g_hash_table_unref0 (realname_freq), _tmp13_); g_hash_table_insert (*addr2realname, g_strdup (addr), _g_hash_table_ref0 (realname_freq)); } occurs = GPOINTER_TO_UINT (g_hash_table_lookup (realname_freq, from)) + 1; g_hash_table_replace (realname_freq, g_strdup (from), GUINT_TO_POINTER (occurs)); _g_free0 (from); _g_free0 (addr); _g_hash_table_unref0 (realname_freq); } _g_free0 (header); _g_free0 (froms); } } } notmuch_message_destroy (msg); notmuch_messages_move_to_next (msgs); _g_match_info_free0 (matches); _0 (msg); } result = ht; _g_regex_unref0 (re); headers = (_vala_array_free (headers, headers_length1, (GDestroyNotify) g_free), NULL); return result; } static gpointer _address_matcher_mailaddress_freq_dup0 (gpointer self) { return self ? address_matcher_mailaddress_freq_dup (self) : NULL; } static void _vala_array_add1 (char*** array, int* length, int* size, char* value) { if ((*length) == (*size)) { *size = (*size) ? (2 * (*size)) : 4; *array = g_renew (char*, *array, (*size) + 1); } (*array)[(*length)++] = value; (*array)[*length] = NULL; } static void _g_list_free_address_matcher_mailaddress_freq_free (GList* self) { g_list_foreach (self, (GFunc) address_matcher_mailaddress_freq_free, NULL); g_list_free (self); } char** address_matcher_search_address_passes (AddressMatcher* self, notmuch_query_t** queries, int queries_length1, const char* name, int* result_length1) { char** result = NULL; char** _tmp0_; gint _return_value_size_; gint return_value_length1; char** return_value; GHashTable* addrfreq; GHashTable* addr2realname; guint pass; GList* addrs; char** _tmp6_; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); return_value = (_tmp0_ = NULL, return_value_length1 = 0, _return_value_size_ = return_value_length1, _tmp0_); addrfreq = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); addr2realname = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); pass = (guint) 0; { notmuch_query_t** q_collection; int q_collection_length1; int q_it; q_collection = queries; q_collection_length1 = queries_length1; for (q_it = 0; q_it < queries_length1; q_it = q_it + 1) { notmuch_query_t* q; q = q_collection[q_it]; { notmuch_messages_t* msgs; GHashTable* ht; msgs = notmuch_query_search_messages (q); ht = address_matcher_addresses_by_frequency (self, msgs, name, pass, &addr2realname); { GList* addr_collection; GList* addr_it; addr_collection = g_hash_table_get_keys (ht); for (addr_it = addr_collection; addr_it != NULL; addr_it = addr_it->next) { const char* addr; addr = (const char*) addr_it->data; { AddressMatcherMailAddress_freq* freq; freq = _address_matcher_mailaddress_freq_dup0 ((AddressMatcherMailAddress_freq*) g_hash_table_lookup (addrfreq, addr)); if (freq == NULL) { AddressMatcherMailAddress_freq* _tmp5_; AddressMatcherMailAddress_freq _tmp4_; AddressMatcherMailAddress_freq _tmp3_; guint* _tmp1_ = NULL; AddressMatcherMailAddress_freq _tmp2_ = {0}; freq = (_tmp5_ = _address_matcher_mailaddress_freq_dup0 ((_tmp4_ = _tmp3_ = (memset (&_tmp2_, 0, sizeof (AddressMatcherMailAddress_freq)), _tmp2_.address = g_strdup (addr), _tmp2_.occurances = (_tmp1_ = g_new0 (guint, 3), _tmp1_[0] = (guint) 0, _tmp1_[1] = (guint) 0, _tmp1_[2] = (guint) 0, _tmp1_), _tmp2_.occurances_length1 = 3, _tmp2_), &_tmp4_)), _address_matcher_mailaddress_freq_free0 (freq), _tmp5_); address_matcher_mailaddress_freq_destroy (&_tmp3_); } (*freq).occurances[pass] = GPOINTER_TO_UINT (g_hash_table_lookup (ht, addr)); g_hash_table_replace (addrfreq, g_strdup (addr), _address_matcher_mailaddress_freq_dup0 (freq)); _address_matcher_mailaddress_freq_free0 (freq); } } _g_list_free0 (addr_collection); } notmuch_messages_destroy (msgs); pass = pass + ((guint) 1); _0 (msgs); _g_hash_table_unref0 (ht); } } } addrs = g_hash_table_get_values (addrfreq); addrs = g_list_sort (addrs, (GCompareFunc) address_matcher_sort_by_freq); { GList* addr_collection; GList* addr_it; addr_collection = addrs; for (addr_it = addr_collection; addr_it != NULL; addr_it = addr_it->next) { AddressMatcherMailAddress_freq* addr; addr = _address_matcher_mailaddress_freq_dup0 ((AddressMatcherMailAddress_freq*) addr_it->data); { GHashTable* freqs; freqs = _g_hash_table_ref0 ((GHashTable*) g_hash_table_lookup (addr2realname, (*addr).address)); _vala_array_add1 (&return_value, &return_value_length1, &_return_value_size_, address_matcher_frequent_fullname (self, freqs)); _address_matcher_mailaddress_freq_free0 (addr); _g_hash_table_unref0 (freqs); } } } result = (_tmp6_ = return_value, *result_length1 = return_value_length1, _tmp6_); _g_hash_table_unref0 (addrfreq); _g_hash_table_unref0 (addr2realname); __g_list_free_address_matcher_mailaddress_freq_free0 (addrs); return result; return_value = (_vala_array_free (return_value, return_value_length1, (GDestroyNotify) g_free), NULL); _g_hash_table_unref0 (addrfreq); _g_hash_table_unref0 (addr2realname); __g_list_free_address_matcher_mailaddress_freq_free0 (addrs); } static void _vala_array_add2 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) { if ((*length) == (*size)) { *size = (*size) ? (2 * (*size)) : 4; *array = g_renew (notmuch_query_t*, *array, *size); } (*array)[(*length)++] = value; } static void _vala_array_add3 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) { if ((*length) == (*size)) { *size = (*size) ? (2 * (*size)) : 4; *array = g_renew (notmuch_query_t*, *array, *size); } (*array)[(*length)++] = value; } static void _vala_array_add4 (notmuch_query_t*** array, int* length, int* size, notmuch_query_t* value) { if ((*length) == (*size)) { *size = (*size) ? (2 * (*size)) : 4; *array = g_renew (notmuch_query_t*, *array, *size); } (*array)[(*length)++] = value; } void address_matcher_run (AddressMatcher* self, const char* name) { notmuch_query_t** _tmp1_; gint _queries_size_; gint queries_length1; notmuch_query_t** _tmp0_ = NULL; notmuch_query_t** queries; notmuch_database_t* _tmp2_; char* _tmp3_; GString* _tmp4_; GString* querystr; GString* _tmp7_; char** _tmp15_; gint __result__size_; gint _result__length1; gint _tmp14_; char** _result_; g_return_if_fail (self != NULL); queries = (_tmp1_ = (_tmp0_ = g_new0 (notmuch_query_t*, 0), _tmp0_), queries_length1 = 0, _queries_size_ = queries_length1, _tmp1_); self->priv->db = (_tmp2_ = notmuch_database_open (self->priv->user_db_path, NOTMUCH_DATABASE_MODE_READ_ONLY), _notmuch_database_close0 (self->priv->db), _tmp2_); querystr = (_tmp4_ = g_string_new (_tmp3_ = g_strconcat ("tag:", self->priv->user_addrbook_tag, NULL)), _g_free0 (_tmp3_), _tmp4_); if (name != NULL) { char* _tmp6_; char* _tmp5_; g_string_append (querystr, _tmp6_ = g_strconcat (_tmp5_ = g_strconcat (" and from:", name, NULL), "*", NULL)); _g_free0 (_tmp6_); _g_free0 (_tmp5_); } else { name = ""; } _vala_array_add2 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str)); querystr = (_tmp7_ = g_string_new (""), _g_string_free0 (querystr), _tmp7_); if (name != NULL) { char* _tmp9_; char* _tmp8_; g_string_append (querystr, _tmp9_ = g_strconcat (_tmp8_ = g_strconcat ("to:", name, NULL), "*", NULL)); _g_free0 (_tmp9_); _g_free0 (_tmp8_); } if (self->priv->user_primary_email != NULL) { char* _tmp10_; g_string_append (querystr, _tmp10_ = g_strconcat (" from:", self->priv->user_primary_email, NULL)); _g_free0 (_tmp10_); } _vala_array_add3 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str)); if ((notmuch_query_count_messages (queries[0]) + notmuch_query_count_messages (queries[1])) < 10) { GString* _tmp11_; querystr = (_tmp11_ = g_string_new (""), _g_string_free0 (querystr), _tmp11_); if (name != NULL) { char* _tmp13_; char* _tmp12_; g_string_append (querystr, _tmp13_ = g_strconcat (_tmp12_ = g_strconcat ("from:", name, NULL), "*", NULL)); _g_free0 (_tmp13_); _g_free0 (_tmp12_); } _vala_array_add4 (&queries, &queries_length1, &_queries_size_, notmuch_query_create (self->priv->db, querystr->str)); } _result_ = (_tmp15_ = address_matcher_search_address_passes (self, queries, queries_length1, name, &_tmp14_), _result__length1 = _tmp14_, __result__size_ = _result__length1, _tmp15_); { char** name_collection; int name_collection_length1; int name_it; name_collection = _result_; name_collection_length1 = _result__length1; for (name_it = 0; name_it < _result__length1; name_it = name_it + 1) { char* name; name = g_strdup (name_collection[name_it]); { fprintf (stdout, "%s\n", name); _g_free0 (name); } } } queries = (g_free (queries), NULL); _g_string_free0 (querystr); _result_ = (_vala_array_free (_result_, _result__length1, (GDestroyNotify) g_free), NULL); } static guint* _vala_array_dup1 (guint* self, int length) { return g_memdup (self, length * sizeof (guint)); } static void address_matcher_mailaddress_freq_copy (const AddressMatcherMailAddress_freq* self, AddressMatcherMailAddress_freq* dest) { guint* _tmp0_; dest->address = g_strdup (self->address); dest->occurances = (_tmp0_ = self->occurances, (_tmp0_ == NULL) ? ((gpointer) _tmp0_) : _vala_array_dup1 (_tmp0_, (*self).occurances_length1)); dest->occurances_length1 = self->occurances_length1; } static void address_matcher_mailaddress_freq_destroy (AddressMatcherMailAddress_freq* self) { _g_free0 (self->address); self->occurances = (g_free (self->occurances), NULL); } static AddressMatcherMailAddress_freq* address_matcher_mailaddress_freq_dup (const AddressMatcherMailAddress_freq* self) { AddressMatcherMailAddress_freq* dup; dup = g_new0 (AddressMatcherMailAddress_freq, 1); address_matcher_mailaddress_freq_copy (self, dup); return dup; } static void address_matcher_mailaddress_freq_free (AddressMatcherMailAddress_freq* self) { address_matcher_mailaddress_freq_destroy (self); g_free (self); } static GType address_matcher_mailaddress_freq_get_type (void) { static volatile gsize address_matcher_mailaddress_freq_type_id__volatile = 0; if (g_once_init_enter (&address_matcher_mailaddress_freq_type_id__volatile)) { GType address_matcher_mailaddress_freq_type_id; address_matcher_mailaddress_freq_type_id = g_boxed_type_register_static ("AddressMatcherMailAddress_freq", (GBoxedCopyFunc) address_matcher_mailaddress_freq_dup, (GBoxedFreeFunc) address_matcher_mailaddress_freq_free); g_once_init_leave (&address_matcher_mailaddress_freq_type_id__volatile, address_matcher_mailaddress_freq_type_id); } return address_matcher_mailaddress_freq_type_id__volatile; } static void value_address_matcher_init (GValue* value) { value->data[0].v_pointer = NULL; } static void value_address_matcher_free_value (GValue* value) { if (value->data[0].v_pointer) { address_matcher_unref (value->data[0].v_pointer); } } static void value_address_matcher_copy_value (const GValue* src_value, GValue* dest_value) { if (src_value->data[0].v_pointer) { dest_value->data[0].v_pointer = address_matcher_ref (src_value->data[0].v_pointer); } else { dest_value->data[0].v_pointer = NULL; } } static gpointer value_address_matcher_peek_pointer (const GValue* value) { return value->data[0].v_pointer; } static gchar* value_address_matcher_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) { if (collect_values[0].v_pointer) { AddressMatcher* object; object = collect_values[0].v_pointer; if (object->parent_instance.g_class == NULL) { return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); } else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) { return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); } value->data[0].v_pointer = address_matcher_ref (object); } else { value->data[0].v_pointer = NULL; } return NULL; } static gchar* value_address_matcher_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) { AddressMatcher** object_p; object_p = collect_values[0].v_pointer; if (!object_p) { return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); } if (!value->data[0].v_pointer) { *object_p = NULL; } else if (collect_flags && G_VALUE_NOCOPY_CONTENTS) { *object_p = value->data[0].v_pointer; } else { *object_p = address_matcher_ref (value->data[0].v_pointer); } return NULL; } GParamSpec* param_spec_address_matcher (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags) { ParamSpecAddressMatcher* spec; g_return_val_if_fail (g_type_is_a (object_type, TYPE_ADDRESS_MATCHER), NULL); spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags); G_PARAM_SPEC (spec)->value_type = object_type; return G_PARAM_SPEC (spec); } gpointer value_get_address_matcher (const GValue* value) { g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER), NULL); return value->data[0].v_pointer; } void value_set_address_matcher (GValue* value, gpointer v_object) { AddressMatcher* old; g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER)); old = value->data[0].v_pointer; if (v_object) { g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADDRESS_MATCHER)); g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); value->data[0].v_pointer = v_object; address_matcher_ref (value->data[0].v_pointer); } else { value->data[0].v_pointer = NULL; } if (old) { address_matcher_unref (old); } } void value_take_address_matcher (GValue* value, gpointer v_object) { AddressMatcher* old; g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADDRESS_MATCHER)); old = value->data[0].v_pointer; if (v_object) { g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADDRESS_MATCHER)); g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); value->data[0].v_pointer = v_object; } else { value->data[0].v_pointer = NULL; } if (old) { address_matcher_unref (old); } } static void address_matcher_class_init (AddressMatcherClass * klass) { address_matcher_parent_class = g_type_class_peek_parent (klass); ADDRESS_MATCHER_CLASS (klass)->finalize = address_matcher_finalize; g_type_class_add_private (klass, sizeof (AddressMatcherPrivate)); } static void address_matcher_instance_init (AddressMatcher * self) { self->priv = ADDRESS_MATCHER_GET_PRIVATE (self); self->priv->user_db_path = NULL; self->priv->user_primary_email = NULL; self->priv->user_addrbook_tag = NULL; self->ref_count = 1; } static void address_matcher_finalize (AddressMatcher* obj) { AddressMatcher * self; self = ADDRESS_MATCHER (obj); _notmuch_database_close0 (self->priv->db); _g_free0 (self->priv->user_db_path); _g_free0 (self->priv->user_primary_email); _g_free0 (self->priv->user_addrbook_tag); } GType address_matcher_get_type (void) { static volatile gsize address_matcher_type_id__volatile = 0; if (g_once_init_enter (&address_matcher_type_id__volatile)) { static const GTypeValueTable g_define_type_value_table = { value_address_matcher_init, value_address_matcher_free_value, value_address_matcher_copy_value, value_address_matcher_peek_pointer, "p", value_address_matcher_collect_value, "p", value_address_matcher_lcopy_value }; static const GTypeInfo g_define_type_info = { sizeof (AddressMatcherClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) address_matcher_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (AddressMatcher), 0, (GInstanceInitFunc) address_matcher_instance_init, &g_define_type_value_table }; static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) }; GType address_matcher_type_id; address_matcher_type_id = g_type_register_fundamental (g_type_fundamental_next (), "AddressMatcher", &g_define_type_info, &g_define_type_fundamental_info, 0); g_once_init_leave (&address_matcher_type_id__volatile, address_matcher_type_id); } return address_matcher_type_id__volatile; } gpointer address_matcher_ref (gpointer instance) { AddressMatcher* self; self = instance; g_atomic_int_inc (&self->ref_count); return instance; } void address_matcher_unref (gpointer instance) { AddressMatcher* self; self = instance; if (g_atomic_int_dec_and_test (&self->ref_count)) { ADDRESS_MATCHER_GET_CLASS (self)->finalize (self); g_type_free_instance ((GTypeInstance *) self); } } gint _vala_main (char** args, int args_length1) { gint result = 0; AddressMatcher* app; app = address_matcher_new (); address_matcher_run (app, args[1]); result = 0; _address_matcher_unref0 (app); return result; } int main (int argc, char ** argv) { g_type_init (); return _vala_main (argv, argc); } static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func) { if ((array != NULL) && (destroy_func != NULL)) { int i; for (i = 0; i < array_length; i = i + 1) { if (((gpointer*) array)[i] != NULL) { destroy_func (((gpointer*) array)[i]); } } } } static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func) { _vala_array_destroy (array, array_length, destroy_func); g_free (array); } --=-=-= Sebastian --=-=-=--