[PATCH 5/6] cli/new: support /<regex>/ in new.ignore

Jani Nikula jani at nikula.org
Fri Sep 1 08:53:10 PDT 2017


Add support for using /<regex>/ style regular expressions in
new.ignore, mixed with the old style verbatim file and directory
basenames. The regex is matched against the relative path from the
database path.
---
 doc/man1/notmuch-config.rst |  20 ++++++--
 notmuch-new.c               | 109 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 114 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f1517..aab0676b1578 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -75,11 +75,21 @@ The available configuration items are described below.
         Default: ``unread;inbox``.
 
     **new.ignore**
-        A list of file and directory names, without path, that will not
-        be searched for messages by **notmuch new**. All the files and
-        directories matching any of the names specified here will be
-        ignored, regardless of the location in the mail store directory
-        hierarchy.
+        A list to specify files and directories that will not be
+        searched for messages by **notmuch new**. Each entry in the
+        list is either:
+
+	  A file or a directory name, without path, that will be
+	  ignored, regardless of the location in the mail store
+	  directory hierarchy.
+
+	Or:
+
+	  A regular expression delimited with // that will be matched
+	  against the path of the file or directory relative to the
+	  database path. The beginning and end of string must be
+	  explictly anchored. For example, /.*/foo$/ would match
+	  "bar/foo" and "bar/baz/foo", but not "foo" or "bar/foobar".
 
         Default: empty list.
 
diff --git a/notmuch-new.c b/notmuch-new.c
index 2ce3af872f0e..8146c99413b8 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -42,13 +42,17 @@ enum verbosity {
 };
 
 typedef struct {
+    const char *db_path;
+
     int output_is_a_tty;
     enum verbosity verbosity;
     notmuch_bool_t debug;
     const char **new_tags;
     size_t new_tags_length;
-    const char **new_ignore;
-    size_t new_ignore_length;
+    const char **ignore_verbatim;
+    size_t ignore_verbatim_length;
+    regex_t *ignore_regex;
+    size_t ignore_regex_length;
 
     int total_files;
     int processed_files;
@@ -240,18 +244,102 @@ _special_directory (const char *entry)
     return strcmp (entry, ".") == 0 || strcmp (entry, "..") == 0;
 }
 
+static notmuch_bool_t
+_setup_ignore (notmuch_config_t *config, add_files_state_t *state)
+{
+    const char **ignore_list, **ignore;
+    int nregex = 0, nverbatim = 0;
+    const char **verbatim = NULL;
+    regex_t *regex = NULL;
+
+    ignore_list = notmuch_config_get_new_ignore (config, NULL);
+    if (! ignore_list)
+	return TRUE;
+
+    for (ignore = ignore_list; *ignore; ignore++) {
+	const char *s = *ignore;
+	size_t len = strlen (s);
+
+	if (len > 2 && s[0] == '/' && s[len - 1] == '/') {
+	    regex_t *preg;
+	    char *r = talloc_strndup (config, s + 1, len - 2);
+
+	    regex = talloc_realloc (config, regex, regex_t, nregex + 1);
+	    preg = &regex[nregex];
+
+	    if (xregcomp (preg, r, REG_EXTENDED | REG_NOSUB) == 0)
+		nregex++;
+
+	    talloc_free (r);
+	} else {
+	    verbatim = talloc_realloc (config, verbatim, const char *,
+				       nverbatim + 1);
+	    verbatim[nverbatim++] = s;
+	}
+    }
+
+    state->ignore_regex = regex;
+    state->ignore_regex_length = nregex;
+    state->ignore_verbatim = verbatim;
+    state->ignore_verbatim_length = nverbatim;
+
+    return TRUE;
+}
+
+static char *
+_get_relative_path (const char *db_path, const char *dirpath, const char *entry)
+{
+    size_t db_path_len = strlen (db_path);
+
+    /* paranoia? */
+    if (strncmp (dirpath, db_path, db_path_len) != 0) {
+	fprintf (stderr, "Warning: '%s' is not a subdirectory of '%s'\n",
+		 dirpath, db_path);
+	return NULL;
+    }
+
+    dirpath += db_path_len;
+    while (*dirpath == '/')
+	dirpath++;
+
+    if (*dirpath)
+	return talloc_asprintf (NULL, "%s/%s", dirpath, entry);
+    else
+	return talloc_strdup (NULL, entry);
+}
+
 /* Test if the file/directory is to be ignored.
  */
 static notmuch_bool_t
-_entry_in_ignore_list (const char *entry, add_files_state_t *state)
+_entry_in_ignore_list (add_files_state_t *state, const char *dirpath,
+		       const char *entry)
 {
+    notmuch_bool_t ret = FALSE;
     size_t i;
+    char *path;
 
-    for (i = 0; i < state->new_ignore_length; i++)
-	if (strcmp (entry, state->new_ignore[i]) == 0)
+    for (i = 0; i < state->ignore_verbatim_length; i++) {
+	if (strcmp (entry, state->ignore_verbatim[i]) == 0)
 	    return TRUE;
+    }
+
+    if (! state->ignore_regex_length)
+	return FALSE;
 
-    return FALSE;
+    path = _get_relative_path (state->db_path, dirpath, entry);
+    if (! path)
+	return FALSE;
+
+    for (i = 0; i < state->ignore_regex_length; i++) {
+	if (regexec (&state->ignore_regex[i], path, 0, NULL, 0) == 0) {
+	    ret = TRUE;
+	    break;
+	}
+    }
+
+    talloc_free (path);
+
+    return ret;
 }
 
 /* Add a single file to the database. */
@@ -461,7 +549,7 @@ add_files (notmuch_database_t *notmuch,
 	 * and because we don't care if dirent_type fails on entries
 	 * that are explicitly ignored.
 	 */
-	if (_entry_in_ignore_list (entry->d_name, state)) {
+	if (_entry_in_ignore_list (state, path, entry->d_name)) {
 	    if (state->debug)
 		printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",
 			path, entry->d_name);
@@ -526,7 +614,7 @@ add_files (notmuch_database_t *notmuch,
 	    continue;
 
 	/* Ignore files & directories user has configured to be ignored */
-	if (_entry_in_ignore_list (entry->d_name, state)) {
+	if (_entry_in_ignore_list (state, path, entry->d_name)) {
 	    if (state->debug)
 		printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",
 			path, entry->d_name);
@@ -756,7 +844,7 @@ count_files (const char *path, int *count, add_files_state_t *state)
 	/* Ignore any files/directories the user has configured to be
 	 * ignored
 	 */
-	if (_entry_in_ignore_list (entry->d_name, state)) {
+	if (_entry_in_ignore_list (state, path, entry->d_name)) {
 	    if (state->debug)
 		printf ("(D) count_files: explicitly ignoring %s/%s\n",
 			path, entry->d_name);
@@ -980,9 +1068,10 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
 	add_files_state.verbosity = VERBOSITY_VERBOSE;
 
     add_files_state.new_tags = notmuch_config_get_new_tags (config, &add_files_state.new_tags_length);
-    add_files_state.new_ignore = notmuch_config_get_new_ignore (config, &add_files_state.new_ignore_length);
     add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
     db_path = notmuch_config_get_database_path (config);
+    add_files_state.db_path = db_path;
+    _setup_ignore (config, &add_files_state);
 
     for (i = 0; i < add_files_state.new_tags_length; i++) {
 	const char *error_msg;
-- 
2.11.0



More information about the notmuch mailing list