unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: David Bremner <david@tethera.net>
To: notmuch@notmuchmail.org
Cc: David Bremner <david@tethera.net>
Subject: [PATCH 16/28] CLI/config: migrate notmuch_config_open to new config
Date: Tue,  6 Apr 2021 22:55:18 -0300	[thread overview]
Message-ID: <20210407015530.2964017-17-david@tethera.net> (raw)
In-Reply-To: <20210407015530.2964017-1-david@tethera.net>

notmuch_config_open will be preserved in the medium term for use by
the commands that are manipulating the config file directly (config
and setup)
---
 lib/config.cc                                 |   5 +-
 notmuch-client.h                              |   2 +-
 notmuch-config.c                              | 371 +++++-------------
 notmuch.c                                     |   2 +-
 test/T030-config.sh                           |   2 +-
 test/T590-libconfig.sh                        |  12 +-
 .../config-with-comments                      |   2 -
 7 files changed, 106 insertions(+), 290 deletions(-)

diff --git a/lib/config.cc b/lib/config.cc
index 36f242f2..26280842 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -598,7 +598,7 @@ _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
     case NOTMUCH_CONFIG_EXCLUDE_TAGS:
 	return "";
     case NOTMUCH_CONFIG_NEW_TAGS:
-	return "inbox;unread";
+	return "unread;inbox";
     case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
 	return "true";
     case NOTMUCH_CONFIG_USER_NAME:
@@ -615,9 +615,10 @@ _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
 	else
 	    email = _get_email_from_passwd_file (notmuch);
 	return email;
+    case NOTMUCH_CONFIG_NEW_IGNORE:
+	return "";
     case NOTMUCH_CONFIG_HOOK_DIR:
     case NOTMUCH_CONFIG_BACKUP_DIR:
-    case NOTMUCH_CONFIG_NEW_IGNORE:
     case NOTMUCH_CONFIG_OTHER_EMAIL:
 	return NULL;
     default:
diff --git a/notmuch-client.h b/notmuch-client.h
index b560e3ed..7911c20c 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -276,7 +276,7 @@ typedef enum {
 } notmuch_command_mode_t;
 
 notmuch_config_t *
