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 B203F4196F0 for ; Tue, 23 Mar 2010 12:48:17 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -2.8 X-Spam-Level: X-Spam-Status: No, score=-2.8 tagged_above=-999 required=5 tests=[BAYES_05=-0.5, RCVD_IN_DNSWL_MED=-2.3] 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 0stSLAseDfei for ; Tue, 23 Mar 2010 12:48:15 -0700 (PDT) Received: from ipex2.johnshopkins.edu (ipex2.johnshopkins.edu [162.129.8.151]) by olra.theworths.org (Postfix) with ESMTP id E6BE4431FC1 for ; Tue, 23 Mar 2010 12:48:14 -0700 (PDT) X-IronPort-AV: E=Sophos;i="4.51,296,1267419600"; d="scan'208";a="315237919" Received: from c-69-255-36-229.hsd1.md.comcast.net (HELO lucky) ([69.255.36.229]) by ipex2.johnshopkins.edu with ESMTP/TLS/AES256-SHA; 23 Mar 2010 15:48:14 -0400 Received: from jkr by lucky with local (Exim 4.69) (envelope-from ) id 1NuA5O-0004g0-6O; Tue, 23 Mar 2010 15:48:22 -0400 From: Jesse Rosenthal To: notmuch@notmuchmail.org Date: Tue, 23 Mar 2010 15:48:22 -0400 Message-ID: <87d3yulszd.fsf@jhu.edu> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Subject: [notmuch] [PATCH] Allow saved queries for searching. 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: Tue, 23 Mar 2010 19:48:17 -0000 Add a "query:foo" term to the notmuch search syntax. Queries are "saved searches", and are written in the config file: [queries] foo=from:jrosenthal and to:notmuchmail and queries bar=tag:personal or (tag:unread and from:whomever) Then, if you search with the query `tag:inbox or query:foo' it will be passed along to notmuch as `tag:inbox and (from:jrosenthal and to:notmuchmail and queries)'. At the moment, when you ask for a query that doesn't exist it just treats it as blank. This seems more consistent than bailing out with an error (since no other search term will do that, short of running out of memory). This does mean, though, that non-existent queries, entered by themselves, will lead to a blank-search-term error. It should be pretty simple to improve this behavior, but I wanted to get the initial functionality out there. Note that this changed the arguments to query_string_from_args to include the hash-table of saved queries -- and since a lot functions call that one, this patch affects all of them. --- notmuch-client.h | 11 ++++++++--- notmuch-config.c | 38 ++++++++++++++++++++++++++++++++++++++ notmuch-count.c | 7 ++++++- notmuch-reply.c | 7 ++++++- notmuch-search-tags.c | 7 ++++++- notmuch-search.c | 8 +++++++- notmuch-show.c | 7 ++++++- notmuch-tag.c | 20 +++++++++++++------- query-string.c | 21 +++++++++++++++++++-- 9 files changed, 109 insertions(+), 17 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index c80b39c..daecbf4 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -116,9 +116,6 @@ notmuch_time_print_formatted_seconds (double seconds); double notmuch_time_elapsed (struct timeval start, struct timeval end); -char * -query_string_from_args (void *ctx, int argc, char *argv[]); - notmuch_status_t show_message_body (const char *filename, void (*show_part) (GMimeObject *part, int *part_count)); @@ -135,6 +132,10 @@ notmuch_config_open (void *ctx, const char *filename, notmuch_bool_t *is_new_ret); +char * +query_string_from_args (void *ctx, GHashTable *queries_hash, + int argc, char *argv[]); + void notmuch_config_close (notmuch_config_t *config); @@ -162,6 +163,10 @@ void notmuch_config_set_user_primary_email (notmuch_config_t *config, const char *primary_email); +void +notmuch_config_get_queries (notmuch_config_t *config, + GHashTable *queries_hash); + char ** notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length); diff --git a/notmuch-config.c b/notmuch-config.c index 95430db..ad81c25 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -62,6 +62,8 @@ struct _notmuch_config { char *user_primary_email; char **user_other_email; size_t user_other_email_length; + + GHashTable *queries_hash; }; static int @@ -70,6 +72,8 @@ notmuch_config_destructor (notmuch_config_t *config) if (config->key_file) g_key_file_free (config->key_file); + g_hash_table_unref (config->queries_hash); + return 0; } @@ -200,6 +204,11 @@ notmuch_config_open (void *ctx, config->user_other_email = NULL; config->user_other_email_length = 0; + config->queries_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + free, + NULL); + if (! g_key_file_load_from_file (config->key_file, config->filename, G_KEY_FILE_KEEP_COMMENTS, @@ -264,6 +273,8 @@ notmuch_config_open (void *ctx, } } + notmuch_config_get_queries (config, config->queries_hash); + /* When we create a new configuration file here, we add some * comments to help the user understand what can be done. */ if (is_new) { @@ -342,6 +353,33 @@ notmuch_config_get_database_path (notmuch_config_t *config) return config->database_path; } + +void +notmuch_config_get_queries (notmuch_config_t *config, + GHashTable *queries_hash) +{ + char **keys; + gsize size; + keys = g_key_file_get_keys (config->key_file, + "queries", + &size, + NULL); + gsize i; + for (i = 0; i < size; i++) { + char *value; + value = g_key_file_get_string (config->key_file, + "queries", + keys[i], + NULL); + + g_hash_table_insert (queries_hash, + keys[i], + value); + } +} + + + void notmuch_config_set_database_path (notmuch_config_t *config, const char *database_path) diff --git a/notmuch-count.c b/notmuch-count.c index 77aa433..2e8463b 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -85,7 +85,12 @@ notmuch_count_command (void *ctx, int argc, char *argv[]) if (notmuch == NULL) return 1; - query_str = query_string_from_args (ctx, argc, argv); + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + + query_str = query_string_from_args (ctx, queries_hash, argc, argv); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; diff --git a/notmuch-reply.c b/notmuch-reply.c index 6c15536..5b10c68 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -456,7 +456,12 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) if (config == NULL) return 1; - query_string = query_string_from_args (ctx, argc, argv); + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + + query_string = query_string_from_args (ctx, queries_hash, argc, argv); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return 1; diff --git a/notmuch-search-tags.c b/notmuch-search-tags.c index 6f3cfcc..e021cc3 100644 --- a/notmuch-search-tags.c +++ b/notmuch-search-tags.c @@ -57,8 +57,13 @@ notmuch_search_tags_command (void *ctx, int argc, char *argv[]) goto error; } + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + if (argc > 0) { - if ((query_str = query_string_from_args (ctx, argc, argv)) == NULL) { + if ((query_str = query_string_from_args (ctx, queries_hash, argc, argv)) == NULL) { fprintf (stderr, "Out of memory.\n"); goto error; } diff --git a/notmuch-search.c b/notmuch-search.c index 4e3514b..a35838b 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -244,12 +244,18 @@ notmuch_search_command (void *ctx, int argc, char *argv[]) if (config == NULL) return 1; + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; - query_str = query_string_from_args (ctx, argc, argv); + + query_str = query_string_from_args (ctx, queries_hash, argc, argv); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; diff --git a/notmuch-show.c b/notmuch-show.c index ff1fecb..f324ca3 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -446,7 +446,12 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) if (config == NULL) return 1; - query_string = query_string_from_args (ctx, argc, argv); + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + + query_string = query_string_from_args (ctx, queries_hash, argc, argv); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return 1; diff --git a/notmuch-tag.c b/notmuch-tag.c index 8b6f7dc..f65bb53 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -85,13 +85,6 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[])) return 1; } - query_string = query_string_from_args (ctx, argc - i, &argv[i]); - - if (*query_string == '\0') { - fprintf (stderr, "Error: notmuch tag requires at least one search term.\n"); - return 1; - } - config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; @@ -101,6 +94,19 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[])) if (notmuch == NULL) return 1; + GHashTable *queries_hash; + queries_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + free, NULL); + notmuch_config_get_queries (config, queries_hash); + + query_string = query_string_from_args (ctx, queries_hash, argc - i, &argv[i]); + + if (*query_string == '\0') { + fprintf (stderr, "Error: notmuch tag requires at least one search term.\n"); + return 1; + } + + query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory.\n"); diff --git a/query-string.c b/query-string.c index 6536512..2bd79c8 100644 --- a/query-string.c +++ b/query-string.c @@ -30,7 +30,8 @@ * This function returns NULL in case of insufficient memory. */ char * -query_string_from_args (void *ctx, int argc, char *argv[]) +query_string_from_args (void *ctx, GHashTable *queries_hash, + int argc, char *argv[]) { char *query_string; int i; @@ -46,7 +47,23 @@ query_string_from_args (void *ctx, int argc, char *argv[]) return NULL; } - query_string = talloc_strdup_append (query_string, argv[i]); + if (STRNCMP_LITERAL (argv[i], "query:") == 0) { + + char *saved_search; + char *replacement; + char *replacement_string; + + saved_search = argv[i] + sizeof ("query:") - 1; + + replacement = g_hash_table_lookup (queries_hash, saved_search); + replacement_string = talloc_asprintf (ctx, + "(%s)", + (char *) replacement); + + query_string = talloc_strdup_append (query_string, replacement); + } else { + query_string = talloc_strdup_append (query_string, argv[i]); + } if (query_string == NULL) return NULL; } -- 1.6.3.3