[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 = ®ex[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