-notmuch_config_open (void *ctx,
+notmuch_config_open (notmuch_database_t *notmuch,
 		     const char *filename,
 		     notmuch_command_mode_t config_mode);
 
diff --git a/notmuch-config.c b/notmuch-config.c
index ae315cb7..0c618d51 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -31,87 +31,88 @@ static const char toplevel_config_comment[] =
     "\n"
     " For more information about notmuch, see https://notmuchmail.org";
 
-static const char database_config_comment[] =
-    " Database configuration\n"
-    "\n"
-    " The only value supported here is 'path' which should be the top-level\n"
-    " directory where your mail currently exists and to where mail will be\n"
-    " delivered in the future. Files should be individual email messages.\n"
-    " Notmuch will store its database within a sub-directory of the path\n"
-    " configured here named \".notmuch\".\n";
-
-static const char new_config_comment[] =
-    " Configuration for \"notmuch new\"\n"
-    "\n"
-    " 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"
-    "\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"
-    "\n"
-    " Here is where you can let notmuch know how you would like to be\n"
-    " addressed. Valid settings are\n"
-    "\n"
-    "\tname		Your full name.\n"
-    "\tprimary_email	Your primary email address.\n"
-    "\tother_email	A list (separated by ';') of other email addresses\n"
-    "\t		at which you receive email.\n"
-    "\n"
-    " Notmuch will use the various email addresses configured here when\n"
-    " formatting replies. It will avoid including your own addresses in the\n"
-    " recipient list of replies, and will set the From address based on the\n"
-    " address to which the original email was addressed.\n";
-
-static const char maildir_config_comment[] =
-    " Maildir compatibility configuration\n"
-    "\n"
-    " The following option is supported here:\n"
-    "\n"
-    "\tsynchronize_flags      Valid values are true and false.\n"
-    "\n"
-    "\tIf true, then the following maildir flags (in message filenames)\n"
-    "\twill be synchronized with the corresponding notmuch tags:\n"
-    "\n"
-    "\t\tFlag	Tag\n"
-    "\t\t----	-------\n"
-    "\t\tD	draft\n"
-    "\t\tF	flagged\n"
-    "\t\tP	passed\n"
-    "\t\tR	replied\n"
-    "\t\tS	unread (added when 'S' flag is not present)\n"
-    "\n"
-    "\tThe \"notmuch new\" command will notice flag changes in filenames\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";
-
-static const char crypto_config_comment[] =
-    " Cryptography related configuration\n"
-    "\n"
-    " The following old option is now ignored:\n"
-    "\n"
-    "\tgpgpath\n"
-    "\t\tThis option was used by older builds of notmuch to choose\n"
-    "\t\tthe version of gpg to use.\n"
-    "\t\tSetting $PATH is a better approach.\n";
+struct config_group {
+    const char *group_name;
+    const char *comment;
+} group_comment_table [] = {
+    {
+	"database",
+	" Database configuration\n"
+	"\n"
+	" The only value supported here is 'path' which should be the top-level\n"
+	" directory where your mail currently exists and to where mail will be\n"
+	" delivered in the future. Files should be individual email messages.\n"
+	" Notmuch will store its database within a sub-directory of the path\n"
+	" configured here named \".notmuch\".\n"
+    },
+    {
+	"user",
+	" User configuration\n"
+	"\n"
+	" Here is where you can let notmuch know how you would like to be\n"
+	" addressed. Valid settings are\n"
+	"\n"
+	"\tname		Your full name.\n"
+	"\tprimary_email	Your primary email address.\n"
+	"\tother_email	A list (separated by ';') of other email addresses\n"
+	"\t		at which you receive email.\n"
+	"\n"
+	" Notmuch will use the various email addresses configured here when\n"
+	" formatting replies. It will avoid including your own addresses in the\n"
+	" recipient list of replies, and will set the From address based on the\n"
+	" address to which the original email was addressed.\n"
+    },
+    {
+	"new",
+	" Configuration for \"notmuch new\"\n"
+	"\n"
+	" 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"
+	"\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"
+    },
+    {
+	"search",
+	" 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"
+    },
+    {
+	"maildir",
+	" Maildir compatibility configuration\n"
+	"\n"
+	" The following option is supported here:\n"
+	"\n"
+	"\tsynchronize_flags      Valid values are true and false.\n"
+	"\n"
+	"\tIf true, then the following maildir flags (in message filenames)\n"
+	"\twill be synchronized with the corresponding notmuch tags:\n"
+	"\n"
+	"\t\tFlag	Tag\n"
+	"\t\t----	-------\n"
+	"\t\tD	draft\n"
+	"\t\tF	flagged\n"
+	"\t\tP	passed\n"
+	"\t\tR	replied\n"
+	"\t\tS	unread (added when 'S' flag is not present)\n"
+	"\n"
+	"\tThe \"notmuch new\" command will notice flag changes in filenames\n"
+	"\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
+	"\tcommands will notice tag changes and update flags in filenames\n"
+    },
+};
 
 struct _notmuch_config {
     char *filename;
@@ -141,69 +142,6 @@ notmuch_config_destructor (notmuch_config_t *config)
     return 0;
 }
 
-static char *
-get_name_from_passwd_file (void *ctx)
-{
-    long pw_buf_size;
-    char *pw_buf;
-    struct passwd passwd, *ignored;
-    char *name;
-    int e;
-
-    pw_buf_size = sysconf (_SC_GETPW_R_SIZE_MAX);
-    if (pw_buf_size == -1) pw_buf_size = 64;
-    pw_buf = talloc_size (ctx, pw_buf_size);
-
-    while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
-			    pw_buf_size, &ignored)) == ERANGE) {
-	pw_buf_size = pw_buf_size * 2;
-	pw_buf = talloc_zero_size (ctx, pw_buf_size);
-    }
-
-    if (e == 0) {
-	char *comma = strchr (passwd.pw_gecos, ',');
-	if (comma)
-	    name = talloc_strndup (ctx, passwd.pw_gecos,
-				   comma - passwd.pw_gecos);
-	else
-	    name = talloc_strdup (ctx, passwd.pw_gecos);
-    } else {
-	name = talloc_strdup (ctx, "");
-    }
-
-    talloc_free (pw_buf);
-
-    return name;
-}
-
-static char *
-get_username_from_passwd_file (void *ctx)
-{
-    long pw_buf_size;
-    char *pw_buf;
-    struct passwd passwd, *ignored;
-    char *name;
-    int e;
-
-    pw_buf_size = sysconf (_SC_GETPW_R_SIZE_MAX);
-    if (pw_buf_size == -1) pw_buf_size = 64;
-    pw_buf = talloc_zero_size (ctx, pw_buf_size);
-
-    while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
-			    pw_buf_size, &ignored)) == ERANGE) {
-	pw_buf_size = pw_buf_size * 2;
-	pw_buf = talloc_zero_size (ctx, pw_buf_size);
-    }
-
-    if (e == 0)
-	name = talloc_strdup (ctx, passwd.pw_name);
-    else
-	name = talloc_strdup (ctx, "");
-
-    talloc_free (pw_buf);
-
-    return name;
-}
 
 static bool
 get_config_from_file (notmuch_config_t *config, bool create_new)
@@ -322,21 +260,13 @@ get_config_from_file (notmuch_config_t *config, bool create_new)
  *	user in editing the file directly.
  */
 notmuch_config_t *
