mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-25 04:18:08 +01:00
cli/new: support /<regex>/ in new.ignore
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.
This commit is contained in:
parent
89f651a403
commit
f2a6790583
2 changed files with 140 additions and 15 deletions
|
@ -79,11 +79,22 @@ The available configuration items are described below.
|
||||||
Default: ``unread;inbox``.
|
Default: ``unread;inbox``.
|
||||||
|
|
||||||
**new.ignore**
|
**new.ignore**
|
||||||
A list of file and directory names, without path, that will not
|
A list to specify files and directories that will not be
|
||||||
be searched for messages by **notmuch new**. All the files and
|
searched for messages by **notmuch new**. Each entry in the
|
||||||
directories matching any of the names specified here will be
|
list is either:
|
||||||
ignored, regardless of the location in the mail store directory
|
|
||||||
hierarchy.
|
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.
|
Default: empty list.
|
||||||
|
|
||||||
|
|
132
notmuch-new.c
132
notmuch-new.c
|
@ -42,13 +42,17 @@ enum verbosity {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
const char *db_path;
|
||||||
|
|
||||||
int output_is_a_tty;
|
int output_is_a_tty;
|
||||||
enum verbosity verbosity;
|
enum verbosity verbosity;
|
||||||
bool debug;
|
bool debug;
|
||||||
const char **new_tags;
|
const char **new_tags;
|
||||||
size_t new_tags_length;
|
size_t new_tags_length;
|
||||||
const char **new_ignore;
|
const char **ignore_verbatim;
|
||||||
size_t new_ignore_length;
|
size_t ignore_verbatim_length;
|
||||||
|
regex_t *ignore_regex;
|
||||||
|
size_t ignore_regex_length;
|
||||||
|
|
||||||
int total_files;
|
int total_files;
|
||||||
int processed_files;
|
int processed_files;
|
||||||
|
@ -240,18 +244,125 @@ _special_directory (const char *entry)
|
||||||
return strcmp (entry, ".") == 0 || strcmp (entry, "..") == 0;
|
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 = ®ex[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.
|
/* Test if the file/directory is to be ignored.
|
||||||
*/
|
*/
|
||||||
static bool
|
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;
|
size_t i;
|
||||||
|
char *path;
|
||||||
|
|
||||||
for (i = 0; i < state->new_ignore_length; i++)
|
for (i = 0; i < state->ignore_verbatim_length; i++) {
|
||||||
if (strcmp (entry, state->new_ignore[i]) == 0)
|
if (strcmp (entry, state->ignore_verbatim[i]) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->ignore_regex_length == 0)
|
||||||
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. */
|
/* 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
|
* and because we don't care if dirent_type fails on entries
|
||||||
* that are explicitly ignored.
|
* 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)
|
if (state->debug)
|
||||||
printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",
|
printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",
|
||||||
path, entry->d_name);
|
path, entry->d_name);
|
||||||
|
@ -526,7 +637,7 @@ add_files (notmuch_database_t *notmuch,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Ignore files & directories user has configured to be ignored */
|
/* 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)
|
if (state->debug)
|
||||||
printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",
|
printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",
|
||||||
path, entry->d_name);
|
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
|
/* Ignore any files/directories the user has configured to be
|
||||||
* ignored
|
* ignored
|
||||||
*/
|
*/
|
||||||
if (_entry_in_ignore_list (entry->d_name, state)) {
|
if (_entry_in_ignore_list (state, path, entry->d_name)) {
|
||||||
if (state->debug)
|
if (state->debug)
|
||||||
printf ("(D) count_files: explicitly ignoring %s/%s\n",
|
printf ("(D) count_files: explicitly ignoring %s/%s\n",
|
||||||
path, entry->d_name);
|
path, entry->d_name);
|
||||||
|
@ -981,9 +1092,12 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
|
||||||
add_files_state.verbosity = VERBOSITY_VERBOSE;
|
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_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);
|
add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
|
||||||
db_path = notmuch_config_get_database_path (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++) {
|
for (i = 0; i < add_files_state.new_tags_length; i++) {
|
||||||
const char *error_msg;
|
const char *error_msg;
|
||||||
|
|
Loading…
Reference in a new issue