" The following options are supported here:\n"
"\n"
"\ttags A list (separated by ';') of the tags that will be\n"
- "\t added to all messages incorporated by \"notmuch new\".\n";
+ "\t added to all messages incorporated by \"notmuch new\".\n"
+ "\n"
+ "\tignore A list (separated by ';') of file and directory names\n"
+ "\t that will not be searched for messages by \"notmuch new\".\n"
+ "\n"
+ "\t NOTE: *Every* file/directory that goes by one of those\n"
+ "\t names will be ignored, independent of its depth/location\n"
+ "\t in the mail store.\n";
static const char user_config_comment[] =
" User configuration\n"
"\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
"\tcommands will notice tag changes and update flags in filenames\n";
+static const char search_config_comment[] =
+ " Search configuration\n"
+ "\n"
+ " The following option is supported here:\n"
+ "\n"
+ "\texclude_tags\n"
+ "\t\tA ;-separated list of tags that will be excluded from\n"
+ "\t\tsearch results by default. Using an excluded tag in a\n"
+ "\t\tquery will override that exclusion.\n";
+
struct _notmuch_config {
char *filename;
GKeyFile *key_file;
+ notmuch_bool_t is_new;
char *database_path;
char *user_name;
size_t user_other_email_length;
const char **new_tags;
size_t new_tags_length;
+ const char **new_ignore;
+ size_t new_ignore_length;
notmuch_bool_t maildir_synchronize_flags;
+ const char **search_exclude_tags;
+ size_t search_exclude_tags_length;
};
static int
* These default configuration settings are determined as
* follows:
*
- * database_path: $HOME/mail
+ * database_path: $MAILDIR, otherwise $HOME/mail
*
- * user_name: From /etc/passwd
+ * user_name: $NAME variable if set, otherwise
+ * read from /etc/passwd
*
* user_primary_mail: $EMAIL variable if set, otherwise
* constructed from the username and
notmuch_config_t *
notmuch_config_open (void *ctx,
const char *filename,
- notmuch_bool_t *is_new_ret)
+ notmuch_bool_t create_new)
{
GError *error = NULL;
- int is_new = 0;
size_t tmp;
char *notmuch_config_env = NULL;
int file_had_database_group;
int file_had_new_group;
int file_had_user_group;
int file_had_maildir_group;
-
- if (is_new_ret)
- *is_new_ret = 0;
+ int file_had_search_group;
notmuch_config_t *config = talloc (ctx, notmuch_config_t);
if (config == NULL) {
config->key_file = g_key_file_new ();
+ config->is_new = FALSE;
config->database_path = NULL;
config->user_name = NULL;
config->user_primary_email = NULL;
config->user_other_email_length = 0;
config->new_tags = NULL;
config->new_tags_length = 0;
+ config->new_ignore = NULL;
+ config->new_ignore_length = 0;
config->maildir_synchronize_flags = TRUE;
+ config->search_exclude_tags = NULL;
+ config->search_exclude_tags_length = 0;
if (! g_key_file_load_from_file (config->key_file,
config->filename,
G_KEY_FILE_KEEP_COMMENTS,
&error))
{
- /* If the caller passed a non-NULL value for is_new_ret, then
- * the caller is prepared for a default configuration file in
- * the case of FILE NOT FOUND. Otherwise, any read failure is
- * an error.
- */
- if (is_new_ret &&
- error->domain == G_FILE_ERROR &&
- error->code == G_FILE_ERROR_NOENT)
- {
- g_error_free (error);
- is_new = 1;
+ if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) {
+ /* If create_new is true, then the caller is prepared for a
+ * default configuration file in the case of FILE NOT
+ * FOUND.
+ */
+ if (create_new) {
+ g_error_free (error);
+ config->is_new = TRUE;
+ } else {
+ fprintf (stderr, "Configuration file %s not found.\n"
+ "Try running 'notmuch setup' to create a configuration.\n",
+ config->filename);
+ talloc_free (config);
+ g_error_free (error);
+ return NULL;
+ }
}
else
{
file_had_new_group = g_key_file_has_group (config->key_file, "new");
file_had_user_group = g_key_file_has_group (config->key_file, "user");
file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
+ file_had_search_group = g_key_file_has_group (config->key_file, "search");
if (notmuch_config_get_database_path (config) == NULL) {
- char *path = talloc_asprintf (config, "%s/mail",
- getenv ("HOME"));
+ char *path = getenv ("MAILDIR");
+ if (path)
+ path = talloc_strdup (config, path);
+ else
+ path = talloc_asprintf (config, "%s/mail",
+ getenv ("HOME"));
notmuch_config_set_database_path (config, path);
talloc_free (path);
}
if (notmuch_config_get_user_name (config) == NULL) {
- char *name = get_name_from_passwd_file (config);
+ char *name = getenv ("NAME");
+ if (name)
+ name = talloc_strdup (config, name);
+ else
+ name = get_name_from_passwd_file (config);
notmuch_config_set_user_name (config, name);
talloc_free (name);
}
notmuch_config_set_new_tags (config, tags, 2);
}
+ if (notmuch_config_get_new_ignore (config, &tmp) == NULL) {
+ notmuch_config_set_new_ignore (config, NULL, 0);
+ }
+
+ if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
+ if (config->is_new) {
+ const char *tags[] = { "deleted", "spam" };
+ notmuch_config_set_search_exclude_tags (config, tags, 2);
+ } else {
+ notmuch_config_set_search_exclude_tags (config, NULL, 0);
+ }
+ }
+
error = NULL;
config->maildir_synchronize_flags =
g_key_file_get_boolean (config->key_file,
/* Whenever we know of configuration sections that don't appear in
* the configuration file, we add some comments to help the user
* understand what can be done. */
- if (is_new)
- {
+ if (config->is_new)
g_key_file_set_comment (config->key_file, NULL, NULL,
toplevel_config_comment, NULL);
- }
if (! file_had_database_group)
- {
g_key_file_set_comment (config->key_file, "database", NULL,
database_config_comment, NULL);
- }
if (! file_had_new_group)
- {
g_key_file_set_comment (config->key_file, "new", NULL,
new_config_comment, NULL);
- }
if (! file_had_user_group)
- {
g_key_file_set_comment (config->key_file, "user", NULL,
user_config_comment, NULL);
- }
if (! file_had_maildir_group)
- {
g_key_file_set_comment (config->key_file, "maildir", NULL,
maildir_config_comment, NULL);
- }
- if (is_new_ret)
- *is_new_ret = is_new;
+ if (! file_had_search_group)
+ g_key_file_set_comment (config->key_file, "search", NULL,
+ search_config_comment, NULL);
return config;
}
notmuch_config_save (notmuch_config_t *config)
{
size_t length;
- char *data;
+ char *data, *filename;
GError *error = NULL;
data = g_key_file_to_data (config->key_file, &length, NULL);
return 1;
}
- if (! g_file_set_contents (config->filename, data, length, &error)) {
- fprintf (stderr, "Error saving configuration to %s: %s\n",
- config->filename, error->message);
+ /* Try not to overwrite symlinks. */
+ filename = canonicalize_file_name (config->filename);
+ if (! filename) {
+ if (errno == ENOENT) {
+ filename = strdup (config->filename);
+ if (! filename) {
+ fprintf (stderr, "Out of memory.\n");
+ g_free (data);
+ return 1;
+ }
+ } else {
+ fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
+ strerror (errno));
+ g_free (data);
+ return 1;
+ }
+ }
+
+ if (! g_file_set_contents (filename, data, length, &error)) {
+ if (strcmp (filename, config->filename) != 0) {
+ fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
+ config->filename, filename, error->message);
+ } else {
+ fprintf (stderr, "Error saving configuration to %s: %s\n",
+ filename, error->message);
+ }
g_error_free (error);
+ free (filename);
g_free (data);
return 1;
}
+ free (filename);
g_free (data);
return 0;
}
-const char *
-notmuch_config_get_database_path (notmuch_config_t *config)
-{
- char *path;
-
- if (config->database_path == NULL) {
- path = g_key_file_get_string (config->key_file,
- "database", "path", NULL);
- if (path) {
- config->database_path = talloc_strdup (config, path);
- free (path);
- }
- }
-
- return config->database_path;
-}
-
-void
-notmuch_config_set_database_path (notmuch_config_t *config,
- const char *database_path)
+notmuch_bool_t
+notmuch_config_is_new (notmuch_config_t *config)
{
- g_key_file_set_string (config->key_file,
- "database", "path", database_path);
-
- talloc_free (config->database_path);
- config->database_path = NULL;
+ return config->is_new;
}
-const char *
-notmuch_config_get_user_name (notmuch_config_t *config)
+static const char *
+_config_get (notmuch_config_t *config, char **field,
+ const char *group, const char *key)
{
- char *name;
-
- if (config->user_name == NULL) {
- name = g_key_file_get_string (config->key_file,
- "user", "name", NULL);
- if (name) {
- config->user_name = talloc_strdup (config, name);
- free (name);
+ /* read from config file and cache value, if not cached already */
+ if (*field == NULL) {
+ char *value;
+ value = g_key_file_get_string (config->key_file, group, key, NULL);
+ if (value) {
+ *field = talloc_strdup (config, value);
+ free (value);
}
}
-
- return config->user_name;
-}
-
-void
-notmuch_config_set_user_name (notmuch_config_t *config,
- const char *user_name)
-{
- g_key_file_set_string (config->key_file,
- "user", "name", user_name);
-
- talloc_free (config->user_name);
- config->user_name = NULL;
+ return *field;
}
-const char *
-notmuch_config_get_user_primary_email (notmuch_config_t *config)
-{
- char *email;
-
- if (config->user_primary_email == NULL) {
- email = g_key_file_get_string (config->key_file,
- "user", "primary_email", NULL);
- if (email) {
- config->user_primary_email = talloc_strdup (config, email);
- free (email);
- }
- }
-
- return config->user_primary_email;
-}
-
-void
-notmuch_config_set_user_primary_email (notmuch_config_t *config,
- const char *primary_email)
+static void
+_config_set (notmuch_config_t *config, char **field,
+ const char *group, const char *key, const char *value)
{
- g_key_file_set_string (config->key_file,
- "user", "primary_email", primary_email);
+ g_key_file_set_string (config->key_file, group, key, value);
- talloc_free (config->user_primary_email);
- config->user_primary_email = NULL;
+ /* drop the cached value */
+ talloc_free (*field);
+ *field = NULL;
}
static const char **
{
assert(outlist);
+ /* read from config file and cache value, if not cached already */
if (*outlist == NULL) {
char **inlist = g_key_file_get_string_list (config->key_file,
return *outlist;
}
+static void
+_config_set_list (notmuch_config_t *config,
+ const char *group, const char *name,
+ const char *list[],
+ size_t length, const char ***config_var )
+{
+ g_key_file_set_string_list (config->key_file, group, name, list, length);
+
+ /* drop the cached value */
+ talloc_free (*config_var);
+ *config_var = NULL;
+}
+
+const char *
+notmuch_config_get_database_path (notmuch_config_t *config)
+{
+ return _config_get (config, &config->database_path, "database", "path");
+}
+
+void
+notmuch_config_set_database_path (notmuch_config_t *config,
+ const char *database_path)
+{
+ _config_set (config, &config->database_path, "database", "path", database_path);
+}
+
+const char *
+notmuch_config_get_user_name (notmuch_config_t *config)
+{
+ return _config_get (config, &config->user_name, "user", "name");
+}
+
+void
+notmuch_config_set_user_name (notmuch_config_t *config,
+ const char *user_name)
+{
+ _config_set (config, &config->user_name, "user", "name", user_name);
+}
+
+const char *
+notmuch_config_get_user_primary_email (notmuch_config_t *config)
+{
+ return _config_get (config, &config->user_primary_email, "user", "primary_email");
+}
+
+void
+notmuch_config_set_user_primary_email (notmuch_config_t *config,
+ const char *primary_email)
+{
+ _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
+}
+
const char **
notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
{
&(config->new_tags_length), length);
}
-static void
-_config_set_list (notmuch_config_t *config,
- const char *group, const char *name,
- const char *list[],
- size_t length, const char ***config_var )
+const char **
+notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
{
- g_key_file_set_string_list (config->key_file, group, name, list, length);
- talloc_free (*config_var);
- *config_var = NULL;
+ return _config_get_list (config, "new", "ignore",
+ &(config->new_ignore),
+ &(config->new_ignore_length), length);
}
void
&(config->new_tags));
}
+void
+notmuch_config_set_new_ignore (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "new", "ignore", list, length,
+ &(config->new_ignore));
+}
+
+const char **
+notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
+{
+ return _config_get_list (config, "search", "exclude_tags",
+ &(config->search_exclude_tags),
+ &(config->search_exclude_tags_length), length);
+}
+
+void
+notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "search", "exclude_tags", list, length,
+ &(config->search_exclude_tags));
+}
+
/* Given a configuration item of the form <group>.<key> return the
* component group and key. If any error occurs, print a message on
* stderr and return 1. Otherwise, return 0.
*group = item;
- period = index (item, '.');
+ period = strchr (item, '.');
if (period == NULL || *(period+1) == '\0') {
fprintf (stderr,
"Invalid configuration name: %s\n"
}
static int
-notmuch_config_command_get (void *ctx, char *item)
+notmuch_config_command_get (notmuch_config_t *config, char *item)
{
- notmuch_config_t *config;
-
- config = notmuch_config_open (ctx, NULL, NULL);
- if (config == NULL)
- return 1;
-
if (strcmp(item, "database.path") == 0) {
printf ("%s\n", notmuch_config_get_database_path (config));
} else if (strcmp(item, "user.name") == 0) {
for (i = 0; i < length; i++)
printf ("%s\n", value[i]);
- free (value);
+ g_strfreev (value);
}
- notmuch_config_close (config);
-
return 0;
}
static int
-notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
+notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
{
- notmuch_config_t *config;
char *group, *key;
- int ret;
if (_item_split (item, &group, &key))
return 1;
- config = notmuch_config_open (ctx, NULL, NULL);
- if (config == NULL)
- return 1;
-
/* With only the name of an item, we clear it from the
* configuration file.
*
break;
}
- ret = notmuch_config_save (config);
- notmuch_config_close (config);
+ return notmuch_config_save (config);
+}
- return ret;
+static int
+notmuch_config_command_list (notmuch_config_t *config)
+{
+ char **groups;
+ size_t g, groups_length;
+
+ groups = g_key_file_get_groups (config->key_file, &groups_length);
+ if (groups == NULL)
+ return 1;
+
+ for (g = 0; g < groups_length; g++) {
+ char **keys;
+ size_t k, keys_length;
+
+ keys = g_key_file_get_keys (config->key_file,
+ groups[g], &keys_length, NULL);
+ if (keys == NULL)
+ continue;
+
+ for (k = 0; k < keys_length; k++) {
+ char *value;
+
+ value = g_key_file_get_string (config->key_file,
+ groups[g], keys[k], NULL);
+ if (value != NULL) {
+ printf ("%s.%s=%s\n", groups[g], keys[k], value);
+ free (value);
+ }
+ }
+
+ g_strfreev (keys);
+ }
+
+ g_strfreev (groups);
+
+ return 0;
}
int
-notmuch_config_command (void *ctx, int argc, char *argv[])
+notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
{
+ int ret;
+
argc--; argv++; /* skip subcommand argument */
- if (argc < 2) {
- fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
- return 1;
+ if (argc < 1) {
+ fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp (argv[0], "get") == 0) {
+ if (argc != 2) {
+ fprintf (stderr, "Error: notmuch config get requires exactly "
+ "one argument.\n");
+ return EXIT_FAILURE;
+ }
+ ret = notmuch_config_command_get (config, argv[1]);
+ } else if (strcmp (argv[0], "set") == 0) {
+ if (argc < 2) {
+ fprintf (stderr, "Error: notmuch config set requires at least "
+ "one argument.\n");
+ return EXIT_FAILURE;
+ }
+ ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
+ } else if (strcmp (argv[0], "list") == 0) {
+ ret = notmuch_config_command_list (config);
+ } else {
+ fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
+ argv[0]);
+ return EXIT_FAILURE;
}
- if (strcmp (argv[0], "get") == 0)
- return notmuch_config_command_get (ctx, argv[1]);
- else if (strcmp (argv[0], "set") == 0)
- return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
+ return ret ? EXIT_FAILURE : EXIT_SUCCESS;
- fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
- argv[0]);
- return 1;
}
notmuch_bool_t