-notmuch_config_open (void *ctx,
+notmuch_config_open (notmuch_database_t *notmuch,
 		     const char *filename,
 		     notmuch_command_mode_t config_mode)
 {
-    GError *error = NULL;
-    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;
-    int file_had_search_group;
-    int file_had_crypto_group;
 
-    notmuch_config_t *config = talloc_zero (ctx, notmuch_config_t);
+    notmuch_config_t *config = talloc_zero (notmuch, notmuch_config_t);
 
     if (config == NULL) {
 	fprintf (stderr, "Out of memory.\n");
@@ -368,133 +298,20 @@ 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.
-     *
-     * It would be convenient to just add those comments now, but
-     * apparently g_key_file will clear any comments when keys are
-     * added later that create the groups. So we have to check for the
-     * groups now, but add the comments only after setting all of our
-     * values.
-     */
-    file_had_database_group = g_key_file_has_group (config->key_file,
-						    "database");
-    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");
-    file_had_crypto_group = g_key_file_has_group (config->key_file, "crypto");
-
-    if (notmuch_config_get_database_path (config) == NULL) {
-	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 = 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);
-    }
-
-    if (notmuch_config_get_user_primary_email (config) == NULL) {
-	char *email = getenv ("EMAIL");
-	if (email) {
-	    notmuch_config_set_user_primary_email (config, email);
-	} else {
-	    char hostname[256];
-	    struct hostent *hostent;
-	    const char *domainname;
-
-	    char *username = get_username_from_passwd_file (config);
-
-	    gethostname (hostname, 256);
-	    hostname[255] = '\0';
-
-	    hostent = gethostbyname (hostname);
-	    if (hostent && (domainname = strchr (hostent->h_name, '.')))
-		domainname += 1;
-	    else
-		domainname = "(none)";
-
-	    email = talloc_asprintf (config, "%s@%s.%s",
-				     username, hostname, domainname);
-
-	    notmuch_config_set_user_primary_email (config, email);
-
-	    talloc_free (username);
-	    talloc_free (email);
-	}
-    }
-
-    if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
-	const char *tags[] = { "unread", "inbox" };
-	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,
-				"maildir", "synchronize_flags", &error);
-    if (error) {
-	notmuch_config_set_maildir_synchronize_flags (config, true);
-	g_error_free (error);
-    }
-
-    /* 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 (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)
-	g_key_file_set_comment (config->key_file, "search", NULL,
-				search_config_comment, NULL);
-
-    if (! file_had_crypto_group)
-	g_key_file_set_comment (config->key_file, "crypto", NULL,
-				crypto_config_comment, NULL);
-
+    for (size_t i = 0; i < ARRAY_SIZE (group_comment_table); i++) {
+	const char *name = group_comment_table[i].group_name;
+	if (! g_key_file_has_group (config->key_file,  name)) {
+	    /* Force group to exist before adding comment */
+	    g_key_file_set_value (config->key_file, name, "dummy_key", "dummy_val");
+	    g_key_file_remove_key (config->key_file, name, "dummy_key", NULL);
+	    g_key_file_set_comment (config->key_file, name, NULL,
+				    group_comment_table[i].comment, NULL);
+	}
+    }
     return config;
 }
 
