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 |  21 +++++--
 notmuch-new.c               | 134 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 140 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f1517..0af86f9beee4 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -75,11 +75,22 @@ 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. Matching files and directories will be
+          ignored. 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 0f50457eb894..5a262e419b44 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;
     bool 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,125 @@ _special_directory (const char *entry)
     return strcmp (entry, ".") == 0 || strcmp (entry, "..") == 0;
 }
 
+static bool
+_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 == 0) {
+           fprintf (stderr, "Error: Empty string in new.ignore list\n");
+           return false;
+       }
+
+       if (s[0] == '/') {
+           regex_t *preg;
+           char *r;
+           int rerr;
+
+           if (len < 3 || s[len - 1] != '/') {
+               fprintf (stderr, "Error: Malformed pattern '%s' in 
new.ignore\n",
+                        s);
+               return false;
+           }
+
+           r = talloc_strndup (config, s + 1, len - 2);
+           regex = talloc_realloc (config, regex, regex_t, nregex + 1);
+           preg = &regex[nregex];
+
+           rerr = regcomp (preg, r, REG_EXTENDED | REG_NOSUB);
+           if (rerr) {
+               size_t error_size = regerror (rerr, preg, NULL, 0);
+               char *error = talloc_size (r, error_size);
+
+               regerror (rerr, preg, error, error_size);
+
+               fprintf (stderr, "Error: Invalid regex '%s' in new.ignore: 
%s\n",
+                        r, error);
+               return false;
+           }
+           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 bool
-_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)
 {
+    bool 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;
+    }
 
-    return false;
+    if (state->ignore_regex_length == 0)
+       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 +572,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 +637,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 +867,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 +1091,12 @@ 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;
+
+    if (! _setup_ignore (config, &add_files_state))
+       return EXIT_FAILURE;
 
     for (i = 0; i < add_files_state.new_tags_length; i++) {
        const char *error_msg;
-- 
2.11.0

_______________________________________________
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch

Reply via email to