>From 9418781ad8f1efed5e0866d351ce9d5baf584500 Mon Sep 17 00:00:00 2001
From: Bart Massey <bart@po8.org>
Date: Sun, 17 Jan 2010 17:00:47 -0800
Subject: [PATCH] notmuch new: support for filename pattern filtering

In most maildir formats, only filenames consisting only of
digits should be candidate emails.  We add a new option
"filename_pattern" in the "database" section of the config
file that takes a regexp to filter candidate filenames
against.  By setting this string to "^[0-9]+$" only files
that match this pattern will even be considered.  This will
inhibit bogus "Notice: non-mail" messages, and may also
noticeably improve the efficiency of the tool if, for
example, large mbox files are left lying around in the
maildir.

Signed-off-by: Bart Massey <bart@po8.org>
---
 notmuch-client.h |    7 +++++++
 notmuch-config.c |   46 ++++++++++++++++++++++++++++++++++++++++++++--
 notmuch-new.c    |   35 +++++++++++++++++++++++++++++++----
 3 files changed, 82 insertions(+), 6 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 77766de..191988c 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -146,6 +146,13 @@ notmuch_config_set_database_path (notmuch_config_t *config,
 				  const char *database_path);
 
 const char *
+notmuch_config_get_filename_regex (notmuch_config_t *config);
+
+void
+notmuch_config_set_filename_regex (notmuch_config_t *config,
+				  const char *filename_regex);
+
+const char *
 notmuch_config_get_user_name (notmuch_config_t *config);
 
 void
diff --git a/notmuch-config.c b/notmuch-config.c
index 95430db..edfb4a6 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -31,11 +31,21 @@ static const char toplevel_config_comment[] =
 static const char database_config_comment[] =
     " Database configuration\n"
     "\n"
-    " The only value supported here is 'path' which should be the top-level\n"
+    " The value 'path' should be the top-level\n"
     " directory where your mail currently exists and to where mail will be\n"
     " delivered in the future. Files should be individual email messages.\n"
     " Notmuch will store its database within a sub-directory of the path\n"
-    " configured here named \".notmuch\".\n";
+    " configured here named \".notmuch\".\n"
+    "\n"
+    " The optional value 'filename_pattern' should be a POSIX"
+    " regular expression\n"
+    " matching only those filenames that will be checked for email\n"
+    " messages.  The match is against the last component of the pathname\n"
+    " only.  Anchors may be used, and probably should be.\n"
+    " Typically, this is used to match only\n"
+    " files whose name is a number in standard maildir format.  The\n"
+    " default pattern matches anything.\n" ;
+
 
 static const char user_config_comment[] =
     " User configuration\n"
