X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=notmuch-config.c;h=a564bcae362f6b0b9304ade15e580faa67f84437;hp=3d4d5b9fd1d2f02dedb4bcd8067c8bc0a65021a6;hb=4e2c351c588ad74f4800ca0344232be90387c54a;hpb=42a907992823030f070fc395a174f779998ca6f5 diff --git a/notmuch-config.c b/notmuch-config.c index 3d4d5b9f..a564bcae 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -44,7 +44,14 @@ static const char new_config_comment[] = " 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" @@ -89,13 +96,15 @@ static const char search_config_comment[] = "\n" " The following option is supported here:\n" "\n" - "\tauto_exclude_tags A ;-separated list of tags that will be\n" - "\t excluded from search results by default. Using an excluded tag\n" - "\t in a query will override that exclusion.\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; @@ -104,9 +113,11 @@ struct _notmuch_config { 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 **auto_exclude_tags; - size_t auto_exclude_tags_length; + const char **search_exclude_tags; + size_t search_exclude_tags_length; }; static int @@ -206,9 +217,10 @@ get_username_from_passwd_file (void *ctx) * 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 @@ -222,10 +234,9 @@ get_username_from_passwd_file (void *ctx) 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; @@ -234,9 +245,6 @@ notmuch_config_open (void *ctx, int file_had_maildir_group; int file_had_search_group; - if (is_new_ret) - *is_new_ret = 0; - notmuch_config_t *config = talloc (ctx, notmuch_config_t); if (config == NULL) { fprintf (stderr, "Out of memory.\n"); @@ -256,6 +264,7 @@ notmuch_config_open (void *ctx, config->key_file = g_key_file_new (); + config->is_new = FALSE; config->database_path = NULL; config->user_name = NULL; config->user_primary_email = NULL; @@ -263,26 +272,33 @@ notmuch_config_open (void *ctx, 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->auto_exclude_tags = NULL; - config->auto_exclude_tags_length = 0; + 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 { @@ -313,14 +329,22 @@ notmuch_config_open (void *ctx, 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); } @@ -360,9 +384,17 @@ notmuch_config_open (void *ctx, notmuch_config_set_new_tags (config, tags, 2); } - if (notmuch_config_get_auto_exclude_tags (config, &tmp) == NULL) { - const char *tags[] = { "deleted", "spam" }; - notmuch_config_set_auto_exclude_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; @@ -377,43 +409,29 @@ notmuch_config_open (void *ctx, /* 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 (! file_had_search_group) { + if (! file_had_search_group) g_key_file_set_comment (config->key_file, "search", NULL, search_config_comment, NULL); - } - - if (is_new_ret) - *is_new_ret = is_new; return config; } @@ -441,7 +459,7 @@ int 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); @@ -450,100 +468,74 @@ notmuch_config_save (notmuch_config_t *config) 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) -{ - g_key_file_set_string (config->key_file, - "database", "path", database_path); - - talloc_free (config->database_path); - config->database_path = NULL; -} - -const char * -notmuch_config_get_user_name (notmuch_config_t *config) -{ - 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); - } - } - - return config->user_name; -} - -void -notmuch_config_set_user_name (notmuch_config_t *config, - const char *user_name) +notmuch_bool_t +notmuch_config_is_new (notmuch_config_t *config) { - g_key_file_set_string (config->key_file, - "user", "name", user_name); - - talloc_free (config->user_name); - config->user_name = NULL; + return config->is_new; } -const char * -notmuch_config_get_user_primary_email (notmuch_config_t *config) +static const char * +_config_get (notmuch_config_t *config, char **field, + const char *group, const char *key) { - 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); + /* 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_primary_email; + return *field; } -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 ** @@ -553,6 +545,7 @@ _config_get_list (notmuch_config_t *config, { 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, @@ -577,6 +570,58 @@ _config_get_list (notmuch_config_t *config, 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) { @@ -593,15 +638,12 @@ notmuch_config_get_new_tags (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 @@ -622,21 +664,30 @@ notmuch_config_set_new_tags (notmuch_config_t *config, &(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_auto_exclude_tags (notmuch_config_t *config, size_t *length) +notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length) { - return _config_get_list (config, "search", "auto_exclude_tags", - &(config->auto_exclude_tags), - &(config->auto_exclude_tags_length), length); + return _config_get_list (config, "search", "exclude_tags", + &(config->search_exclude_tags), + &(config->search_exclude_tags_length), length); } void -notmuch_config_set_auto_exclude_tags (notmuch_config_t *config, +notmuch_config_set_search_exclude_tags (notmuch_config_t *config, const char *list[], size_t length) { - _config_set_list (config, "search", "auto_exclude_tags", list, length, - &(config->auto_exclude_tags)); + _config_set_list (config, "search", "exclude_tags", list, length, + &(config->search_exclude_tags)); } /* Given a configuration item of the form . return the @@ -652,7 +703,7 @@ _item_split (char *item, char **group, char **key) *group = item; - period = index (item, '.'); + period = strchr (item, '.'); if (period == NULL || *(period+1) == '\0') { fprintf (stderr, "Invalid configuration name: %s\n" @@ -667,14 +718,8 @@ _item_split (char *item, char **group, char **key) } 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) { @@ -715,28 +760,20 @@ notmuch_config_command_get (void *ctx, char *item) 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. * @@ -757,30 +794,83 @@ notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[]) break; } - ret = notmuch_config_save (config); - notmuch_config_close (config); + return notmuch_config_save (config); +} + +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 ret; + 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