diff --git a/notmuch.c b/notmuch.c
index 0f387b00..4132e561 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -588,7 +588,7 @@ main (int argc, char *argv[])
     }
 
     if (command->mode & NOTMUCH_COMMAND_CONFIG_OPEN) {
-	config = notmuch_config_open (local, config_file_name, command->mode);
+	config = notmuch_config_open (notmuch, config_file_name, command->mode);
 	if (! config) {
 	    ret = EXIT_FAILURE;
 	    goto DONE;
diff --git a/test/T030-config.sh b/test/T030-config.sh
index ffbd7681..c87d35b6 100755
--- a/test/T030-config.sh
+++ b/test/T030-config.sh
@@ -57,7 +57,7 @@ foo.list=this;is another;list value;
 foo.string=this is another string value
 maildir.synchronize_flags=true
 new.ignore=
-new.tags=unread;inbox;
+new.tags=unread;inbox
 search.exclude_tags=
 user.name=Notmuch Test Suite
 user.other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
index 292778d5..51dd29c8 100755
--- a/test/T590-libconfig.sh
+++ b/test/T590-libconfig.sh
@@ -394,8 +394,8 @@ MAIL_DIR
 MAIL_DIR/.notmuch/hooks
 MAIL_DIR/.notmuch/backups
 
-inbox;unread
-NULL
+unread;inbox
+
 true
 USERNAME@FQDN
 NULL
@@ -705,7 +705,7 @@ MAIL_DIR
 MAIL_DIR/.notmuch/hooks
 MAIL_DIR/.notmuch/backups
 foo;bar;fub
-unread;inbox;
+unread;inbox
 sekrit_junk
 true
 test_suite@notmuchmail.org
@@ -736,8 +736,8 @@ MAIL_DIR
 MAIL_DIR/.notmuch/hooks
 MAIL_DIR/.notmuch/backups
 
-inbox;unread
-NULL
+unread;inbox
+
 true
 USERNAME@FQDN
 NULL
@@ -815,7 +815,7 @@ database.path MAIL_DIR
 key with spaces value, with, spaces!
 maildir.synchronize_flags true
 new.ignore sekrit_junk
-new.tags unread;inbox;
+new.tags unread;inbox
 search.exclude_tags foo;bar;fub
 test.key1 testvalue1
 test.key2 testvalue2
diff --git a/test/setup.expected-output/config-with-comments b/test/setup.expected-output/config-with-comments
index d9f796f2..56c628e5 100644
--- a/test/setup.expected-output/config-with-comments
+++ b/test/setup.expected-output/config-with-comments
@@ -49,7 +49,6 @@ other_email=another.suite@example.com;
 #
 [new]
 tags=foo;bar;
-ignore=
 
 # Search configuration
 #
@@ -85,4 +84,3 @@ exclude_tags=baz;
 #	commands will notice tag changes and update flags in filenames
 #
 [maildir]
-synchronize_flags=true
-- 
2.30.2

  parent reply	other threads:[~2021-04-07  1:57 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-07  1:55 v2 convert remaining CLI to new configuration David Bremner
2021-04-07  1:55 ` [PATCH 01/28] lib/open: fix leaks calling _trial_open David Bremner
2021-04-07  1:55 ` [PATCH 02/28] lib: add missing status strings David Bremner
2021-04-07  1:55 ` [PATCH 03/28] test: convert random-corpus to use n_d_open_with_config David Bremner
2021-04-07  1:55 ` [PATCH 04/28] lib/open: pull _load_key_file out of _choose_database_path David Bremner
2021-04-07  1:55 ` [PATCH 05/28] lib: provide notmuch_database_load_config David Bremner
2021-04-07  1:55 ` [PATCH 06/28] lib/config: add notmuch_config_get_values_string David Bremner
2021-04-07  1:55 ` [PATCH 07/28] lib/config: add config_pairs iterators David Bremner
2021-04-07  1:55 ` [PATCH 08/28] lib/config: set defaults for user full name David Bremner
2021-04-07  1:55 ` [PATCH 09/28] lib/config: set default for primary user email David Bremner
2021-04-07  1:55 ` [PATCH 10/28] lib/open: canonicalize relative path read from config file David Bremner
2021-04-07  1:55 ` [PATCH 11/28] CLI: load merged config at top level David Bremner
2021-04-07  1:55 ` [PATCH 12/28] CLI/config: use merged config for "config get" David Bremner
2021-04-07  1:55 ` [PATCH 13/28] test/setup: check file output instead of notmuch config list David Bremner
2021-04-07  1:55 ` [PATCH 14/28] CLI/setup: switch to new configuration framework David Bremner
2021-04-07  1:55 ` [PATCH 15/28] CLI/config: switch "notmuch config list" to merged config David Bremner
2021-04-07  1:55 ` David Bremner [this message]
2021-04-07  1:55 ` [PATCH 17/28] CLI/config: use notmuch_database_reopen David Bremner
2021-04-07  1:55 ` [PATCH 18/28] CLI/notmuch: switch notmuch_command to notmuch_config_get David Bremner
2021-04-07  1:55 ` [PATCH 19/28] CLI/config: drop obsolete notmuch_config_get_* David Bremner
2021-04-07  1:55 ` [PATCH 20/28] CLI/config: drop cached data from notmuch_config_t David Bremner
2021-04-07  1:55 ` [PATCH 21/28] CLI/config: default to storing all config in external files David Bremner
2021-04-07  1:55 ` [PATCH 22/28] lib: add NOTMUCH_STATUS_NO_DATABASE David Bremner
2021-04-07  1:55 ` [PATCH 23/28] CLI+lib: detect missing database in split configurations David Bremner
2021-04-07  1:55 ` [PATCH 24/28] lib: provide notmuch_config_path David Bremner
2021-04-07  1:55 ` [PATCH 25/28] CLI/config: support set/get with split configuration David Bremner
2021-04-07  1:55 ` [PATCH 26/28] CLI/config: remove calls to notmuch_config_open from top level David Bremner
2021-04-07  1:55 ` [PATCH 27/28] CLI: drop notmuch_config_t from subcommand interface David Bremner
2021-04-07  1:55 ` [PATCH 28/28] CLI: rename notmuch_config_t to notmuch_conffile_t David Bremner
2021-04-10 12:12 ` v2 convert remaining CLI to new configuration David Bremner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210407015530.2964017-17-david@tethera.net \
    --to=david@tethera.net \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).