@@ -58,6 +68,7 @@ struct _notmuch_config {
     GKeyFile *key_file;
 
     char *database_path;
+    char *filename_regex;
     char *user_name;
     char *user_primary_email;
     char **user_other_email;
@@ -151,6 +162,8 @@ get_username_from_passwd_file (void *ctx)
  *
  *	database_path:		$HOME/mail
  *
+ *	filename_pattern:	.*
+ *
  *	user_name:		From /etc/passwd
  *
  *	user_primary_mail: 	$EMAIL variable if set, otherwise
@@ -195,6 +208,7 @@ notmuch_config_open (void *ctx,
     config->key_file = g_key_file_new ();
 
     config->database_path = NULL;
+    config->filename_regex = NULL;
     config->user_name = NULL;
     config->user_primary_email = NULL;
     config->user_other_email = NULL;
@@ -354,6 +368,34 @@ notmuch_config_set_database_path (notmuch_config_t *config,
 }
 
 const char *
+notmuch_config_get_filename_regex (notmuch_config_t *config)
+{
+    char *regex;
+
+    if (config->filename_regex == NULL) {
+	regex = g_key_file_get_string (config->key_file,
+				      "database", "filename_pattern", NULL);
+	if (regex) {
+	    config->filename_regex = talloc_strdup (config, regex);
+	    free (regex);
+	}
+    }
+
+    return config->filename_regex;
+}
+
+void
+notmuch_config_set_filename_regex (notmuch_config_t *config,
+				   const char *filename_regex)
+{
+    g_key_file_set_string (config->key_file,
+			   "database", "filename_pattern", filename_regex);
+
+    talloc_free (config->filename_regex);
+    config->filename_regex = NULL;
+}
+
+const char *
 notmuch_config_get_user_name (notmuch_config_t *config)
 {
     char *name;
diff --git a/notmuch-new.c b/notmuch-new.c
index b740ee2..ab7ef9e 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -21,6 +21,8 @@
 #include "notmuch-client.h"
 
 #include <unistd.h>
+#include <sys/types.h>
+#include <regex.h>
 
 typedef struct _filename_node {
     char *filename;
@@ -207,6 +209,7 @@ _entries_resemble_maildir (struct dirent **entries, int count)
 static notmuch_status_t
 add_files_recursive (notmuch_database_t *notmuch,
 		     const char *path,
+		     const regex_t *maybe_regex,
 		     add_files_state_t *state)
 {
     DIR *dir = NULL;
@@ -291,7 +294,7 @@ add_files_recursive (notmuch_database_t *notmuch,
 	}
 
 	next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
-	status = add_files_recursive (notmuch, next, state);
+	status = add_files_recursive (notmuch, next, maybe_regex, state);
 	if (status && ret == NOTMUCH_STATUS_SUCCESS)
 	    ret = status;
 	talloc_free (next);
@@ -372,7 +375,7 @@ add_files_recursive (notmuch_database_t *notmuch,
 	}
 
 	/* We're now looking at a regular file that doesn't yet exist
-	 * in the database, so add it. */
+	 * in the database. */
 	next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
 
 	state->processed_files++;
@@ -390,6 +393,14 @@ add_files_recursive (notmuch_database_t *notmuch,
 	    fflush (stdout);
 	}
 
+	/* Check against the regex (if any) for valid mail
+	 * file names and bail on failure */
+	if (maybe_regex) {
+	    status = regexec(maybe_regex, entry->d_name, 0, 0, 0);
+	    if (status)
+		goto CLEANUP;
+	}
+
 	status = notmuch_database_add_message (notmuch, next, &message);
 	switch (status) {
 	/* success */
@@ -428,6 +439,7 @@ add_files_recursive (notmuch_database_t *notmuch,
 	    message = NULL;
 	}
 
+    CLEANUP:
 	if (do_add_files_print_progress) {
 	    do_add_files_print_progress = 0;
 	    add_files_print_progress (state);
@@ -492,6 +504,7 @@ add_files_recursive (notmuch_database_t *notmuch,
 static notmuch_status_t
 add_files (notmuch_database_t *notmuch,
 	   const char *path,
+	   const regex_t *maybe_regex,
 	   add_files_state_t *state)
 {
     notmuch_status_t status;
@@ -529,7 +542,7 @@ add_files (notmuch_database_t *notmuch,
 	return NOTMUCH_STATUS_FILE_ERROR;
     }
 
-    status = add_files_recursive (notmuch, path, state);
+    status = add_files_recursive (notmuch, path, maybe_regex, state);
 
     if (timer_is_active) {
 	/* Now stop the timer. */
@@ -696,6 +709,9 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
     int ret = 0;
     struct stat st;
     const char *db_path;
+    const char *filename_regex;
+    regex_t regex;
+    const regex_t *maybe_regex = 0;
     char *dot_notmuch_path;
     struct sigaction action;
     _filename_node_t *f;
@@ -721,6 +737,17 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
 
     db_path = notmuch_config_get_database_path (config);
 
+    filename_regex = notmuch_config_get_filename_regex (config);
+    if (filename_regex) {
+	status = regcomp(&regex, filename_regex, REG_EXTENDED | REG_NOSUB);
+	if (status) {
+	    fprintf (stderr, "Note: Ignoring bad filename_pattern "
+		     "in config file: %s\n", filename_regex);
+	} else {
+	    maybe_regex = &regex;
+	}
+    }
+
     dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch");
 
     if (stat (dot_notmuch_path, &st)) {
@@ -774,7 +801,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
     add_files_state.removed_files = _filename_list_create (ctx);
     add_files_state.removed_directories = _filename_list_create (ctx);
 
-    ret = add_files (notmuch, db_path, &add_files_state);
+    ret = add_files (notmuch, db_path, maybe_regex, &add_files_state);
 
     removed_files = 0;
     renamed_files = 0;
-- 
1.5.6.5
