Testsuite: perl version oddity
[exim.git] / src / src / readconf.c
index 9b3eef3673f1d798438f21c7597d7f05883a5824..d87a56f3d6e5562cc44f313ace3366d0318ba38a 100644 (file)
@@ -2,8 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Functions for reading the configuration file, and for displaying
 overall configuration values. Thanks to Brian Candler for the original
@@ -19,7 +21,7 @@ implementation of the conditional .ifdef etc. */
 
 
 static uschar * syslog_facility_str;
-static void fn_smtp_receive_timeout(const uschar *, const uschar *);
+static void fn_smtp_receive_timeout(const uschar *, const uschar *, unsigned);
 
 /*************************************************
 *           Main configuration options           *
@@ -29,356 +31,378 @@ static void fn_smtp_receive_timeout(const uschar *, const uschar *);
 must be in alphabetic order because it is searched by binary chop. */
 
 static optionlist optionlist_config[] = {
-  { "*set_exim_group",          opt_bool|opt_hidden, &exim_gid_set },
-  { "*set_exim_user",           opt_bool|opt_hidden, &exim_uid_set },
-  { "*set_system_filter_group", opt_bool|opt_hidden, &system_filter_gid_set },
-  { "*set_system_filter_user",  opt_bool|opt_hidden, &system_filter_uid_set },
-  { "accept_8bitmime",          opt_bool,        &accept_8bitmime },
-  { "acl_not_smtp",             opt_stringptr,   &acl_not_smtp },
+  { "*set_exim_group",          opt_bool|opt_hidden, {&exim_gid_set} },
+  { "*set_exim_user",           opt_bool|opt_hidden, {&exim_uid_set} },
+  { "*set_system_filter_group", opt_bool|opt_hidden, {&system_filter_gid_set} },
+  { "*set_system_filter_user",  opt_bool|opt_hidden, {&system_filter_uid_set} },
+  { "accept_8bitmime",          opt_bool,        {&accept_8bitmime} },
+  { "acl_not_smtp",             opt_stringptr,   {&acl_not_smtp} },
 #ifdef WITH_CONTENT_SCAN
-  { "acl_not_smtp_mime",        opt_stringptr,   &acl_not_smtp_mime },
+  { "acl_not_smtp_mime",        opt_stringptr,   {&acl_not_smtp_mime} },
 #endif
-  { "acl_not_smtp_start",       opt_stringptr,   &acl_not_smtp_start },
-  { "acl_smtp_auth",            opt_stringptr,   &acl_smtp_auth },
-  { "acl_smtp_connect",         opt_stringptr,   &acl_smtp_connect },
-  { "acl_smtp_data",            opt_stringptr,   &acl_smtp_data },
+  { "acl_not_smtp_start",       opt_stringptr,   {&acl_not_smtp_start} },
+  { "acl_smtp_auth",            opt_stringptr,   {&acl_smtp_auth} },
+  { "acl_smtp_connect",         opt_stringptr,   {&acl_smtp_connect} },
+  { "acl_smtp_data",            opt_stringptr,   {&acl_smtp_data} },
 #ifndef DISABLE_PRDR
-  { "acl_smtp_data_prdr",       opt_stringptr,   &acl_smtp_data_prdr },
+  { "acl_smtp_data_prdr",       opt_stringptr,   {&acl_smtp_data_prdr} },
 #endif
 #ifndef DISABLE_DKIM
-  { "acl_smtp_dkim",            opt_stringptr,   &acl_smtp_dkim },
+  { "acl_smtp_dkim",            opt_stringptr,   {&acl_smtp_dkim} },
 #endif
-  { "acl_smtp_etrn",            opt_stringptr,   &acl_smtp_etrn },
-  { "acl_smtp_expn",            opt_stringptr,   &acl_smtp_expn },
-  { "acl_smtp_helo",            opt_stringptr,   &acl_smtp_helo },
-  { "acl_smtp_mail",            opt_stringptr,   &acl_smtp_mail },
-  { "acl_smtp_mailauth",        opt_stringptr,   &acl_smtp_mailauth },
+  { "acl_smtp_etrn",            opt_stringptr,   {&acl_smtp_etrn} },
+  { "acl_smtp_expn",            opt_stringptr,   {&acl_smtp_expn} },
+  { "acl_smtp_helo",            opt_stringptr,   {&acl_smtp_helo} },
+  { "acl_smtp_mail",            opt_stringptr,   {&acl_smtp_mail} },
+  { "acl_smtp_mailauth",        opt_stringptr,   {&acl_smtp_mailauth} },
 #ifdef WITH_CONTENT_SCAN
-  { "acl_smtp_mime",            opt_stringptr,   &acl_smtp_mime },
+  { "acl_smtp_mime",            opt_stringptr,   {&acl_smtp_mime} },
 #endif
-  { "acl_smtp_notquit",         opt_stringptr,   &acl_smtp_notquit },
-  { "acl_smtp_predata",         opt_stringptr,   &acl_smtp_predata },
-  { "acl_smtp_quit",            opt_stringptr,   &acl_smtp_quit },
-  { "acl_smtp_rcpt",            opt_stringptr,   &acl_smtp_rcpt },
-#ifdef SUPPORT_TLS
-  { "acl_smtp_starttls",        opt_stringptr,   &acl_smtp_starttls },
+  { "acl_smtp_notquit",         opt_stringptr,   {&acl_smtp_notquit} },
+  { "acl_smtp_predata",         opt_stringptr,   {&acl_smtp_predata} },
+  { "acl_smtp_quit",            opt_stringptr,   {&acl_smtp_quit} },
+  { "acl_smtp_rcpt",            opt_stringptr,   {&acl_smtp_rcpt} },
+#ifndef DISABLE_TLS
+  { "acl_smtp_starttls",        opt_stringptr,   {&acl_smtp_starttls} },
 #endif
-  { "acl_smtp_vrfy",            opt_stringptr,   &acl_smtp_vrfy },
-  { "add_environment",          opt_stringptr,   &add_environment },
-  { "admin_groups",             opt_gidlist,     &admin_groups },
-  { "allow_domain_literals",    opt_bool,        &allow_domain_literals },
-  { "allow_mx_to_ip",           opt_bool,        &allow_mx_to_ip },
-  { "allow_utf8_domains",       opt_bool,        &allow_utf8_domains },
-  { "auth_advertise_hosts",     opt_stringptr,   &auth_advertise_hosts },
-  { "auto_thaw",                opt_time,        &auto_thaw },
+  { "acl_smtp_vrfy",            opt_stringptr,   {&acl_smtp_vrfy} },
+  { "add_environment",          opt_stringptr,   {&add_environment} },
+  { "admin_groups",             opt_gidlist,     {&admin_groups} },
+  { "allow_domain_literals",    opt_bool,        {&allow_domain_literals} },
+  { "allow_mx_to_ip",           opt_bool,        {&allow_mx_to_ip} },
+  { "allow_utf8_domains",       opt_bool,        {&allow_utf8_domains} },
+  { "auth_advertise_hosts",     opt_stringptr,   {&auth_advertise_hosts} },
+  { "auto_thaw",                opt_time,        {&auto_thaw} },
 #ifdef WITH_CONTENT_SCAN
-  { "av_scanner",               opt_stringptr,   &av_scanner },
+  { "av_scanner",               opt_stringptr,   {&av_scanner} },
 #endif
-  { "bi_command",               opt_stringptr,   &bi_command },
+  { "bi_command",               opt_stringptr,   {&bi_command} },
 #ifdef EXPERIMENTAL_BRIGHTMAIL
-  { "bmi_config_file",          opt_stringptr,   &bmi_config_file },
+  { "bmi_config_file",          opt_stringptr,   {&bmi_config_file} },
 #endif
-  { "bounce_message_file",      opt_stringptr,   &bounce_message_file },
-  { "bounce_message_text",      opt_stringptr,   &bounce_message_text },
-  { "bounce_return_body",       opt_bool,        &bounce_return_body },
-  { "bounce_return_linesize_limit", opt_mkint,   &bounce_return_linesize_limit },
-  { "bounce_return_message",    opt_bool,        &bounce_return_message },
-  { "bounce_return_size_limit", opt_mkint,       &bounce_return_size_limit },
-  { "bounce_sender_authentication",opt_stringptr,&bounce_sender_authentication },
-  { "callout_domain_negative_expire", opt_time,  &callout_cache_domain_negative_expire },
-  { "callout_domain_positive_expire", opt_time,  &callout_cache_domain_positive_expire },
-  { "callout_negative_expire",  opt_time,        &callout_cache_negative_expire },
-  { "callout_positive_expire",  opt_time,        &callout_cache_positive_expire },
-  { "callout_random_local_part",opt_stringptr,   &callout_random_local_part },
-  { "check_log_inodes",         opt_int,         &check_log_inodes },
-  { "check_log_space",          opt_Kint,        &check_log_space },
-  { "check_rfc2047_length",     opt_bool,        &check_rfc2047_length },
-  { "check_spool_inodes",       opt_int,         &check_spool_inodes },
-  { "check_spool_space",        opt_Kint,        &check_spool_space },
-  { "chunking_advertise_hosts", opt_stringptr,  &chunking_advertise_hosts },
-  { "commandline_checks_require_admin", opt_bool,&commandline_checks_require_admin },
-  { "daemon_smtp_port",         opt_stringptr|opt_hidden, &daemon_smtp_port },
-  { "daemon_smtp_ports",        opt_stringptr,   &daemon_smtp_port },
-  { "daemon_startup_retries",   opt_int,         &daemon_startup_retries },
-  { "daemon_startup_sleep",     opt_time,        &daemon_startup_sleep },
+  { "bounce_message_file",      opt_stringptr,   {&bounce_message_file} },
+  { "bounce_message_text",      opt_stringptr,   {&bounce_message_text} },
+  { "bounce_return_body",       opt_bool,        {&bounce_return_body} },
+  { "bounce_return_linesize_limit", opt_mkint,   {&bounce_return_linesize_limit} },
+  { "bounce_return_message",    opt_bool,        {&bounce_return_message} },
+  { "bounce_return_size_limit", opt_mkint,       {&bounce_return_size_limit} },
+  { "bounce_sender_authentication",opt_stringptr,{&bounce_sender_authentication} },
+  { "callout_domain_negative_expire", opt_time,  {&callout_cache_domain_negative_expire} },
+  { "callout_domain_positive_expire", opt_time,  {&callout_cache_domain_positive_expire} },
+  { "callout_negative_expire",  opt_time,        {&callout_cache_negative_expire} },
+  { "callout_positive_expire",  opt_time,        {&callout_cache_positive_expire} },
+  { "callout_random_local_part",opt_stringptr,   {&callout_random_local_part} },
+  { "check_log_inodes",         opt_int,         {&check_log_inodes} },
+  { "check_log_space",          opt_Kint,        {&check_log_space} },
+  { "check_rfc2047_length",     opt_bool,        {&check_rfc2047_length} },
+  { "check_spool_inodes",       opt_int,         {&check_spool_inodes} },
+  { "check_spool_space",        opt_Kint,        {&check_spool_space} },
+  { "chunking_advertise_hosts", opt_stringptr,  {&chunking_advertise_hosts} },
+  { "commandline_checks_require_admin", opt_bool,{&commandline_checks_require_admin} },
+  { "daemon_smtp_port",         opt_stringptr|opt_hidden, {&daemon_smtp_port} },
+  { "daemon_smtp_ports",        opt_stringptr,   {&daemon_smtp_port} },
+  { "daemon_startup_retries",   opt_int,         {&daemon_startup_retries} },
+  { "daemon_startup_sleep",     opt_time,        {&daemon_startup_sleep} },
 #ifdef EXPERIMENTAL_DCC
-  { "dcc_direct_add_header",    opt_bool,        &dcc_direct_add_header },
-  { "dccifd_address",           opt_stringptr,   &dccifd_address },
-  { "dccifd_options",           opt_stringptr,   &dccifd_options },
+  { "dcc_direct_add_header",    opt_bool,        {&dcc_direct_add_header} },
+  { "dccifd_address",           opt_stringptr,   {&dccifd_address} },
+  { "dccifd_options",           opt_stringptr,   {&dccifd_options} },
 #endif
-  { "debug_store",              opt_bool,        &debug_store },
-  { "delay_warning",            opt_timelist,    &delay_warning },
-  { "delay_warning_condition",  opt_stringptr,   &delay_warning_condition },
-  { "deliver_drop_privilege",   opt_bool,        &deliver_drop_privilege },
-  { "deliver_queue_load_max",   opt_fixed,       &deliver_queue_load_max },
-  { "delivery_date_remove",     opt_bool,        &delivery_date_remove },
+  { "debug_store",              opt_bool,        {&debug_store} },
+  { "delay_warning",            opt_timelist,    {&delay_warning} },
+  { "delay_warning_condition",  opt_stringptr,   {&delay_warning_condition} },
+  { "deliver_drop_privilege",   opt_bool,        {&deliver_drop_privilege} },
+  { "deliver_queue_load_max",   opt_fixed,       {&deliver_queue_load_max} },
+  { "delivery_date_remove",     opt_bool,        {&delivery_date_remove} },
 #ifdef ENABLE_DISABLE_FSYNC
-  { "disable_fsync",            opt_bool,        &disable_fsync },
+  { "disable_fsync",            opt_bool,        {&disable_fsync} },
 #endif
-  { "disable_ipv6",             opt_bool,        &disable_ipv6 },
+  { "disable_ipv6",             opt_bool,        {&disable_ipv6} },
 #ifndef DISABLE_DKIM
-  { "dkim_verify_signers",      opt_stringptr,   &dkim_verify_signers },
+  { "dkim_verify_hashes",       opt_stringptr,   {&dkim_verify_hashes} },
+  { "dkim_verify_keytypes",     opt_stringptr,   {&dkim_verify_keytypes} },
+  { "dkim_verify_min_keysizes", opt_stringptr,   {&dkim_verify_min_keysizes} },
+  { "dkim_verify_minimal",      opt_bool,        {&dkim_verify_minimal} },
+  { "dkim_verify_signers",      opt_stringptr,   {&dkim_verify_signers} },
 #endif
-#ifdef EXPERIMENTAL_DMARC
-  { "dmarc_forensic_sender",    opt_stringptr,   &dmarc_forensic_sender },
-  { "dmarc_history_file",       opt_stringptr,   &dmarc_history_file },
-  { "dmarc_tld_file",           opt_stringptr,   &dmarc_tld_file },
+#ifdef SUPPORT_DMARC
+  { "dmarc_forensic_sender",    opt_stringptr,   {&dmarc_forensic_sender} },
+  { "dmarc_history_file",       opt_stringptr,   {&dmarc_history_file} },
+  { "dmarc_tld_file",           opt_stringptr,   {&dmarc_tld_file} },
 #endif
-  { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
-  { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
-  { "dns_cname_loops",         opt_int,         &dns_cname_loops },
-  { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
-  { "dns_csa_use_reverse",      opt_bool,        &dns_csa_use_reverse },
-  { "dns_dnssec_ok",            opt_int,         &dns_dnssec_ok },
-  { "dns_ipv4_lookup",          opt_stringptr,   &dns_ipv4_lookup },
-  { "dns_retrans",              opt_time,        &dns_retrans },
-  { "dns_retry",                opt_int,         &dns_retry },
-  { "dns_trust_aa",             opt_stringptr,   &dns_trust_aa },
-  { "dns_use_edns0",            opt_int,         &dns_use_edns0 },
+  { "dns_again_means_nonexist", opt_stringptr,   {&dns_again_means_nonexist} },
+  { "dns_check_names_pattern",  opt_stringptr,   {&check_dns_names_pattern} },
+  { "dns_cname_loops",         opt_int,         {&dns_cname_loops} },
+  { "dns_csa_search_limit",     opt_int,         {&dns_csa_search_limit} },
+  { "dns_csa_use_reverse",      opt_bool,        {&dns_csa_use_reverse} },
+  { "dns_dnssec_ok",            opt_int,         {&dns_dnssec_ok} },
+  { "dns_ipv4_lookup",          opt_stringptr,   {&dns_ipv4_lookup} },
+  { "dns_retrans",              opt_time,        {&dns_retrans} },
+  { "dns_retry",                opt_int,         {&dns_retry} },
+  { "dns_trust_aa",             opt_stringptr,   {&dns_trust_aa} },
+  { "dns_use_edns0",            opt_int,         {&dns_use_edns0} },
  /* This option is now a no-op, retained for compatibility */
-  { "drop_cr",                  opt_bool,        &drop_cr },
+  { "drop_cr",                  opt_bool,        {&drop_cr} },
 /*********************************************************/
-  { "dsn_advertise_hosts",      opt_stringptr,   &dsn_advertise_hosts },
-  { "dsn_from",                 opt_stringptr,   &dsn_from },
-  { "envelope_to_remove",       opt_bool,        &envelope_to_remove },
-  { "errors_copy",              opt_stringptr,   &errors_copy },
-  { "errors_reply_to",          opt_stringptr,   &errors_reply_to },
+  { "dsn_advertise_hosts",      opt_stringptr,   {&dsn_advertise_hosts} },
+  { "dsn_from",                 opt_stringptr,   {&dsn_from} },
+  { "envelope_to_remove",       opt_bool,        {&envelope_to_remove} },
+  { "errors_copy",              opt_stringptr,   {&errors_copy} },
+  { "errors_reply_to",          opt_stringptr,   {&errors_reply_to} },
 #ifndef DISABLE_EVENT
-  { "event_action",             opt_stringptr,   &event_action },
+  { "event_action",             opt_stringptr,   {&event_action} },
 #endif
-  { "exim_group",               opt_gid,         &exim_gid },
-  { "exim_path",                opt_stringptr,   &exim_path },
-  { "exim_user",                opt_uid,         &exim_uid },
-  { "extra_local_interfaces",   opt_stringptr,   &extra_local_interfaces },
-  { "extract_addresses_remove_arguments", opt_bool, &extract_addresses_remove_arguments },
-  { "finduser_retries",         opt_int,         &finduser_retries },
-  { "freeze_tell",              opt_stringptr,   &freeze_tell },
-  { "gecos_name",               opt_stringptr,   &gecos_name },
-  { "gecos_pattern",            opt_stringptr,   &gecos_pattern },
-#ifdef SUPPORT_TLS
-  { "gnutls_allow_auto_pkcs11", opt_bool,        &gnutls_allow_auto_pkcs11 },
-  { "gnutls_compat_mode",       opt_bool,        &gnutls_compat_mode },
+  { "exim_group",               opt_gid,         {&exim_gid} },
+  { "exim_path",                opt_stringptr,   {&exim_path} },
+  { "exim_user",                opt_uid,         {&exim_uid} },
+  { "exim_version",             opt_stringptr,   {&version_string} },
+  { "extra_local_interfaces",   opt_stringptr,   {&extra_local_interfaces} },
+  { "extract_addresses_remove_arguments", opt_bool, {&extract_addresses_remove_arguments} },
+  { "finduser_retries",         opt_int,         {&finduser_retries} },
+  { "freeze_tell",              opt_stringptr,   {&freeze_tell} },
+  { "gecos_name",               opt_stringptr,   {&gecos_name} },
+  { "gecos_pattern",            opt_stringptr,   {&gecos_pattern} },
+#ifndef DISABLE_TLS
+  { "gnutls_allow_auto_pkcs11", opt_bool,        {&gnutls_allow_auto_pkcs11} },
+  { "gnutls_compat_mode",       opt_bool,        {&gnutls_compat_mode} },
 #endif
-  { "header_line_maxsize",      opt_int,         &header_line_maxsize },
-  { "header_maxsize",           opt_int,         &header_maxsize },
-  { "headers_charset",          opt_stringptr,   &headers_charset },
-  { "helo_accept_junk_hosts",   opt_stringptr,   &helo_accept_junk_hosts },
-  { "helo_allow_chars",         opt_stringptr,   &helo_allow_chars },
-  { "helo_lookup_domains",      opt_stringptr,   &helo_lookup_domains },
-  { "helo_try_verify_hosts",    opt_stringptr,   &helo_try_verify_hosts },
-  { "helo_verify_hosts",        opt_stringptr,   &helo_verify_hosts },
-  { "hold_domains",             opt_stringptr,   &hold_domains },
-  { "host_lookup",              opt_stringptr,   &host_lookup },
-  { "host_lookup_order",        opt_stringptr,   &host_lookup_order },
-  { "host_reject_connection",   opt_stringptr,   &host_reject_connection },
-  { "hosts_connection_nolog",   opt_stringptr,   &hosts_connection_nolog },
+  { "header_line_maxsize",      opt_int,         {&header_line_maxsize} },
+  { "header_maxsize",           opt_int,         {&header_maxsize} },
+  { "headers_charset",          opt_stringptr,   {&headers_charset} },
+  { "helo_accept_junk_hosts",   opt_stringptr,   {&helo_accept_junk_hosts} },
+  { "helo_allow_chars",         opt_stringptr,   {&helo_allow_chars} },
+  { "helo_lookup_domains",      opt_stringptr,   {&helo_lookup_domains} },
+  { "helo_try_verify_hosts",    opt_stringptr,   {&helo_try_verify_hosts} },
+  { "helo_verify_hosts",        opt_stringptr,   {&helo_verify_hosts} },
+  { "hold_domains",             opt_stringptr,   {&hold_domains} },
+  { "host_lookup",              opt_stringptr,   {&host_lookup} },
+  { "host_lookup_order",        opt_stringptr,   {&host_lookup_order} },
+  { "host_reject_connection",   opt_stringptr,   {&host_reject_connection} },
+  { "hosts_connection_nolog",   opt_stringptr,   {&hosts_connection_nolog} },
 #ifdef SUPPORT_PROXY
-  { "hosts_proxy",              opt_stringptr,   &hosts_proxy },
+  { "hosts_proxy",              opt_stringptr,   {&hosts_proxy} },
+#endif
+#ifndef DISABLE_TLS
+  { "hosts_require_alpn",       opt_stringptr,   {&hosts_require_alpn} },
+#endif
+  { "hosts_require_helo",       opt_stringptr,   {&hosts_require_helo} },
+  { "hosts_treat_as_local",     opt_stringptr,   {&hosts_treat_as_local} },
+#ifdef EXPERIMENTAL_XCLIENT
+  { "hosts_xclient",           opt_stringptr,   {&hosts_xclient} },
 #endif
-  { "hosts_treat_as_local",     opt_stringptr,   &hosts_treat_as_local },
 #ifdef LOOKUP_IBASE
-  { "ibase_servers",            opt_stringptr,   &ibase_servers },
+  { "ibase_servers",            opt_stringptr,   {&ibase_servers} },
 #endif
-  { "ignore_bounce_errors_after", opt_time,      &ignore_bounce_errors_after },
-  { "ignore_fromline_hosts",    opt_stringptr,   &ignore_fromline_hosts },
-  { "ignore_fromline_local",    opt_bool,        &ignore_fromline_local },
-  { "keep_environment",         opt_stringptr,   &keep_environment },
-  { "keep_malformed",           opt_time,        &keep_malformed },
+  { "ignore_bounce_errors_after", opt_time,      {&ignore_bounce_errors_after} },
+  { "ignore_fromline_hosts",    opt_stringptr,   {&ignore_fromline_hosts} },
+  { "ignore_fromline_local",    opt_bool,        {&ignore_fromline_local} },
+  { "keep_environment",         opt_stringptr,   {&keep_environment} },
+  { "keep_malformed",           opt_time,        {&keep_malformed} },
 #ifdef LOOKUP_LDAP
-  { "ldap_ca_cert_dir",         opt_stringptr,   &eldap_ca_cert_dir },
-  { "ldap_ca_cert_file",        opt_stringptr,   &eldap_ca_cert_file },
-  { "ldap_cert_file",           opt_stringptr,   &eldap_cert_file },
-  { "ldap_cert_key",            opt_stringptr,   &eldap_cert_key },
-  { "ldap_cipher_suite",        opt_stringptr,   &eldap_cipher_suite },
-  { "ldap_default_servers",     opt_stringptr,   &eldap_default_servers },
-  { "ldap_require_cert",        opt_stringptr,   &eldap_require_cert },
-  { "ldap_start_tls",           opt_bool,        &eldap_start_tls },
-  { "ldap_version",             opt_int,         &eldap_version },
+  { "ldap_ca_cert_dir",         opt_stringptr,   {&eldap_ca_cert_dir} },
+  { "ldap_ca_cert_file",        opt_stringptr,   {&eldap_ca_cert_file} },
+  { "ldap_cert_file",           opt_stringptr,   {&eldap_cert_file} },
+  { "ldap_cert_key",            opt_stringptr,   {&eldap_cert_key} },
+  { "ldap_cipher_suite",        opt_stringptr,   {&eldap_cipher_suite} },
+  { "ldap_default_servers",     opt_stringptr,   {&eldap_default_servers} },
+  { "ldap_require_cert",        opt_stringptr,   {&eldap_require_cert} },
+  { "ldap_start_tls",           opt_bool,        {&eldap_start_tls} },
+  { "ldap_version",             opt_int,         {&eldap_version} },
+#endif
+#ifndef DISABLE_ESMTP_LIMITS
+  { "limits_advertise_hosts", opt_stringptr, {&limits_advertise_hosts} },
 #endif
-  { "local_from_check",         opt_bool,        &local_from_check },
-  { "local_from_prefix",        opt_stringptr,   &local_from_prefix },
-  { "local_from_suffix",        opt_stringptr,   &local_from_suffix },
-  { "local_interfaces",         opt_stringptr,   &local_interfaces },
+  { "local_from_check",         opt_bool,        {&local_from_check} },
+  { "local_from_prefix",        opt_stringptr,   {&local_from_prefix} },
+  { "local_from_suffix",        opt_stringptr,   {&local_from_suffix} },
+  { "local_interfaces",         opt_stringptr,   {&local_interfaces} },
 #ifdef HAVE_LOCAL_SCAN
-  { "local_scan_timeout",       opt_time,        &local_scan_timeout },
+  { "local_scan_timeout",       opt_time,        {&local_scan_timeout} },
 #endif
-  { "local_sender_retain",      opt_bool,        &local_sender_retain },
-  { "localhost_number",         opt_stringptr,   &host_number_string },
-  { "log_file_path",            opt_stringptr,   &log_file_path },
-  { "log_selector",             opt_stringptr,   &log_selector_string },
-  { "log_timezone",             opt_bool,        &log_timezone },
-  { "lookup_open_max",          opt_int,         &lookup_open_max },
-  { "max_username_length",      opt_int,         &max_username_length },
-  { "message_body_newlines",    opt_bool,        &message_body_newlines },
-  { "message_body_visible",     opt_mkint,       &message_body_visible },
-  { "message_id_header_domain", opt_stringptr,   &message_id_domain },
-  { "message_id_header_text",   opt_stringptr,   &message_id_text },
-  { "message_logs",             opt_bool,        &message_logs },
-  { "message_size_limit",       opt_stringptr,   &message_size_limit },
+  { "local_sender_retain",      opt_bool,        {&local_sender_retain} },
+  { "localhost_number",         opt_stringptr,   {&host_number_string} },
+  { "log_file_path",            opt_stringptr,   {&log_file_path} },
+  { "log_selector",             opt_stringptr,   {&log_selector_string} },
+  { "log_timezone",             opt_bool,        {&log_timezone} },
+  { "lookup_open_max",          opt_int,         {&lookup_open_max} },
+  { "max_username_length",      opt_int,         {&max_username_length} },
+  { "message_body_newlines",    opt_bool,        {&message_body_newlines} },
+  { "message_body_visible",     opt_mkint,       {&message_body_visible} },
+  { "message_id_header_domain", opt_stringptr,   {&message_id_domain} },
+  { "message_id_header_text",   opt_stringptr,   {&message_id_text} },
+  { "message_logs",             opt_bool,        {&message_logs} },
+  { "message_size_limit",       opt_stringptr,   {&message_size_limit} },
 #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
-  { "move_frozen_messages",     opt_bool,        &move_frozen_messages },
+  { "move_frozen_messages",     opt_bool,        {&move_frozen_messages} },
 #endif
-  { "mua_wrapper",              opt_bool,        &mua_wrapper },
+  { "mua_wrapper",              opt_bool,        {&mua_wrapper} },
 #ifdef LOOKUP_MYSQL
-  { "mysql_servers",            opt_stringptr,   &mysql_servers },
+  { "mysql_servers",            opt_stringptr,   {&mysql_servers} },
 #endif
-  { "never_users",              opt_uidlist,     &never_users },
-#ifdef SUPPORT_TLS
-  { "openssl_options",          opt_stringptr,   &openssl_options },
+  { "never_users",              opt_uidlist,     {&never_users} },
+  { "notifier_socket",          opt_stringptr,   {&notifier_socket} },
+#ifndef DISABLE_TLS
+  { "openssl_options",          opt_stringptr,   {&openssl_options} },
 #endif
 #ifdef LOOKUP_ORACLE
-  { "oracle_servers",           opt_stringptr,   &oracle_servers },
+  { "oracle_servers",           opt_stringptr,   {&oracle_servers} },
 #endif
-  { "percent_hack_domains",     opt_stringptr,   &percent_hack_domains },
+  { "panic_coredump",           opt_bool,        {&panic_coredump} },
+  { "percent_hack_domains",     opt_stringptr,   {&percent_hack_domains} },
 #ifdef EXIM_PERL
-  { "perl_at_start",            opt_bool,        &opt_perl_at_start },
-  { "perl_startup",             opt_stringptr,   &opt_perl_startup },
-  { "perl_taintmode",           opt_bool,        &opt_perl_taintmode },
+  { "perl_at_start",            opt_bool,        {&opt_perl_at_start} },
+  { "perl_startup",             opt_stringptr,   {&opt_perl_startup} },
+  { "perl_taintmode",           opt_bool,        {&opt_perl_taintmode} },
 #endif
 #ifdef LOOKUP_PGSQL
-  { "pgsql_servers",            opt_stringptr,   &pgsql_servers },
+  { "pgsql_servers",            opt_stringptr,   {&pgsql_servers} },
+#endif
+  { "pid_file_path",            opt_stringptr,   {&pid_file_path} },
+  { "pipelining_advertise_hosts", opt_stringptr, {&pipelining_advertise_hosts} },
+#ifndef DISABLE_PIPE_CONNECT
+  { "pipelining_connect_advertise_hosts", opt_stringptr,
+                                                {&pipe_connect_advertise_hosts} },
 #endif
-  { "pid_file_path",            opt_stringptr,   &pid_file_path },
-  { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts },
 #ifndef DISABLE_PRDR
-  { "prdr_enable",              opt_bool,        &prdr_enable },
+  { "prdr_enable",              opt_bool,        {&prdr_enable} },
+#endif
+  { "preserve_message_logs",    opt_bool,        {&preserve_message_logs} },
+  { "primary_hostname",         opt_stringptr,   {&primary_hostname} },
+  { "print_topbitchars",        opt_bool,        {&print_topbitchars} },
+  { "process_log_path",         opt_stringptr,   {&process_log_path} },
+  { "prod_requires_admin",      opt_bool,        {&prod_requires_admin} },
+#ifdef SUPPORT_PROXY
+  { "proxy_protocol_timeout",   opt_time,        {&proxy_protocol_timeout} },
 #endif
-  { "preserve_message_logs",    opt_bool,        &preserve_message_logs },
-  { "primary_hostname",         opt_stringptr,   &primary_hostname },
-  { "print_topbitchars",        opt_bool,        &print_topbitchars },
-  { "process_log_path",         opt_stringptr,   &process_log_path },
-  { "prod_requires_admin",      opt_bool,        &prod_requires_admin },
-  { "qualify_domain",           opt_stringptr,   &qualify_domain_sender },
-  { "qualify_recipient",        opt_stringptr,   &qualify_domain_recipient },
-  { "queue_domains",            opt_stringptr,   &queue_domains },
-  { "queue_list_requires_admin",opt_bool,        &queue_list_requires_admin },
-  { "queue_only",               opt_bool,        &queue_only },
-  { "queue_only_file",          opt_stringptr,   &queue_only_file },
-  { "queue_only_load",          opt_fixed,       &queue_only_load },
-  { "queue_only_load_latch",    opt_bool,        &queue_only_load_latch },
-  { "queue_only_override",      opt_bool,        &queue_only_override },
-  { "queue_run_in_order",       opt_bool,        &queue_run_in_order },
-  { "queue_run_max",            opt_stringptr,   &queue_run_max },
-  { "queue_smtp_domains",       opt_stringptr,   &queue_smtp_domains },
-  { "receive_timeout",          opt_time,        &receive_timeout },
-  { "received_header_text",     opt_stringptr,   &received_header_text },
-  { "received_headers_max",     opt_int,         &received_headers_max },
-  { "recipient_unqualified_hosts", opt_stringptr, &recipient_unqualified_hosts },
-  { "recipients_max",           opt_int,         &recipients_max },
-  { "recipients_max_reject",    opt_bool,        &recipients_max_reject },
+  { "qualify_domain",           opt_stringptr,   {&qualify_domain_sender} },
+  { "qualify_recipient",        opt_stringptr,   {&qualify_domain_recipient} },
+  { "queue_domains",            opt_stringptr,   {&queue_domains} },
+#ifndef DISABLE_QUEUE_RAMP
+  { "queue_fast_ramp",          opt_bool,        {&queue_fast_ramp} },
+#endif
+  { "queue_list_requires_admin",opt_bool,        {&queue_list_requires_admin} },
+  { "queue_only",               opt_bool,        {&queue_only} },
+  { "queue_only_file",          opt_stringptr,   {&queue_only_file} },
+  { "queue_only_load",          opt_fixed,       {&queue_only_load} },
+  { "queue_only_load_latch",    opt_bool,        {&queue_only_load_latch} },
+  { "queue_only_override",      opt_bool,        {&queue_only_override} },
+  { "queue_run_in_order",       opt_bool,        {&queue_run_in_order} },
+  { "queue_run_max",            opt_stringptr,   {&queue_run_max} },
+  { "queue_smtp_domains",       opt_stringptr,   {&queue_smtp_domains} },
+  { "receive_timeout",          opt_time,        {&receive_timeout} },
+  { "received_header_text",     opt_stringptr,   {&received_header_text} },
+  { "received_headers_max",     opt_int,         {&received_headers_max} },
+  { "recipient_unqualified_hosts", opt_stringptr, {&recipient_unqualified_hosts} },
+  { "recipients_max",           opt_stringptr,   {&recipients_max} },
+  { "recipients_max_reject",    opt_bool,        {&recipients_max_reject} },
 #ifdef LOOKUP_REDIS
-  { "redis_servers",            opt_stringptr,   &redis_servers },
+  { "redis_servers",            opt_stringptr,   {&redis_servers} },
 #endif
-  { "remote_max_parallel",      opt_int,         &remote_max_parallel },
-  { "remote_sort_domains",      opt_stringptr,   &remote_sort_domains },
-  { "retry_data_expire",        opt_time,        &retry_data_expire },
-  { "retry_interval_max",       opt_time,        &retry_interval_max },
-  { "return_path_remove",       opt_bool,        &return_path_remove },
-  { "return_size_limit",        opt_mkint|opt_hidden, &bounce_return_size_limit },
-  { "rfc1413_hosts",            opt_stringptr,   &rfc1413_hosts },
-  { "rfc1413_query_timeout",    opt_time,        &rfc1413_query_timeout },
-  { "sender_unqualified_hosts", opt_stringptr,   &sender_unqualified_hosts },
-  { "slow_lookup_log",          opt_int,         &slow_lookup_log },
-  { "smtp_accept_keepalive",    opt_bool,        &smtp_accept_keepalive },
-  { "smtp_accept_max",          opt_int,         &smtp_accept_max },
-  { "smtp_accept_max_nonmail",  opt_int,         &smtp_accept_max_nonmail },
-  { "smtp_accept_max_nonmail_hosts", opt_stringptr, &smtp_accept_max_nonmail_hosts },
-  { "smtp_accept_max_per_connection", opt_int,   &smtp_accept_max_per_connection },
-  { "smtp_accept_max_per_host", opt_stringptr,   &smtp_accept_max_per_host },
-  { "smtp_accept_queue",        opt_int,         &smtp_accept_queue },
-  { "smtp_accept_queue_per_connection", opt_int, &smtp_accept_queue_per_connection },
-  { "smtp_accept_reserve",      opt_int,         &smtp_accept_reserve },
-  { "smtp_active_hostname",     opt_stringptr,   &raw_active_hostname },
-  { "smtp_banner",              opt_stringptr,   &smtp_banner },
-  { "smtp_check_spool_space",   opt_bool,        &smtp_check_spool_space },
-  { "smtp_connect_backlog",     opt_int,         &smtp_connect_backlog },
-  { "smtp_enforce_sync",        opt_bool,        &smtp_enforce_sync },
-  { "smtp_etrn_command",        opt_stringptr,   &smtp_etrn_command },
-  { "smtp_etrn_serialize",      opt_bool,        &smtp_etrn_serialize },
-  { "smtp_load_reserve",        opt_fixed,       &smtp_load_reserve },
-  { "smtp_max_synprot_errors",  opt_int,         &smtp_max_synprot_errors },
-  { "smtp_max_unknown_commands",opt_int,         &smtp_max_unknown_commands },
-  { "smtp_ratelimit_hosts",     opt_stringptr,   &smtp_ratelimit_hosts },
-  { "smtp_ratelimit_mail",      opt_stringptr,   &smtp_ratelimit_mail },
-  { "smtp_ratelimit_rcpt",      opt_stringptr,   &smtp_ratelimit_rcpt },
-  { "smtp_receive_timeout",     opt_func,        &fn_smtp_receive_timeout },
-  { "smtp_reserve_hosts",       opt_stringptr,   &smtp_reserve_hosts },
-  { "smtp_return_error_details",opt_bool,        &smtp_return_error_details },
+  { "remote_max_parallel",      opt_int,         {&remote_max_parallel} },
+  { "remote_sort_domains",      opt_stringptr,   {&remote_sort_domains} },
+  { "retry_data_expire",        opt_time,        {&retry_data_expire} },
+  { "retry_interval_max",       opt_time,        {&retry_interval_max} },
+  { "return_path_remove",       opt_bool,        {&return_path_remove} },
+  { "return_size_limit",        opt_mkint|opt_hidden, {&bounce_return_size_limit} },
+  { "rfc1413_hosts",            opt_stringptr,   {&rfc1413_hosts} },
+  { "rfc1413_query_timeout",    opt_time,        {&rfc1413_query_timeout} },
+  { "sender_unqualified_hosts", opt_stringptr,   {&sender_unqualified_hosts} },
+  { "slow_lookup_log",          opt_int,         {&slow_lookup_log} },
+  { "smtp_accept_keepalive",    opt_bool,        {&smtp_accept_keepalive} },
+  { "smtp_accept_max",          opt_int,         {&smtp_accept_max} },
+  { "smtp_accept_max_nonmail",  opt_int,         {&smtp_accept_max_nonmail} },
+  { "smtp_accept_max_nonmail_hosts", opt_stringptr, {&smtp_accept_max_nonmail_hosts} },
+  { "smtp_accept_max_per_connection", opt_stringptr,   {&smtp_accept_max_per_connection} },
+  { "smtp_accept_max_per_host", opt_stringptr,   {&smtp_accept_max_per_host} },
+  { "smtp_accept_queue",        opt_int,         {&smtp_accept_queue} },
+  { "smtp_accept_queue_per_connection", opt_int, {&smtp_accept_queue_per_connection} },
+  { "smtp_accept_reserve",      opt_int,         {&smtp_accept_reserve} },
+  { "smtp_active_hostname",     opt_stringptr,   {&raw_active_hostname} },
+  { "smtp_backlog_monitor",     opt_int,         {&smtp_backlog_monitor} },
+  { "smtp_banner",              opt_stringptr,   {&smtp_banner} },
+  { "smtp_check_spool_space",   opt_bool,        {&smtp_check_spool_space} },
+  { "smtp_connect_backlog",     opt_int,         {&smtp_connect_backlog} },
+  { "smtp_enforce_sync",        opt_bool,        {&smtp_enforce_sync} },
+  { "smtp_etrn_command",        opt_stringptr,   {&smtp_etrn_command} },
+  { "smtp_etrn_serialize",      opt_bool,        {&smtp_etrn_serialize} },
+  { "smtp_load_reserve",        opt_fixed,       {&smtp_load_reserve} },
+  { "smtp_max_synprot_errors",  opt_int,         {&smtp_max_synprot_errors} },
+  { "smtp_max_unknown_commands",opt_int,         {&smtp_max_unknown_commands} },
+  { "smtp_ratelimit_hosts",     opt_stringptr,   {&smtp_ratelimit_hosts} },
+  { "smtp_ratelimit_mail",      opt_stringptr,   {&smtp_ratelimit_mail} },
+  { "smtp_ratelimit_rcpt",      opt_stringptr,   {&smtp_ratelimit_rcpt} },
+  { "smtp_receive_timeout",     opt_func,        {.fn = &fn_smtp_receive_timeout} },
+  { "smtp_reserve_hosts",       opt_stringptr,   {&smtp_reserve_hosts} },
+  { "smtp_return_error_details",opt_bool,        {&smtp_return_error_details} },
 #ifdef SUPPORT_I18N
-  { "smtputf8_advertise_hosts", opt_stringptr,   &smtputf8_advertise_hosts },
+  { "smtputf8_advertise_hosts", opt_stringptr,   {&smtputf8_advertise_hosts} },
 #endif
 #ifdef WITH_CONTENT_SCAN
-  { "spamd_address",            opt_stringptr,   &spamd_address },
+  { "spamd_address",            opt_stringptr,   {&spamd_address} },
 #endif
 #ifdef SUPPORT_SPF
-  { "spf_guess",                opt_stringptr,   &spf_guess },
+  { "spf_guess",                opt_stringptr,   {&spf_guess} },
+  { "spf_smtp_comment_template",opt_stringptr,   {&spf_smtp_comment_template} },
 #endif
-  { "split_spool_directory",    opt_bool,        &split_spool_directory },
-  { "spool_directory",          opt_stringptr,   &spool_directory },
-  { "spool_wireformat",         opt_bool,        &spool_wireformat },
+  { "split_spool_directory",    opt_bool,        {&split_spool_directory} },
+  { "spool_directory",          opt_stringptr,   {&spool_directory} },
+  { "spool_wireformat",         opt_bool,        {&spool_wireformat} },
 #ifdef LOOKUP_SQLITE
-  { "sqlite_lock_timeout",      opt_int,         &sqlite_lock_timeout },
-#endif
-#ifdef EXPERIMENTAL_SRS
-  { "srs_config",               opt_stringptr,   &srs_config },
-  { "srs_hashlength",           opt_int,         &srs_hashlength },
-  { "srs_hashmin",              opt_int,         &srs_hashmin },
-  { "srs_maxage",               opt_int,         &srs_maxage },
-  { "srs_secrets",              opt_stringptr,   &srs_secrets },
-  { "srs_usehash",              opt_bool,        &srs_usehash },
-  { "srs_usetimestamp",         opt_bool,        &srs_usetimestamp },
+  { "sqlite_dbfile",            opt_stringptr,   {&sqlite_dbfile} },
+  { "sqlite_lock_timeout",      opt_int,         {&sqlite_lock_timeout} },
 #endif
-  { "strict_acl_vars",          opt_bool,        &strict_acl_vars },
-  { "strip_excess_angle_brackets", opt_bool,     &strip_excess_angle_brackets },
-  { "strip_trailing_dot",       opt_bool,        &strip_trailing_dot },
-  { "syslog_duplication",       opt_bool,        &syslog_duplication },
-  { "syslog_facility",          opt_stringptr,   &syslog_facility_str },
-  { "syslog_pid",               opt_bool,        &syslog_pid },
-  { "syslog_processname",       opt_stringptr,   &syslog_processname },
-  { "syslog_timestamp",         opt_bool,        &syslog_timestamp },
-  { "system_filter",            opt_stringptr,   &system_filter },
-  { "system_filter_directory_transport", opt_stringptr,&system_filter_directory_transport },
-  { "system_filter_file_transport",opt_stringptr,&system_filter_file_transport },
-  { "system_filter_group",      opt_gid,         &system_filter_gid },
-  { "system_filter_pipe_transport",opt_stringptr,&system_filter_pipe_transport },
-  { "system_filter_reply_transport",opt_stringptr,&system_filter_reply_transport },
-  { "system_filter_user",       opt_uid,         &system_filter_uid },
-  { "tcp_nodelay",              opt_bool,        &tcp_nodelay },
+  { "strict_acl_vars",          opt_bool,        {&strict_acl_vars} },
+  { "strip_excess_angle_brackets", opt_bool,     {&strip_excess_angle_brackets} },
+  { "strip_trailing_dot",       opt_bool,        {&strip_trailing_dot} },
+  { "syslog_duplication",       opt_bool,        {&syslog_duplication} },
+  { "syslog_facility",          opt_stringptr,   {&syslog_facility_str} },
+  { "syslog_pid",               opt_bool,        {&syslog_pid} },
+  { "syslog_processname",       opt_stringptr,   {&syslog_processname} },
+  { "syslog_timestamp",         opt_bool,        {&syslog_timestamp} },
+  { "system_filter",            opt_stringptr,   {&system_filter} },
+  { "system_filter_directory_transport", opt_stringptr,{&system_filter_directory_transport} },
+  { "system_filter_file_transport",opt_stringptr,{&system_filter_file_transport} },
+  { "system_filter_group",      opt_gid,         {&system_filter_gid} },
+  { "system_filter_pipe_transport",opt_stringptr,{&system_filter_pipe_transport} },
+  { "system_filter_reply_transport",opt_stringptr,{&system_filter_reply_transport} },
+  { "system_filter_user",       opt_uid,         {&system_filter_uid} },
+  { "tcp_nodelay",              opt_bool,        {&tcp_nodelay} },
 #ifdef USE_TCP_WRAPPERS
-  { "tcp_wrappers_daemon_name", opt_stringptr,   &tcp_wrappers_daemon_name },
+  { "tcp_wrappers_daemon_name", opt_stringptr,   {&tcp_wrappers_daemon_name} },
 #endif
-  { "timeout_frozen_after",     opt_time,        &timeout_frozen_after },
-  { "timezone",                 opt_stringptr,   &timezone_string },
-  { "tls_advertise_hosts",      opt_stringptr,   &tls_advertise_hosts },
-#ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_REQUIRETLS
-  { "tls_advertise_requiretls", opt_stringptr,   &tls_advertise_requiretls },
-# endif
-  { "tls_certificate",          opt_stringptr,   &tls_certificate },
-  { "tls_crl",                  opt_stringptr,   &tls_crl },
-  { "tls_dh_max_bits",          opt_int,         &tls_dh_max_bits },
-  { "tls_dhparam",              opt_stringptr,   &tls_dhparam },
-  { "tls_eccurve",              opt_stringptr,   &tls_eccurve },
+  { "timeout_frozen_after",     opt_time,        {&timeout_frozen_after} },
+  { "timezone",                 opt_stringptr,   {&timezone_string} },
+  { "tls_advertise_hosts",      opt_stringptr,   {&tls_advertise_hosts} },
+#ifndef DISABLE_TLS
+  { "tls_alpn",                        opt_stringptr,   {&tls_alpn} },
+  { "tls_certificate",          opt_stringptr,   {&tls_certificate} },
+  { "tls_crl",                  opt_stringptr,   {&tls_crl} },
+  { "tls_dh_max_bits",          opt_int,         {&tls_dh_max_bits} },
+  { "tls_dhparam",              opt_stringptr,   {&tls_dhparam} },
+  { "tls_eccurve",              opt_stringptr,   {&tls_eccurve} },
 # ifndef DISABLE_OCSP
-  { "tls_ocsp_file",            opt_stringptr,   &tls_ocsp_file },
+  { "tls_ocsp_file",            opt_stringptr,   {&tls_ocsp_file} },
 # endif
-  { "tls_on_connect_ports",     opt_stringptr,   &tls_in.on_connect_ports },
-  { "tls_privatekey",           opt_stringptr,   &tls_privatekey },
-  { "tls_remember_esmtp",       opt_bool,        &tls_remember_esmtp },
-  { "tls_require_ciphers",      opt_stringptr,   &tls_require_ciphers },
-  { "tls_try_verify_hosts",     opt_stringptr,   &tls_try_verify_hosts },
-  { "tls_verify_certificates",  opt_stringptr,   &tls_verify_certificates },
-  { "tls_verify_hosts",         opt_stringptr,   &tls_verify_hosts },
+  { "tls_on_connect_ports",     opt_stringptr,   {&tls_in.on_connect_ports} },
+  { "tls_privatekey",           opt_stringptr,   {&tls_privatekey} },
+  { "tls_remember_esmtp",       opt_bool,        {&tls_remember_esmtp} },
+  { "tls_require_ciphers",      opt_stringptr,   {&tls_require_ciphers} },
+# ifndef DISABLE_TLS_RESUME
+  { "tls_resumption_hosts",     opt_stringptr,   {&tls_resumption_hosts} },
+# endif
+  { "tls_try_verify_hosts",     opt_stringptr,   {&tls_try_verify_hosts} },
+  { "tls_verify_certificates",  opt_stringptr,   {&tls_verify_certificates} },
+  { "tls_verify_hosts",         opt_stringptr,   {&tls_verify_hosts} },
 #endif
-  { "trusted_groups",           opt_gidlist,     &trusted_groups },
-  { "trusted_users",            opt_uidlist,     &trusted_users },
-  { "unknown_login",            opt_stringptr,   &unknown_login },
-  { "unknown_username",         opt_stringptr,   &unknown_username },
-  { "untrusted_set_sender",     opt_stringptr,   &untrusted_set_sender },
-  { "uucp_from_pattern",        opt_stringptr,   &uucp_from_pattern },
-  { "uucp_from_sender",         opt_stringptr,   &uucp_from_sender },
-  { "warn_message_file",        opt_stringptr,   &warn_message_file },
-  { "write_rejectlog",          opt_bool,        &write_rejectlog }
+  { "trusted_groups",           opt_gidlist,     {&trusted_groups} },
+  { "trusted_users",            opt_uidlist,     {&trusted_users} },
+  { "unknown_login",            opt_stringptr,   {&unknown_login} },
+  { "unknown_username",         opt_stringptr,   {&unknown_username} },
+  { "untrusted_set_sender",     opt_stringptr,   {&untrusted_set_sender} },
+  { "uucp_from_pattern",        opt_stringptr,   {&uucp_from_pattern} },
+  { "uucp_from_sender",         opt_stringptr,   {&uucp_from_sender} },
+  { "warn_message_file",        opt_stringptr,   {&warn_message_file} },
+  { "write_rejectlog",          opt_bool,        {&write_rejectlog} },
 };
 
 #ifndef MACRO_PREDEF
@@ -388,7 +412,8 @@ static int optionlist_config_size = nelem(optionlist_config);
 
 #ifdef MACRO_PREDEF
 
-static void fn_smtp_receive_timeout(const uschar * name, const uschar * str) {/*Dummy*/}
+static void
+fn_smtp_receive_timeout(const uschar * name, const uschar * str, unsigned flags) {/*Dummy*/}
 
 void
 options_main(void)
@@ -399,26 +424,26 @@ options_from_list(optionlist_config, nelem(optionlist_config), US"MAIN", NULL);
 void
 options_auths(void)
 {
-struct auth_info * ai;
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
 
 options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
 
-for (ai = auths_available; ai->driver_name[0]; ai++)
+for (struct auth_info * ai = auths_available; ai->driver_name[0]; ai++)
   {
   spf(buf, sizeof(buf), US"_DRIVER_AUTHENTICATOR_%T", ai->driver_name);
   builtin_macro_create(buf);
   options_from_list(ai->options, (unsigned)*ai->options_count, US"AUTHENTICATOR", ai->driver_name);
+
+  if (ai->macros_create) (ai->macros_create)();
   }
 }
 
 void
 options_logging(void)
 {
-bit_table * bp;
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
 
-for (bp = log_options; bp < log_options + log_options_count; bp++)
+for (bit_table * bp = log_options; bp < log_options + log_options_count; bp++)
   {
   spf(buf, sizeof(buf), US"_LOG_%T", bp->name);
   builtin_macro_create(buf);
@@ -477,7 +502,7 @@ typedef struct syslog_fac_item {
 } syslog_fac_item;
 
 /* constants */
-static const char * const hidden = "<value not displayable>";
+static const uschar * const hidden = US"<value not displayable>";
 
 /* Static variables */
 
@@ -555,6 +580,8 @@ static syslog_fac_item syslog_list[] = {
 static int syslog_list_size = sizeof(syslog_list)/sizeof(syslog_fac_item);
 
 
+#define opt_fn_print           BIT(0)
+#define opt_fn_print_label     BIT(1)
 
 
 /*************************************************
@@ -565,50 +592,104 @@ static int syslog_list_size = sizeof(syslog_list)/sizeof(syslog_fac_item);
 pointer variables in the options table or in option tables for various drivers.
 For debugging output, it is useful to be able to find the name of the option
 which is currently being processed. This function finds it, if it exists, by
-searching the table(s).
+searching the table(s) for a value with the given content.
 
 Arguments:   a value that is presumed to be in the table above
 Returns:     the option name, or an empty string
 */
 
-uschar *
-readconf_find_option(void *p)
+const uschar *
+readconf_find_option(void * listptr)
 {
-int i;
-router_instance *r;
-transport_instance *t;
+uschar * list = * USS listptr;
+const uschar * name = NULL, * drname = NULL;
 
-for (i = 0; i < nelem(optionlist_config); i++)
-  if (p == optionlist_config[i].value) return US optionlist_config[i].name;
+for (optionlist * o = optionlist_config;              /* main-config options */
+     o < optionlist_config + optionlist_config_size; o++)
+  if (listptr == o->v.value)
+    return US o->name;
 
-for (r = routers; r; r = r->next)
+for (router_instance * r = routers; r; r = r->next)
+  if (router_name && Ustrcmp(r->name, router_name) == 0)
   {
-  router_info *ri = r->info;
-  for (i = 0; i < *ri->options_count; i++)
-    {
-    if ((ri->options[i].type & opt_mask) != opt_stringptr) continue;
-    if (p == CS (r->options_block) + (long int)(ri->options[i].value))
-      return US ri->options[i].name;
-    }
+  const router_info * ri = r->info;
+
+  /* Check for a listptr match first */
+
+  for (optionlist * o = optionlist_routers;            /* generic options */
+      o < optionlist_routers + optionlist_routers_size; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && listptr == CS r + o->v.offset)
+      return US o->name;
+
+  for (optionlist * o = ri->options;                   /* private options */
+      o < ri->options + *ri->options_count; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && listptr == CS (r->options_block) + o->v.offset)
+      return US o->name;
+
+  /* Check for a list addr match, unless null */
+
+  if (!list) continue;
+
+  for (optionlist * o = optionlist_routers;            /* generic options */
+      o < optionlist_routers + optionlist_routers_size; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && list == * USS(CS r + o->v.offset))
+      if (name) return string_sprintf("DUP: %s %s vs. %s %s",
+       drname, name, r->name, o->name);
+      else { name = US o->name; drname = r->name; }
+
+  for (optionlist * o = ri->options;                   /* private options */
+      o < ri->options + *ri->options_count; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && list == * USS(CS (r->options_block) + o->v.offset))
+      if (name) return string_sprintf("DUP: %s %s vs. %s %s",
+       drname, name, r->name, o->name);
+      else { name = US o->name; drname = r->name; }
   }
 
-for (t = transports; t; t = t->next)
+for (transport_instance * t = transports; t; t = t->next)
+  if (transport_name && Ustrcmp(t->name, transport_name) == 0)
   {
-  transport_info *ti = t->info;
-  for (i = 0; i < *ti->options_count; i++)
-    {
-    optionlist * op = &ti->options[i];
-    if ((op->type & opt_mask) != opt_stringptr) continue;
-    if (p == (  op->type & opt_public
-            ? CS t
-            : CS t->options_block
-            )
-            + (long int)op->value)
-       return US op->name;
-    }
+  const transport_info * ti = t->info;
+
+  /* Check for a listptr match first */
+
+  for (optionlist * o = optionlist_transports;         /* generic options */
+      o < optionlist_transports + optionlist_transports_size; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && listptr == CS t + o->v.offset)
+      return US o->name;
+
+  for (optionlist * o = ti->options;                   /* private options */
+      o < ti->options + *ti->options_count; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && listptr == CS t->options_block + o->v.offset)
+      return US o->name;
+
+  /* Check for a list addr match, unless null */
+
+  if (!list) continue;
+
+  for (optionlist * o = optionlist_transports;         /* generic options */
+      o < optionlist_transports + optionlist_transports_size; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && list == * USS(CS t + o->v.offset))
+      if (name) return string_sprintf("DUP: %s %s vs. %s %s",
+       drname, name, t->name, o->name);
+      else { name = US o->name; drname = t->name; }
+
+  for (optionlist * o = ti->options;                   /* private options */
+      o < ti->options + *ti->options_count; o++)
+    if (  (o->type & opt_mask) == opt_stringptr
+       && list == * USS(CS t->options_block + o->v.offset))
+      if (name) return string_sprintf("DUP: %s %s vs. %s %s",
+       drname, name, t->name, o->name);
+      else { name = US o->name; drname = t->name; }
   }
 
-return US"";
+return name ? name : US"";
 }
 
 
@@ -628,7 +709,7 @@ Args:
 macro_item *
 macro_create(const uschar * name, const uschar * val, BOOL command_line)
 {
-macro_item * m = store_get(sizeof(macro_item));
+macro_item * m = store_get(sizeof(macro_item), GET_UNTAINTED);
 
 READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val);
 m->next = NULL;
@@ -663,7 +744,7 @@ Returns:       FALSE iff fatal error
 BOOL
 macro_read_assignment(uschar *s)
 {
-uschar name[64];
+uschar name[EXIM_DRIVERNAME_MAX];
 int namelen = 0;
 BOOL redef = FALSE;
 macro_item *m;
@@ -680,7 +761,7 @@ while (isalnum(*s) || *s == '_')
   }
 name[namelen] = 0;
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 if (*s++ != '=')
   {
   log_write(0, LOG_PANIC|LOG_CONFIG_IN, "malformed macro definition");
@@ -692,7 +773,7 @@ if (*s == '=')
   redef = TRUE;
   s++;
   }
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
 /* If an existing macro of the same name was defined on the command line, we
 just skip this definition. It's an error to attempt to redefine a macro without
@@ -780,12 +861,11 @@ macros_expand(int len, int * newlen, BOOL * macro_found)
 {
 uschar * ss = big_buffer + len;
 uschar * s;
-macro_item * m;
 
 /* Find the true start of the physical line - leading spaces are always
 ignored. */
 
-while (isspace(*ss)) ss++;
+Uskip_whitespace(&ss);
 
 /* Process the physical line for macros. If this is the start of the logical
 line, skip over initial text at the start of the line if it starts with an
@@ -797,8 +877,7 @@ s = ss;
 if (len == 0 && isupper(*s))
   {
   while (isalnum(*s) || *s == '_') s++;
-  while (isspace(*s)) s++;
-  if (*s != '=') s = ss;          /* Not a macro definition */
+  if (Uskip_whitespace(&s) != '=') s = ss;          /* Not a macro definition */
   }
 
 /* Skip leading chars which cannot start a macro name, to avoid multiple
@@ -810,7 +889,7 @@ while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
 replacing all occurrences of the macro. */
 
 *macro_found = FALSE;
-if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next)
+if (*s) for (macro_item * m = *s == '_' ? macros : macros_user; m; m = m->next)
   {
   uschar * p, *pp;
   uschar * t;
@@ -861,7 +940,7 @@ if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next)
 /* An empty macro replacement at the start of a line could mean that ss no
 longer points to the first non-blank character. */
 
-while (isspace(*ss)) ss++;
+Uskip_whitespace(&ss);
 return ss;
 }
 
@@ -962,7 +1041,7 @@ for (;;)
 
   /* Handle conditionals, which are also applied to physical lines. Conditions
   are of the form ".ifdef ANYTEXT" and are treated as true if any macro
-  expansion occured on the rest of the line. A preliminary test for the leading
+  expansion occurred on the rest of the line. A preliminary test for the leading
   '.' saves effort on most lines. */
 
   if (*ss == '.')
@@ -1029,12 +1108,12 @@ for (;;)
          (Ustrncmp(ss+8, "_if_exists", 10) == 0 && isspace(ss[18]))))
     {
     uschar *t;
-    int include_if_exists = isspace(ss[8])? 0 : 10;
+    int include_if_exists = isspace(ss[8]) ? 0 : 10;
     config_file_item *save;
     struct stat statbuf;
 
     ss += 9 + include_if_exists;
-    while (isspace(*ss)) ss++;
+    Uskip_whitespace(&ss);
     t = ss + Ustrlen(ss);
     while (t > ss && isspace(t[-1])) t--;
     if (*ss == '\"' && t[-1] == '\"')
@@ -1060,7 +1139,7 @@ for (;;)
 
     if (config_lines)
       save_config_position(config_filename, config_lineno);
-    save = store_get(sizeof(config_file_item));
+    save = store_get(sizeof(config_file_item), GET_UNTAINTED);
     save->next = config_file_stack;
     config_file_stack = save;
     save->file = config_file;
@@ -1125,7 +1204,7 @@ if (config_lines)
 if (strncmpic(s, US"begin ", 6) == 0)
   {
   s += 6;
-  while (isspace(*s)) s++;
+  Uskip_whitespace(&s);
   if (big_buffer + len - s > sizeof(next_section) - 2)
     s[sizeof(next_section) - 2] = 0;
   Ustrcpy(next_section, s);
@@ -1155,21 +1234,30 @@ Arguments:
 Returns:    new input pointer
 */
 
-uschar *
-readconf_readname(uschar *name, int len, uschar *s)
+const uschar *
+readconf_readname(uschar * name, int len, const uschar * s)
 {
 int p = 0;
-while (isspace(*s)) s++;
-if (isalpha(*s))
-  {
+BOOL broken = FALSE;
+
+if (isalpha(Uskip_whitespace(&s)))
   while (isalnum(*s) || *s == '_')
     {
     if (p < len-1) name[p++] = *s;
+    else {
+      broken = TRUE;
+      break;
+    }
     s++;
     }
-  }
+
 name[p] = 0;
-while (isspace(*s)) s++;
+if (broken) {
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+            "exim item name too long (>%d), unable to use \"%s\" (truncated)",
+            len, name);
+}
+Uskip_whitespace(&s);
 return s;
 }
 
@@ -1293,7 +1381,7 @@ Returns:    pointer to an option entry, or NULL if not found
 */
 
 static optionlist *
-find_option(uschar *name, optionlist *ol, int last)
+find_option(const uschar *name, optionlist *ol, int last)
 {
 int first = 0;
 while (last > first)
@@ -1332,16 +1420,16 @@ Returns:        a pointer to the boolean flag.
 */
 
 static BOOL *
-get_set_flag(uschar *name, optionlist *oltop, int last, void *data_block)
+get_set_flag(const uschar *name, optionlist *oltop, int last, void *data_block)
 {
 optionlist *ol;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
 sprintf(CS name2, "*set_%.50s", name);
-ol = find_option(name2, oltop, last);
-if (ol == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-  "Exim internal error: missing set flag for %s", name);
-return (data_block == NULL)? (BOOL *)(ol->value) :
-  (BOOL *)(US data_block + (long int)(ol->value));
+if (!(ol = find_option(name2, oltop, last)))
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "Exim internal error: missing set flag for %s", name);
+return data_block
+  ? (BOOL *)(US data_block + ol->v.offset) : (BOOL *)ol->v.value;
 }
 
 
@@ -1400,20 +1488,20 @@ Returns:      the control block for the parsed rule.
 static rewrite_rule *
 readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal)
 {
-rewrite_rule *next = store_get(sizeof(rewrite_rule));
+rewrite_rule * next = store_get(sizeof(rewrite_rule), GET_UNTAINTED);
 
 next->next = NULL;
 next->key = string_dequote(&p);
 
-while (isspace(*p)) p++;
-if (*p == 0)
+Uskip_whitespace(&p);
+if (!*p)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
     "missing rewrite replacement string");
 
 next->flags = 0;
 next->replacement = string_dequote(&p);
 
-while (*p != 0) switch (*p++)
+while (*p) switch (*p++)
   {
   case ' ': case '\t': break;
 
@@ -1528,9 +1616,16 @@ return yield;
 *            Custom-handler options              *
 *************************************************/
 static void
-fn_smtp_receive_timeout(const uschar * name, const uschar * str)
+fn_smtp_receive_timeout(const uschar * name, const uschar * str, unsigned flags)
 {
-if (*str == '$')
+if (flags & opt_fn_print)
+  {
+  if (flags & opt_fn_print_label) printf("%s = ", name);
+  printf("%s\n", smtp_receive_timeout_s
+    ? string_printing2(smtp_receive_timeout_s, SP_TAB)
+    : readconf_printtime(smtp_receive_timeout));
+  }
+else if (*str == '$')
   smtp_receive_timeout_s = string_copy(str);
 else
   {
@@ -1587,7 +1682,7 @@ readconf_handle_option(uschar *buffer, optionlist *oltop, int last,
 {
 int ptr = 0;
 int offset = 0;
-int n, count, type, value;
+int count, type, value;
 int issecure = 0;
 uid_t uid;
 gid_t gid;
@@ -1595,27 +1690,26 @@ BOOL boolvalue = TRUE;
 BOOL freesptr = TRUE;
 optionlist *ol, *ol2;
 struct passwd *pw;
-void *reset_point;
+rmark reset_point;
 int intbase = 0;
 uschar *inttype = US"";
 uschar *sptr;
-uschar *s = buffer;
+const uschar * s = buffer;
 uschar **str_target;
-uschar name[64];
-uschar name2[64];
+uschar name[EXIM_DRIVERNAME_MAX];
+uschar name2[EXIM_DRIVERNAME_MAX];
 
 /* There may be leading spaces; thereafter, we expect an option name starting
 with a letter. */
 
-while (isspace(*s)) s++;
-if (!isalpha(*s))
+if (!isalpha( Uskip_whitespace(&s) ))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "option setting expected: %s", s);
 
 /* Read the name of the option, and skip any subsequent white space. If
 it turns out that what we read was "hide", set the flag indicating that
 this is a secure option, and loop to read the next word. */
 
-for (n = 0; n < 2; n++)
+for (int n = 0; n < 2; n++)
   {
   while (isalnum(*s) || *s == '_')
     {
@@ -1623,7 +1717,7 @@ for (n = 0; n < 2; n++)
     s++;
     }
   name[ptr] = 0;
-  while (isspace(*s)) s++;
+  Uskip_whitespace(&s);
   if (Ustrcmp(name, "hide") != 0) break;
   issecure = opt_secure;
   ptr = 0;
@@ -1648,7 +1742,7 @@ is set twice, is a disaster. */
 
 if (!(ol = find_option(name + offset, oltop, last)))
   {
-  if (unknown_txt == NULL) return FALSE;
+  if (!unknown_txt) return FALSE;
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, CS unknown_txt, name);
   }
 
@@ -1667,7 +1761,7 @@ if (type < opt_bool || type > opt_bool_last)
   if (offset != 0)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "negation prefix applied to a non-boolean option");
-  if (*s == 0)
+  if (!*s)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "unexpected end of line (data missing) after %s", name);
   if (*s != '=')
@@ -1678,17 +1772,17 @@ if (type < opt_bool || type > opt_bool_last)
 true/false/yes/no, or, in the case of opt_expand_bool, a general string that
 ultimately expands to one of those values. */
 
-else if (*s != 0 && (offset != 0 || *s != '='))
+else if (*s && (offset != 0 || *s != '='))
   extra_chars_error(s, US"boolean option ", name, US"");
 
 /* Skip white space after = */
 
-if (*s == '=') while (isspace((*(++s))));
+if (*s == '=') while (isspace(*++s));
 
 /* If there is a data block and the opt_public flag is not set, change
 the data block pointer to the private options block. */
 
-if (data_block != NULL && (ol->type & opt_public) == 0)
+if (data_block && !(ol->type & opt_public))
   data_block = (void *)(((driver_instance *)data_block)->options_block);
 
 /* Now get the data according to the type. */
@@ -1719,331 +1813,337 @@ switch (type)
   case opt_gidlist:
   case opt_rewrite:
 
-  reset_point = sptr = read_string(s, name);
+    reset_point = store_mark();
+    sptr = read_string(s, name);
 
-  /* Having read a string, we now have several different ways of using it,
-  depending on the data type, so do another switch. If keeping the actual
-  string is not required (because it is interpreted), freesptr is set TRUE,
-  and at the end we reset the pool. */
+    /* Having read a string, we now have several different ways of using it,
+    depending on the data type, so do another switch. If keeping the actual
+    string is not required (because it is interpreted), freesptr is set TRUE,
+    and at the end we reset the pool. */
 
-  switch (type)
-    {
-    /* If this was a string, set the variable to point to the new string,
-    and set the flag so its store isn't reclaimed. If it was a list of rewrite
-    rules, we still keep the string (for printing), and parse the rules into a
-    control block and flags word. */
-
-    case opt_stringptr:
-    str_target = data_block ? USS (US data_block + (long int)(ol->value))
-                           : USS (ol->value);
-    if (ol->type & opt_rep_con)
-      {
-      uschar * saved_condition;
-      /* We already have a condition, we're conducting a crude hack to let
-      multiple condition rules be chained together, despite storing them in
-      text form. */
-      *str_target = string_copy_malloc( (saved_condition = *str_target)
-       ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
-           saved_condition, sptr)
-       : sptr);
-      /* TODO(pdp): there is a memory leak here and just below
-      when we set 3 or more conditions; I still don't
-      understand the store mechanism enough to know
-      what's the safe way to free content from an earlier store.
-      AFAICT, stores stack, so freeing an early stored item also stores
-      all data alloc'd after it.  If we knew conditions were adjacent,
-      we could survive that, but we don't.  So I *think* we need to take
-      another bit from opt_type to indicate "malloced"; this seems like
-      quite a hack, especially for this one case.  It also means that
-      we can't ever reclaim the store from the *first* condition.
-
-      Because we only do this once, near process start-up, I'm prepared to
-      let this slide for the time being, even though it rankles.  */
-      }
-    else if (ol->type & opt_rep_str)
+    switch (type)
       {
-      uschar sep_o = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':';
-      int    sep_i = -(int)sep_o;
-      const uschar * list = sptr;
-      uschar * s;
-      gstring * list_o = NULL;
+      /* If this was a string, set the variable to point to the new string,
+      and set the flag so its store isn't reclaimed. If it was a list of rewrite
+      rules, we still keep the string (for printing), and parse the rules into a
+      control block and flags word. */
+
+      case opt_stringptr:
+       str_target = data_block ? USS (US data_block + ol->v.offset)
+                               : USS ol->v.value;
+       if (ol->type & opt_rep_con)
+         {
+         uschar * saved_condition;
+         /* We already have a condition, we're conducting a crude hack to let
+         multiple condition rules be chained together, despite storing them in
+         text form. */
+         *str_target = string_copy_perm( (saved_condition = *str_target)
+           ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
+               saved_condition, sptr)
+           : sptr,
+           FALSE);
+         /* TODO(pdp): there is a memory leak here and just below
+         when we set 3 or more conditions; I still don't
+         understand the store mechanism enough to know
+         what's the safe way to free content from an earlier store.
+         AFAICT, stores stack, so freeing an early stored item also stores
+         all data alloc'd after it.  If we knew conditions were adjacent,
+         we could survive that, but we don't.  So I *think* we need to take
+         another bit from opt_type to indicate "malloced"; this seems like
+         quite a hack, especially for this one case.  It also means that
+         we can't ever reclaim the store from the *first* condition.
+
+         Because we only do this once, near process start-up, I'm prepared to
+         let this slide for the time being, even though it rankles.  */
+         }
+       else if (ol->type & opt_rep_str)
+         {
+         uschar sep_o =
+           Ustrncmp(name, "headers_add", 11) == 0      ? '\n'
+           : Ustrncmp(name, "set", 3) == 0             ? ';'
+           : ':';
+         int    sep_i = -(int)sep_o;
+         const uschar * list = sptr;
+         uschar * s;
+         gstring * list_o = NULL;
+
+         if (*str_target)
+           {
+           list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
+           list_o = string_cat(list_o, *str_target);
+           }
 
-      if (*str_target)
-       {
-       list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
-       list_o = string_cat(list_o, *str_target);
-       }
+         while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
+           list_o = string_append_listele(list_o, sep_o, s);
 
-      while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
-       list_o = string_append_listele(list_o, sep_o, s);
+         if (list_o)
+           *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
+         }
+       else
+         {
+         *str_target = sptr;
+         freesptr = FALSE;
+         }
+       break;
 
-      if (list_o)
-       *str_target = string_copy_malloc(string_from_gstring(list_o));
-      }
-    else
-      {
-      *str_target = sptr;
-      freesptr = FALSE;
-      }
-    break;
+      case opt_rewrite:
+       if (data_block)
+         *USS (US data_block + ol->v.offset) = sptr;
+       else
+         *USS ol->v.value = sptr;
+       freesptr = FALSE;
+       if (type == opt_rewrite)
+         {
+         int sep = 0;
+         int *flagptr;
+         uschar *p = sptr;
+         rewrite_rule **chain;
+         optionlist *ol3;
+
+         sprintf(CS name2, "*%.50s_rules", name);
+         ol2 = find_option(name2, oltop, last);
+         sprintf(CS name2, "*%.50s_flags", name);
+         ol3 = find_option(name2, oltop, last);
+
+         if (!ol2 || !ol3)
+           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+             "rewrite rules not available for driver");
+
+         if (data_block)
+           {
+           chain = (rewrite_rule **)(US data_block + ol2->v.offset);
+           flagptr = (int *)(US data_block + ol3->v.offset);
+           }
+         else
+           {
+           chain = (rewrite_rule **)ol2->v.value;
+           flagptr = (int *)ol3->v.value;
+           }
 
-    case opt_rewrite:
-    if (data_block)
-      *USS (US data_block + (long int)(ol->value)) = sptr;
-    else
-      *USS (ol->value) = sptr;
-    freesptr = FALSE;
-    if (type == opt_rewrite)
-      {
-      int sep = 0;
-      int *flagptr;
-      uschar *p = sptr;
-      rewrite_rule **chain;
-      optionlist *ol3;
-
-      sprintf(CS name2, "*%.50s_rules", name);
-      ol2 = find_option(name2, oltop, last);
-      sprintf(CS name2, "*%.50s_flags", name);
-      ol3 = find_option(name2, oltop, last);
-
-      if (ol2 == NULL || ol3 == NULL)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-          "rewrite rules not available for driver");
+         /* This will trap if sptr is tainted. Not sure if that can happen */
+         while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
+           {
+           rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
+           *chain = next;
+           chain = &(next->next);
+           }
 
-      if (data_block == NULL)
-        {
-        chain = (rewrite_rule **)(ol2->value);
-        flagptr = (int *)(ol3->value);
-        }
-      else
-        {
-        chain = (rewrite_rule **)(US data_block + (long int)(ol2->value));
-        flagptr = (int *)(US data_block + (long int)(ol3->value));
-        }
+         if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
+           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
+             "non-header rewrite - not allowed at transport time -");
+         }
+       break;
 
-      while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
-        {
-        rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
-        *chain = next;
-        chain = &(next->next);
-        }
+      /* If it was an expanded uid, see if there is any expansion to be
+      done by checking for the presence of a $ character. If there is, save it
+      in the corresponding *expand_user option field. Otherwise, fall through
+      to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
+      of data. */
 
-      if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
-          "non-header rewrite - not allowed at transport time -");
-      }
-    break;
+      case opt_expand_uid:
+       sprintf(CS name2, "*expand_%.50s", name);
+       if ((ol2 = find_option(name2, oltop, last)))
+         {
+         uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
 
-    /* If it was an expanded uid, see if there is any expansion to be
-    done by checking for the presence of a $ character. If there is, save it
-    in the corresponding *expand_user option field. Otherwise, fall through
-    to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
-    of data. */
+         if (data_block)
+           *(USS(US data_block + ol2->v.offset)) = ss;
+         else
+           *(USS ol2->v.value) = ss;
 
-    case opt_expand_uid:
-    sprintf(CS name2, "*expand_%.50s", name);
-    ol2 = find_option(name2, oltop, last);
-    if (ol2 != NULL)
-      {
-      uschar *ss = (Ustrchr(sptr, '$') != NULL)? sptr : NULL;
+         if (ss)
+           {
+           *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+           freesptr = FALSE;
+           break;
+           }
+         }
 
-      if (data_block == NULL)
-        *((uschar **)(ol2->value)) = ss;
-      else
-        *((uschar **)(US data_block + (long int)(ol2->value))) = ss;
+      /* Look up a fixed uid, and also make use of the corresponding gid
+      if a passwd entry is returned and the gid has not been set. */
 
-      if (ss != NULL)
-        {
-        *(get_set_flag(name, oltop, last, data_block)) = FALSE;
-        freesptr = FALSE;
-        break;
-        }
-      }
+      case opt_uid:
+       if (!route_finduser(sptr, &pw, &uid))
+         log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
+       if (data_block)
+         *(uid_t *)(US data_block + ol->v.offset) = uid;
+       else
+         *(uid_t *)ol->v.value = uid;
 
-    /* Look up a fixed uid, and also make use of the corresponding gid
-    if a passwd entry is returned and the gid has not been set. */
+       /* Set the flag indicating a fixed value is set */
 
-    case opt_uid:
-    if (!route_finduser(sptr, &pw, &uid))
-      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
-    if (data_block == NULL)
-      *((uid_t *)(ol->value)) = uid;
-    else
-      *((uid_t *)(US data_block + (long int)(ol->value))) = uid;
+       *(get_set_flag(name, oltop, last, data_block)) = TRUE;
 
-    /* Set the flag indicating a fixed value is set */
+       /* Handle matching gid if we have a passwd entry: done by finding the
+       same name with terminating "user" changed to "group"; if not found,
+       ignore. Also ignore if the value is already set. */
 
-    *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+       if (pw == NULL) break;
+       Ustrcpy(name+Ustrlen(name)-4, US"group");
+       ol2 = find_option(name, oltop, last);
+       if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
+           (ol2->type & opt_mask) == opt_expand_gid))
+         {
+         BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
+         if (!*set_flag)
+           {
+           if (data_block)
+             *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
+           else
+             *((gid_t *)ol2->v.value) = pw->pw_gid;
+           *set_flag = TRUE;
+           }
+         }
+       break;
 
-    /* Handle matching gid if we have a passwd entry: done by finding the
-    same name with terminating "user" changed to "group"; if not found,
-    ignore. Also ignore if the value is already set. */
+      /* If it was an expanded gid, see if there is any expansion to be
+      done by checking for the presence of a $ character. If there is, save it
+      in the corresponding *expand_user option field. Otherwise, fall through
+      to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
+      of data. */
 
-    if (pw == NULL) break;
-    Ustrcpy(name+Ustrlen(name)-4, "group");
-    ol2 = find_option(name, oltop, last);
-    if (ol2 != NULL && ((ol2->type & opt_mask) == opt_gid ||
-        (ol2->type & opt_mask) == opt_expand_gid))
-      {
-      BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
-      if (! *set_flag)
-        {
-        if (data_block == NULL)
-          *((gid_t *)(ol2->value)) = pw->pw_gid;
-        else
-          *((gid_t *)(US data_block + (long int)(ol2->value))) = pw->pw_gid;
-        *set_flag = TRUE;
-        }
-      }
-    break;
+      case opt_expand_gid:
+       sprintf(CS name2, "*expand_%.50s", name);
+       if ((ol2 = find_option(name2, oltop, last)))
+         {
+         uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
 
-    /* If it was an expanded gid, see if there is any expansion to be
-    done by checking for the presence of a $ character. If there is, save it
-    in the corresponding *expand_user option field. Otherwise, fall through
-    to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
-    of data. */
+         if (data_block)
+           *(USS(US data_block + ol2->v.offset)) = ss;
+         else
+           *(USS ol2->v.value) = ss;
 
-    case opt_expand_gid:
-    sprintf(CS name2, "*expand_%.50s", name);
-    ol2 = find_option(name2, oltop, last);
-    if (ol2 != NULL)
-      {
-      uschar *ss = (Ustrchr(sptr, '$') != NULL)? sptr : NULL;
+         if (ss)
+           {
+           *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+           freesptr = FALSE;
+           break;
+           }
+         }
 
-      if (data_block == NULL)
-        *((uschar **)(ol2->value)) = ss;
-      else
-        *((uschar **)(US data_block + (long int)(ol2->value))) = ss;
+      /* Handle freestanding gid */
 
-      if (ss != NULL)
-        {
-        *(get_set_flag(name, oltop, last, data_block)) = FALSE;
-        freesptr = FALSE;
-        break;
-        }
-      }
-
-    /* Handle freestanding gid */
+      case opt_gid:
+       if (!route_findgroup(sptr, &gid))
+         log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
+       if (data_block)
+         *((gid_t *)(US data_block + ol->v.offset)) = gid;
+       else
+         *((gid_t *)ol->v.value) = gid;
+       *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+       break;
 
-    case opt_gid:
-    if (!route_findgroup(sptr, &gid))
-      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
-    if (data_block == NULL)
-      *((gid_t *)(ol->value)) = gid;
-    else
-      *((gid_t *)(US data_block + (long int)(ol->value))) = gid;
-    *(get_set_flag(name, oltop, last, data_block)) = TRUE;
-    break;
+      /* If it was a uid list, look up each individual entry, and build
+      a vector of uids, with a count in the first element. Put the vector
+      in malloc store so we can free the string. (We are reading into
+      permanent store already.) */
 
-    /* If it was a uid list, look up each individual entry, and build
-    a vector of uids, with a count in the first element. Put the vector
-    in malloc store so we can free the string. (We are reading into
-    permanent store already.) */
+      case opt_uidlist:
+       {
+       int count = 1;
+       uid_t *list;
+       int ptr = 0;
+       const uschar *p;
+       const uschar *op = expand_string (sptr);
+
+       if (op == NULL)
+         log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+           name, expand_string_message);
+
+       p = op;
+       if (*p != 0) count++;
+       while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+       list = store_malloc(count*sizeof(uid_t));
+       list[ptr++] = (uid_t)(count - 1);
+
+       if (data_block)
+         *((uid_t **)(US data_block + ol->v.offset)) = list;
+       else
+         *((uid_t **)ol->v.value) = list;
+
+       p = op;
+       while (count-- > 1)
+         {
+         int sep = 0;
+         /* If p is tainted we trap.  Not sure that can happen */
+         (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+         if (!route_finduser(big_buffer, NULL, &uid))
+           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
+             big_buffer);
+         list[ptr++] = uid;
+         }
+       break;
+       }
 
-    case opt_uidlist:
-      {
-      int count = 1;
-      uid_t *list;
-      int ptr = 0;
-      const uschar *p;
-      const uschar *op = expand_string (sptr);
-
-      if (op == NULL)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
-          name, expand_string_message);
-
-      p = op;
-      if (*p != 0) count++;
-      while (*p != 0) if (*p++ == ':' && *p != 0) count++;
-      list = store_malloc(count*sizeof(uid_t));
-      list[ptr++] = (uid_t)(count - 1);
-
-      if (data_block == NULL)
-        *((uid_t **)(ol->value)) = list;
-      else
-        *((uid_t **)(US data_block + (long int)(ol->value))) = list;
+      /* If it was a gid list, look up each individual entry, and build
+      a vector of gids, with a count in the first element. Put the vector
+      in malloc store so we can free the string. (We are reading into permanent
+      store already.) */
 
-      p = op;
-      while (count-- > 1)
-        {
-        int sep = 0;
-        (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
-        if (!route_finduser(big_buffer, NULL, &uid))
-          log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
-            big_buffer);
-        list[ptr++] = uid;
-        }
+      case opt_gidlist:
+       {
+       int count = 1;
+       gid_t *list;
+       int ptr = 0;
+       const uschar *p;
+       const uschar *op = expand_string (sptr);
+
+       if (!op)
+         log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+           name, expand_string_message);
+
+       p = op;
+       if (*p != 0) count++;
+       while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+       list = store_malloc(count*sizeof(gid_t));
+       list[ptr++] = (gid_t)(count - 1);
+
+       if (data_block)
+         *((gid_t **)(US data_block + ol->v.offset)) = list;
+       else
+         *((gid_t **)ol->v.value) = list;
+
+       p = op;
+       while (count-- > 1)
+         {
+         int sep = 0;
+         /* If p is tainted we trap.  Not sure that can happen */
+         (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+         if (!route_findgroup(big_buffer, &gid))
+           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
+             big_buffer);
+         list[ptr++] = gid;
+         }
+       break;
+       }
       }
-    break;
-
-    /* If it was a gid list, look up each individual entry, and build
-    a vector of gids, with a count in the first element. Put the vector
-    in malloc store so we can free the string. (We are reading into permanent
-    store already.) */
 
-    case opt_gidlist:
-      {
-      int count = 1;
-      gid_t *list;
-      int ptr = 0;
-      const uschar *p;
-      const uschar *op = expand_string (sptr);
-
-      if (op == NULL)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
-          name, expand_string_message);
-
-      p = op;
-      if (*p != 0) count++;
-      while (*p != 0) if (*p++ == ':' && *p != 0) count++;
-      list = store_malloc(count*sizeof(gid_t));
-      list[ptr++] = (gid_t)(count - 1);
-
-      if (data_block == NULL)
-        *((gid_t **)(ol->value)) = list;
-      else
-        *((gid_t **)(US data_block + (long int)(ol->value))) = list;
+    /* Release store if the value of the string doesn't need to be kept. */
 
-      p = op;
-      while (count-- > 1)
-        {
-        int sep = 0;
-        (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
-        if (!route_findgroup(big_buffer, &gid))
-          log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
-            big_buffer);
-        list[ptr++] = gid;
-        }
-      }
+    if (freesptr) reset_point = store_reset(reset_point);
     break;
-    }
-
-  /* Release store if the value of the string doesn't need to be kept. */
-
-  if (freesptr) store_reset(reset_point);
-  break;
 
   /* Expanded boolean: if no characters follow, or if there are no dollar
   characters, this is a fixed-valued boolean, and we fall through. Otherwise,
   save the string for later expansion in the alternate place. */
 
   case opt_expand_bool:
-  if (*s != 0 && Ustrchr(s, '$') != 0)
-    {
-    sprintf(CS name2, "*expand_%.50s", name);
-    ol2 = find_option(name2, oltop, last);
-    if (ol2 != NULL)
+    if (*s && Ustrchr(s, '$') != 0)
       {
-      reset_point = sptr = read_string(s, name);
-      if (data_block == NULL)
-        *((uschar **)(ol2->value)) = sptr;
-      else
-        *((uschar **)(US data_block + (long int)(ol2->value))) = sptr;
-      freesptr = FALSE;
-      break;
+      sprintf(CS name2, "*expand_%.50s", name);
+      if ((ol2 = find_option(name2, oltop, last)))
+       {
+       reset_point = store_mark();
+       sptr = read_string(s, name);
+       if (data_block)
+         *(USS(US data_block + ol2->v.offset)) = sptr;
+       else
+         *(USS ol2->v.value) = sptr;
+       freesptr = FALSE;
+       break;
+       }
       }
-    }
-  /* Fall through */
+    /* Fall through */
 
   /* Boolean: if no characters follow, the value is boolvalue. Otherwise
   look for yes/not/true/false. Some booleans are stored in a single bit in
@@ -2056,130 +2156,122 @@ switch (type)
   case opt_bit:
   case opt_bool_verify:
   case opt_bool_set:
-  if (*s != 0)
-    {
-    s = readconf_readname(name2, 64, s);
-    if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
-      boolvalue = TRUE;
-    else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
-      boolvalue = FALSE;
-    else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-      "\"%s\" is not a valid value for the \"%s\" option", name2, name);
-    if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
-      US"for boolean option ", name);
-    }
+    if (*s)
+      {
+      s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
+      if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
+       boolvalue = TRUE;
+      else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
+       boolvalue = FALSE;
+      else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+       "\"%s\" is not a valid value for the \"%s\" option", name2, name);
+      if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
+       US"for boolean option ", name);
+      }
 
-  /* Handle single-bit type. */
+    /* Handle single-bit type. */
 
-  if (type == opt_bit)
-    {
-    int bit = 1 << ((ol->type >> 16) & 31);
-    int *ptr = (data_block == NULL)?
-      (int *)(ol->value) :
-      (int *)(US data_block + (long int)ol->value);
-    if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
-    break;
-    }
+    if (type == opt_bit)
+      {
+      int bit = 1 << ((ol->type >> 16) & 31);
+      int * ptr = data_block
+       ? (int *)(US data_block + ol->v.offset)
+       : (int *)ol->v.value;
+      if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
+      break;
+      }
 
-  /* Handle full BOOL types */
+    /* Handle full BOOL types */
 
-  if (data_block == NULL)
-    *((BOOL *)(ol->value)) = boolvalue;
-  else
-    *((BOOL *)(US data_block + (long int)(ol->value))) = boolvalue;
+    if (data_block)
+      *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
+    else
+      *((BOOL *)ol->v.value) = boolvalue;
 
-  /* Verify fudge */
+    /* Verify fudge */
 
-  if (type == opt_bool_verify)
-    {
-    sprintf(CS name2, "%.50s_recipient", name + offset);
-    ol2 = find_option(name2, oltop, last);
-    if (ol2 != NULL)
+    if (type == opt_bool_verify)
       {
-      if (data_block == NULL)
-        *((BOOL *)(ol2->value)) = boolvalue;
-      else
-        *((BOOL *)(US data_block + (long int)(ol2->value))) = boolvalue;
+      sprintf(CS name2, "%.50s_recipient", name + offset);
+      if ((ol2 = find_option(name2, oltop, last)))
+       if (data_block)
+         *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
+       else
+         *((BOOL *)ol2->v.value) = boolvalue;
       }
-    }
 
-  /* Note that opt_bool_set type is set, if there is somewhere to do so */
+    /* Note that opt_bool_set type is set, if there is somewhere to do so */
 
-  else if (type == opt_bool_set)
-    {
-    sprintf(CS name2, "*set_%.50s", name + offset);
-    ol2 = find_option(name2, oltop, last);
-    if (ol2 != NULL)
+    else if (type == opt_bool_set)
       {
-      if (data_block == NULL)
-        *((BOOL *)(ol2->value)) = TRUE;
-      else
-        *((BOOL *)(US data_block + (long int)(ol2->value))) = TRUE;
+      sprintf(CS name2, "*set_%.50s", name + offset);
+      if ((ol2 = find_option(name2, oltop, last)))
+       if (data_block)
+         *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
+       else
+         *((BOOL *)ol2->v.value) = TRUE;
       }
-    }
-  break;
+    break;
 
   /* Octal integer */
 
   case opt_octint:
-  intbase = 8;
-  inttype = US"octal ";
+    intbase = 8;
+    inttype = US"octal ";
 
   /*  Integer: a simple(ish) case; allow octal and hex formats, and
-  suffixes K, M and G. The different types affect output, not input. */
+  suffixes K, M, G, and T.  The different types affect output, not input. */
 
   case opt_mkint:
   case opt_int:
-    {
-    uschar *endptr;
-    long int lvalue;
+     {
+      uschar *endptr;
+      long int lvalue;
 
-    errno = 0;
-    lvalue = strtol(CS s, CSS &endptr, intbase);
+      errno = 0;
+      lvalue = strtol(CS s, CSS &endptr, intbase);
 
-    if (endptr == s)
-      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
-        inttype, name);
+      if (endptr == s)
+       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
+         inttype, name);
 
-    if (errno != ERANGE && *endptr)
-      {
-      uschar * mp = US"GgMmKk\0";      /* YyZzEePpTtGgMmKk */
-
-      if ((mp = Ustrchr(mp, *endptr)))
+      if (errno != ERANGE && *endptr)
        {
-       endptr++;
-       do
+       uschar * mp = US"TtGgMmKk\0";   /* YyZzEePpTtGgMmKk */
+
+       if ((mp = Ustrchr(mp, *endptr)))
          {
-         if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+         endptr++;
+         do
            {
-           errno = ERANGE;
-           break;
+           if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+             {
+             errno = ERANGE;
+             break;
+             }
+           lvalue *= 1024;
            }
-         lvalue *= 1024;
+         while (*(mp += 2));
          }
-       while (*(mp += 2));
        }
-      }
 
-    if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
-      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-        "absolute value of integer \"%s\" is too large (overflow)", s);
+      if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
+       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+         "absolute value of integer \"%s\" is too large (overflow)", s);
 
-    while (isspace(*endptr)) endptr++;
-    if (*endptr)
-      extra_chars_error(endptr, inttype, US"integer value for ", name);
+      if (Uskip_whitespace(&endptr))
+       extra_chars_error(endptr, inttype, US"integer value for ", name);
 
-    value = (int)lvalue;
-    }
+      value = (int)lvalue;
+     }
 
-  if (data_block)
-    *(int *)(US data_block + (long int)ol->value) = value;
-  else
-    *(int *)ol->value = value;
-  break;
+    if (data_block)
+      *(int *)(US data_block + ol->v.offset) = value;
+    else
+      *(int *)ol->v.value = value;
+    break;
 
-  /*  Integer held in K: again, allow octal and hex formats, and suffixes K, M,
-  G and T. */
+  /*  Integer held in K: again, allow formats and suffixes as above. */
 
   case opt_Kint:
     {
@@ -2193,12 +2285,12 @@ switch (type)
 
     if (errno != ERANGE && *endptr)
       {
-      uschar * mp = US"EePpTtGgMmKk\0";        /* YyZzEePpTtGgMmKk */
+      uschar * mp = US"ZzEePpTtGgMmKk\0";      /* YyZzEePpTtGgMmKk */
 
       if ((mp = Ustrchr(mp, *endptr)))
        {
        endptr++;
-       do
+       while (*(mp += 2))
          {
          if (lvalue > EXIM_ARITH_MAX/1024 || lvalue < EXIM_ARITH_MIN/1024)
            {
@@ -2207,7 +2299,6 @@ switch (type)
            }
          lvalue *= 1024;
          }
-       while (*(mp += 2));
        }
       else
        lvalue = (lvalue + 512)/1024;
@@ -2216,70 +2307,69 @@ switch (type)
     if (errno == ERANGE) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "absolute value of integer \"%s\" is too large (overflow)", s);
 
-    while (isspace(*endptr)) endptr++;
-    if (*endptr != 0)
+    if (Uskip_whitespace(&endptr))
       extra_chars_error(endptr, inttype, US"integer value for ", name);
 
     if (data_block)
-      *(int_eximarith_t *)(US data_block + (long int)ol->value) = lvalue;
+      *(int_eximarith_t *)(US data_block + ol->v.offset) = lvalue;
     else
-      *(int_eximarith_t *)ol->value = lvalue;
+      *(int_eximarith_t *)ol->v.value = lvalue;
     break;
     }
 
   /*  Fixed-point number: held to 3 decimal places. */
 
   case opt_fixed:
-  if (sscanf(CS s, "%d%n", &value, &count) != 1)
-    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-      "fixed-point number expected for %s", name);
+    if (sscanf(CS s, "%d%n", &value, &count) != 1)
+      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+       "fixed-point number expected for %s", name);
 
-  if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-    "integer \"%s\" is too large (overflow)", s);
+    if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+      "integer \"%s\" is too large (overflow)", s);
 
-  value *= 1000;
+    value *= 1000;
 
-  if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-    "integer \"%s\" is too large (overflow)", s);
+    if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+      "integer \"%s\" is too large (overflow)", s);
 
-  /* We get a coverity error here for using count, as it derived
-  from the tainted buffer pointed to by s, as parsed by sscanf().
-  By the definition of sscanf we must be accessing between start
-  and end of s (assuming it is nul-terminated...) so ignore the error.  */
-  /* coverity[tainted_data] */
-  if (s[count] == '.')
-    {
-    int d = 100;
-    while (isdigit(s[++count]))
+    /* We get a coverity error here for using count, as it derived
+    from the tainted buffer pointed to by s, as parsed by sscanf().
+    By the definition of sscanf we must be accessing between start
+    and end of s (assuming it is nul-terminated...) so ignore the error.  */
+    /* coverity[tainted_data] */
+    if (s[count] == '.')
       {
-      value += (s[count] - '0') * d;
-      d /= 10;
+      int d = 100;
+      while (isdigit(s[++count]))
+       {
+       value += (s[count] - '0') * d;
+       d /= 10;
+       }
       }
-    }
 
-  while (isspace(s[count])) count++;
+    while (isspace(s[count])) count++;
 
-  if (s[count] != 0)
-    extra_chars_error(s+count, US"fixed-point value for ", name, US"");
+    if (s[count] != 0)
+      extra_chars_error(s+count, US"fixed-point value for ", name, US"");
 
-  if (data_block == NULL)
-    *((int *)(ol->value)) = value;
-  else
-    *((int *)(US data_block + (long int)(ol->value))) = value;
-  break;
+    if (data_block)
+      *((int *)(US data_block + ol->v.offset)) = value;
+    else
+      *((int *)ol->v.value) = value;
+    break;
 
   /* There's a special routine to read time values. */
 
   case opt_time:
-  value = readconf_readtime(s, 0, FALSE);
-  if (value < 0)
-    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
-      name);
-  if (data_block == NULL)
-    *((int *)(ol->value)) = value;
-  else
-    *((int *)(US data_block + (long int)(ol->value))) = value;
-  break;
+    value = readconf_readtime(s, 0, FALSE);
+    if (value < 0)
+      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
+       name);
+    if (data_block)
+      *((int *)(US data_block + ol->v.offset)) = value;
+    else
+      *((int *)ol->v.value) = value;
+    break;
 
   /* A time list is a list of colon-separated times, with the first
   element holding the size of the list and the second the number of
@@ -2288,9 +2378,9 @@ switch (type)
   case opt_timelist:
     {
     int count = 0;
-    int *list = (data_block == NULL)?
-      (int *)(ol->value) :
-      (int *)(US data_block + (long int)(ol->value));
+    int * list = data_block
+      ? (int *)(US data_block + ol->v.offset)
+      : (int *)ol->v.value;
 
     if (*s != 0) for (count = 1; count <= list[0] - 2; count++)
       {
@@ -2312,7 +2402,7 @@ switch (type)
       list[count+1] = value;
       if (snext == NULL) break;
       s = snext + 1;
-      while (isspace(*s)) s++;
+      Uskip_whitespace(&s);
       }
 
     if (count > list[0] - 2)
@@ -2324,11 +2414,8 @@ switch (type)
     }
 
   case opt_func:
-    {
-    void (*fn)() = ol->value;
-    fn(name, s);
+    ol->v.fn(name, s, 0);
     break;
-    }
   }
 
 return TRUE;
@@ -2352,11 +2439,7 @@ readconf_printtime(int t)
 int s, m, h, d, w;
 uschar *p = time_buffer;
 
-if (t < 0)
-  {
-  *p++ = '-';
-  t = -t;
-  }
+if (t < 0) *p++ = '-', t = -t;
 
 s = t % 60;
 t /= 60;
@@ -2403,7 +2486,7 @@ Returns:         boolean success
 */
 
 static BOOL
-print_ol(optionlist *ol, uschar *name, void *options_block,
+print_ol(optionlist *ol, const uschar *name, void *options_block,
   optionlist *oltop, int last, BOOL no_labels)
 {
 struct passwd *pw;
@@ -2413,7 +2496,7 @@ void *value;
 uid_t *uidlist;
 gid_t *gidlist;
 uschar *s;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
 
 if (!ol)
   {
@@ -2427,15 +2510,15 @@ if (!ol)
 if (!f.admin_user && ol->type & opt_secure)
   {
   if (no_labels)
-    printf("%s\n", hidden);
+    printf("%s\n", CCS hidden);
   else
-    printf("%s = %s\n", name, hidden);
+    printf("%s = %s\n", name, CCS hidden);
   return TRUE;
   }
 
 /* Else show the value of the option */
 
-value = ol->value;
+value = ol->v.value;
 if (options_block)
   {
   if (!(ol->type & opt_public))
@@ -2449,7 +2532,7 @@ switch(ol->type & opt_mask)
   case opt_rewrite:        /* Show the text value */
     s = *(USS value);
     if (!no_labels) printf("%s = ", name);
-    printf("%s\n", s ? string_printing2(s, FALSE) : US"");
+    printf("%s\n", s ? string_printing2(s, SP_TAB) : US"");
     break;
 
   case opt_int:
@@ -2485,6 +2568,7 @@ switch(ol->type & opt_mask)
     int_eximarith_t x = *((int_eximarith_t *)value);
     if (!no_labels) printf("%s = ", name);
     if (x == 0) printf("0\n");
+    else if ((x & ((1<<30)-1)) == 0) printf(PR_EXIM_ARITH "T\n", x >> 30);
     else if ((x & ((1<<20)-1)) == 0) printf(PR_EXIM_ARITH "G\n", x >> 20);
     else if ((x & ((1<<10)-1)) == 0) printf(PR_EXIM_ARITH "M\n", x >> 10);
     else printf(PR_EXIM_ARITH "K\n", x);
@@ -2527,10 +2611,10 @@ switch(ol->type & opt_mask)
       sprintf(CS name2, "*expand_%.50s", name);
       if ((ol2 = find_option(name2, oltop, last)))
        {
-       void *value2 = ol2->value;
        if (options_block)
-         value2 = (void *)(US options_block + (long int)value2);
-       s = *(USS value2);
+         s = *USS (US options_block + ol2->v.offset);
+       else
+         s = *USS ol2->v.value;
        if (!no_labels) printf("%s = ", name);
        printf("%s\n", s ? string_printing(s) : US"");
        break;
@@ -2559,10 +2643,10 @@ switch(ol->type & opt_mask)
       if (  (ol2 = find_option(name2, oltop, last))
         && (ol2->type & opt_mask) == opt_stringptr)
        {
-       void *value2 = ol2->value;
        if (options_block)
-         value2 = (void *)(US options_block + (long int)value2);
-       s = *(USS value2);
+         s = *USS (US options_block + ol2->v.offset);
+       else
+         s = *USS ol2->v.value;
        if (!no_labels) printf("%s = ", name);
        printf("%s\n", s ? string_printing(s) : US"");
        break;
@@ -2587,9 +2671,8 @@ switch(ol->type & opt_mask)
     if (!no_labels) printf("%s =", name);
     if (uidlist)
       {
-      int i;
       uschar sep = no_labels ? '\0' : ' ';
-      for (i = 1; i <= (int)(uidlist[0]); i++)
+      for (int i = 1; i <= (int)(uidlist[0]); i++)
        {
        uschar *name = NULL;
        if ((pw = getpwuid(uidlist[i]))) name = US pw->pw_name;
@@ -2607,9 +2690,8 @@ switch(ol->type & opt_mask)
     if (!no_labels) printf("%s =", name);
     if (gidlist)
       {
-      int i;
       uschar sep = no_labels ? '\0' : ' ';
-      for (i = 1; i <= (int)(gidlist[0]); i++)
+      for (int i = 1; i <= (int)(gidlist[0]); i++)
        {
        uschar *name = NULL;
        if ((gr = getgrgid(gidlist[i]))) name = US gr->gr_name;
@@ -2629,28 +2711,27 @@ switch(ol->type & opt_mask)
 
   case opt_timelist:
     {
-    int i;
     int *list = (int *)value;
     if (!no_labels) printf("%s = ", name);
-    for (i = 0; i < list[1]; i++)
+    for (int i = 0; i < list[1]; i++)
       printf("%s%s", i == 0 ? "" : ":", readconf_printtime(list[i+2]));
     printf("\n");
     }
     break;
 
   case opt_bit:
-    printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))?
-      "" : "no_", name);
+    printf("%s%s\n", (*((int *)value)) & (1 << ((ol->type >> 16) & 31))
+      "" : "no_", name);
     break;
 
   case opt_expand_bool:
     sprintf(CS name2, "*expand_%.50s", name);
-    if ((ol2 = find_option(name2, oltop, last)) && ol2->value)
+    if ((ol2 = find_option(name2, oltop, last)) && ol2->v.value)
       {
-      void *value2 = ol2->value;
       if (options_block)
-       value2 = (void *)(US options_block + (long int)value2);
-      s = *(USS value2);
+       s = *USS (US options_block + ol2->v.offset);
+      else
+       s = *USS ol2->v.value;
       if (s)
        {
        if (!no_labels) printf("%s = ", name);
@@ -2665,7 +2746,11 @@ switch(ol->type & opt_mask)
   case opt_bool:
   case opt_bool_verify:
   case opt_bool_set:
-    printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name);
+    printf("%s%s\n", *((BOOL *)value) ? "" : "no_", name);
+    break;
+
+  case opt_func:
+    ol->v.fn(name, NULL, no_labels ? opt_fn_print : opt_fn_print|opt_fn_print_label);
     break;
   }
 return TRUE;
@@ -2711,20 +2796,17 @@ Returns:      Boolean success
 */
 
 BOOL
-readconf_print(uschar *name, uschar *type, BOOL no_labels)
+readconf_print(const uschar * name, const uschar * type, BOOL no_labels)
 {
 BOOL names_only = FALSE;
-optionlist *ol;
 optionlist *ol2 = NULL;
 driver_instance *d = NULL;
-macro_item *m;
 int size = 0;
 
 if (!type)
   {
   if (*name == '+')
     {
-    int i;
     tree_node *t;
     BOOL found = FALSE;
     static uschar *types[] = { US"address", US"domain", US"host",
@@ -2732,15 +2814,16 @@ if (!type)
     static tree_node **anchors[] = { &addresslist_anchor, &domainlist_anchor,
       &hostlist_anchor, &localpartlist_anchor };
 
-    for (i = 0; i < 4; i++)
+    for (int i = 0; i < 4; i++)
       if ((t = tree_search(*(anchors[i]), name+1)))
         {
+       namedlist_block * nb = t->data.ptr;
+       const uschar * s = nb->hide ? hidden : nb->string;
         found = TRUE;
         if (no_labels)
-          printf("%s\n", ((namedlist_block *)(t->data.ptr))->string);
+          printf("%s\n", CCS s);
         else
-          printf("%slist %s = %s\n", types[i], name+1,
-            ((namedlist_block *)(t->data.ptr))->string);
+          printf("%slist %s = %s\n", types[i], name+1, CCS s);
         }
 
     if (!found)
@@ -2759,7 +2842,7 @@ if (!type)
 
   if (Ustrcmp(name, "all") == 0)
     {
-    for (ol = optionlist_config;
+    for (optionlist * ol = optionlist_config;
          ol < optionlist_config + nelem(optionlist_config); ol++)
       if (!(ol->type & opt_hidden))
         (void) print_ol(ol, US ol->name, NULL,
@@ -2774,7 +2857,7 @@ if (!type)
     printf("local_scan() options are not supported\n");
     return FALSE;
 #else
-    for (ol = local_scan_options;
+    for (optionlist * ol = local_scan_options;
          ol < local_scan_options + local_scan_options_count; ol++)
       (void) print_ol(ol, US ol->name, NULL, local_scan_options,
                  local_scan_options_count, no_labels);
@@ -2852,7 +2935,7 @@ if (!type)
 
   else
     return print_ol(find_option(name,
-      optionlist_config, nelem(optionlist_config)),
+                     optionlist_config, nelem(optionlist_config)),
       name, NULL, optionlist_config, nelem(optionlist_config), no_labels);
   }
 
@@ -2887,10 +2970,10 @@ else if (Ustrcmp(type, "macro") == 0)
   for printing.  So we have an admin_users restriction. */
   if (!f.admin_user)
     {
-    fprintf(stderr, "exim: permission denied\n");
+    fprintf(stderr, "exim: permission denied; not admin\n");
     return FALSE;
     }
-  for (m = macros; m; m = m->next)
+  for (macro_item * m = macros; m; m = m->next)
     if (!name || Ustrcmp(name, m->name) == 0)
       {
       if (names_only)
@@ -2923,11 +3006,11 @@ for (; d; d = d->next)
     printf("\n%s %s:\n", d->name, type);
   else if (Ustrcmp(d->name, name) != 0) continue;
 
-  for (ol = ol2; ol < ol2 + size; ol++)
+  for (optionlist * ol = ol2; ol < ol2 + size; ol++)
     if (!(ol->type & opt_hidden))
       rc |= print_ol(ol, US ol->name, d, ol2, size, no_labels);
 
-  for (ol = d->info->options;
+  for (optionlist * ol = d->info->options;
        ol < d->info->options + *(d->info->options_count); ol++)
     if (!(ol->type & opt_hidden))
       rc |= print_ol(ol, US ol->name, d, d->info->options,
@@ -2961,18 +3044,19 @@ Arguments:
   s           the text of the option line, starting immediately after the name
                 of the list type
   tname       the name of the list type, for messages
+  hide       do not output value on "-bP"
 
 Returns:      nothing
 */
 
 static void
 read_named_list(tree_node **anchorp, int *numberp, int max, uschar *s,
-  uschar *tname)
+  uschar *tname, BOOL hide)
 {
 BOOL forcecache = FALSE;
 uschar *ss;
 tree_node *t;
-namedlist_block *nb = store_get(sizeof(namedlist_block));
+namedlist_block * nb = store_get_perm(sizeof(namedlist_block), FALSE);
 
 if (Ustrncmp(s, "_cache", 6) == 0)
   {
@@ -2987,13 +3071,13 @@ if (*numberp >= max)
  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "too many named %ss (max is %d)\n",
    tname, max);
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 ss = s;
 while (isalnum(*s) || *s == '_') s++;
-t = store_get(sizeof(tree_node) + s-ss);
+t = store_get(sizeof(tree_node) + s-ss, ss);
 Ustrncpy(t->name, ss, s-ss);
 t->name[s-ss] = 0;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
 if (!tree_insertnode(anchorp, t))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -3002,10 +3086,11 @@ if (!tree_insertnode(anchorp, t))
 t->data.ptr = nb;
 nb->number = *numberp;
 *numberp += 1;
+nb->hide = hide;
 
 if (*s++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
   "missing '=' after \"%s\"", t->name);
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 nb->string = read_string(s, t->name);
 nb->cache_data = NULL;
 
@@ -3058,80 +3143,6 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malformed ratelimit data: %s", s);
 
 
 
-/*************************************************
-*       Drop privs for checking TLS config      *
-*************************************************/
-
-/* We want to validate TLS options during readconf, but do not want to be
-root when we call into the TLS library, in case of library linkage errors
-which cause segfaults; before this check, those were always done as the Exim
-runtime user and it makes sense to continue with that.
-
-Assumes:  tls_require_ciphers has been set, if it will be
-          exim_user has been set, if it will be
-          exim_group has been set, if it will be
-
-Returns:  bool for "okay"; false will cause caller to immediately exit.
-*/
-
-#ifdef SUPPORT_TLS
-static BOOL
-tls_dropprivs_validate_require_cipher(BOOL nowarn)
-{
-const uschar *errmsg;
-pid_t pid;
-int rc, status;
-void (*oldsignal)(int);
-
-/* If TLS will never be used, no point checking ciphers */
-
-if (  !tls_advertise_hosts
-   || !*tls_advertise_hosts
-   || Ustrcmp(tls_advertise_hosts, ":") == 0
-   )
-  return TRUE;
-else if (!nowarn && !tls_certificate)
-  log_write(0, LOG_MAIN,
-    "Warning: No server certificate defined; will use a selfsigned one.\n"
-    " Suggested action: either install a certificate or change tls_advertise_hosts option");
-
-oldsignal = signal(SIGCHLD, SIG_DFL);
-
-fflush(NULL);
-if ((pid = fork()) < 0)
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
-
-if (pid == 0)
-  {
-  /* in some modes, will have dropped privilege already */
-  if (!geteuid())
-    exim_setugid(exim_uid, exim_gid, FALSE,
-        US"calling tls_validate_require_cipher");
-
-  if ((errmsg = tls_validate_require_cipher()))
-    log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-        "tls_require_ciphers invalid: %s", errmsg);
-  fflush(NULL);
-  _exit(0);
-  }
-
-do {
-  rc = waitpid(pid, &status, 0);
-} while (rc < 0 && errno == EINTR);
-
-DEBUG(D_tls)
-  debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
-      (int)pid, status);
-
-signal(SIGCHLD, oldsignal);
-
-return status == 0;
-}
-#endif /* SUPPORT_TLS */
-
-
-
-
 /*************************************************
 *         Read main configuration options        *
 *************************************************/
@@ -3165,65 +3176,65 @@ readconf_main(BOOL nowarn)
 {
 int sep = 0;
 struct stat statbuf;
-uschar *s, *filename;
-const uschar *list = config_main_filelist;
+uschar * s, * filename;
+const uschar * list = config_main_filelist;
 
 /* Loop through the possible file names */
 
+/* Should never be a tainted list */
 while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
   {
 
   /* Cut out all the fancy processing unless specifically wanted */
 
-  #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
+#if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
   uschar *suffix = filename + Ustrlen(filename);
 
   /* Try for the node-specific file if a node name exists */
 
-  #ifdef CONFIGURE_FILE_USE_NODE
+ifdef CONFIGURE_FILE_USE_NODE
   struct utsname uts;
   if (uname(&uts) >= 0)
     {
-    #ifdef CONFIGURE_FILE_USE_EUID
+#  ifdef CONFIGURE_FILE_USE_EUID
     sprintf(CS suffix, ".%ld.%.256s", (long int)original_euid, uts.nodename);
-    config_file = Ufopen(filename, "rb");
-    if (config_file == NULL)
-    #endif  /* CONFIGURE_FILE_USE_EUID */
+    if (!(config_file = Ufopen(filename, "rb")))
+#  endif  /* CONFIGURE_FILE_USE_EUID */
       {
       sprintf(CS suffix, ".%.256s", uts.nodename);
       config_file = Ufopen(filename, "rb");
       }
     }
-  #endif  /* CONFIGURE_FILE_USE_NODE */
+endif  /* CONFIGURE_FILE_USE_NODE */
 
   /* Otherwise, try the generic name, possibly with the euid added */
 
-  #ifdef CONFIGURE_FILE_USE_EUID
-  if (config_file == NULL)
+ifdef CONFIGURE_FILE_USE_EUID
+  if (!config_file)
     {
     sprintf(CS suffix, ".%ld", (long int)original_euid);
     config_file = Ufopen(filename, "rb");
     }
-  #endif  /* CONFIGURE_FILE_USE_EUID */
+endif  /* CONFIGURE_FILE_USE_EUID */
 
   /* Finally, try the unadorned name */
 
-  if (config_file == NULL)
+  if (!config_file)
     {
     *suffix = 0;
     config_file = Ufopen(filename, "rb");
     }
-  #else  /* if neither defined */
+#else  /* if neither defined */
 
   /* This is the common case when the fancy processing is not included. */
 
   config_file = Ufopen(filename, "rb");
-  #endif
+#endif
 
   /* If the file does not exist, continue to try any others. For any other
   error, break out (and die). */
 
-  if (config_file != NULL || errno != ENOENT) break;
+  if (config_file || errno != ENOENT) break;
   }
 
 /* On success, save the name for verification; config_filename is used when
@@ -3246,39 +3257,37 @@ if (config_file)
     config_main_directory = last_slash == filename ? US"/" : string_copyn(filename, last_slash - filename);
   else
     {
-      /* relative configuration file name: working dir + / + basename(filename) */
+    /* relative configuration file name: working dir + / + basename(filename) */
 
-      uschar buf[PATH_MAX];
-      gstring * g;
+    uschar buf[PATH_MAX];
+    gstring * g;
 
-      if (os_getcwd(buf, PATH_MAX) == NULL)
-        {
-        perror("exim: getcwd");
-        exit(EXIT_FAILURE);
-        }
-      g = string_cat(NULL, buf);
+    if (os_getcwd(buf, PATH_MAX) == NULL)
+      {
+      perror("exim: getcwd");
+      exit(EXIT_FAILURE);
+      }
+    g = string_cat(NULL, buf);
 
-      /* If the dir does not end with a "/", append one */
-      if (g->s[g->ptr-1] != '/')
-        g = string_catn(g, US"/", 1);
+    /* If the dir does not end with a "/", append one */
+    if (gstring_last_char(g) != '/')
+      g = string_catn(g, US"/", 1);
 
-      /* If the config file contains a "/", extract the directory part */
-      if (last_slash)
-        g = string_catn(g, filename, last_slash - filename);
+    /* If the config file contains a "/", extract the directory part */
+    if (last_slash)
+      g = string_catn(g, filename, last_slash - filename);
 
-      config_main_directory = string_from_gstring(g);
+    config_main_directory = string_from_gstring(g);
     }
   config_directory = config_main_directory;
   }
 else
-  {
-  if (filename == NULL)
+  if (!filename)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "non-existent configuration file(s): "
       "%s", config_main_filelist);
   else
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", string_open_failed(errno,
-      "configuration file %s", filename));
-  }
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
+      string_open_failed("configuration file %s", filename));
 
 /* Now, once we found and opened our configuration file, we change the directory
 to a safe place. Later we change to $spool_directory. */
@@ -3298,21 +3307,34 @@ if (f.trusted_config && Ustrcmp(filename, US"/dev/null"))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to stat configuration file %s",
       big_buffer);
 
-  if ((statbuf.st_uid != root_uid                /* owner not root */
-       #ifdef CONFIGURE_OWNER
-       && statbuf.st_uid != config_uid           /* owner not the special one */
-       #endif
-         ) ||                                    /* or */
-      (statbuf.st_gid != root_gid                /* group not root & */
-       #ifdef CONFIGURE_GROUP
-       && statbuf.st_gid != config_gid           /* group not the special one */
-       #endif
-       && (statbuf.st_mode & 020) != 0) ||       /* group writeable  */
-                                                 /* or */
-      ((statbuf.st_mode & 2) != 0))              /* world writeable  */
-
+  if (    statbuf.st_uid != root_uid           /* owner not root */
+#ifdef CONFIGURE_OWNER
+       && statbuf.st_uid != config_uid         /* owner not the special one */
+#endif
+     ||                                                /* or */
+         statbuf.st_gid != root_gid            /* group not root & */
+#ifdef CONFIGURE_GROUP
+       && statbuf.st_gid != config_gid         /* group not the special one */
+#endif
+       && (statbuf.st_mode & 020) != 0         /* group writeable  */
+     ||                                                /* or */
+       (statbuf.st_mode & 2) != 0              /* world writeable  */
+     )
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Exim configuration file %s has the "
       "wrong owner, group, or mode", big_buffer);
+
+  /* Do a dummy store-allocation of a size related to the (toplevel) file size.
+  This assumes we will need this much storage to handle all the allocations
+  during startup; it won't help when .include is being used.  When it does, it
+  will cut down on the number of store blocks (and malloc calls, and sbrk
+  syscalls).  It also assume we're on the relevant pool. */
+
+  if (statbuf.st_size > 8192)
+    {
+    rmark r = store_mark();
+    void * dummy = store_get((int)statbuf.st_size, GET_UNTAINTED);
+    store_reset(r);
+    }
   }
 
 /* Process the main configuration settings. They all begin with a lower case
@@ -3321,28 +3343,36 @@ a macro definition. */
 
 while ((s = get_config_line()))
   {
+  BOOL hide;
+  uschar * t;
+
   if (config_lineno == 1 && Ustrstr(s, "\xef\xbb\xbf") == s)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "found unexpected BOM (Byte Order Mark)");
 
-  if (isupper(s[0]))
-    { if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE, US""); }
+  if (isupper(*s))
+    {
+    if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE);
+    continue;
+    }
+
+  t = (hide = Ustrncmp(s, "hide", 4) == 0 && isspace(s[4])) ? s + 5 : s;
 
-  else if (Ustrncmp(s, "domainlist", 10) == 0)
+  if (Ustrncmp(t, "domainlist", 10) == 0)
     read_named_list(&domainlist_anchor, &domainlist_count,
-      MAX_NAMED_LIST, s+10, US"domain list");
+      MAX_NAMED_LIST, t+10, US"domain list", hide);
 
-  else if (Ustrncmp(s, "hostlist", 8) == 0)
+  else if (Ustrncmp(t, "hostlist", 8) == 0)
     read_named_list(&hostlist_anchor, &hostlist_count,
-      MAX_NAMED_LIST, s+8, US"host list");
+      MAX_NAMED_LIST, t+8, US"host list", hide);
 
-  else if (Ustrncmp(s, US"addresslist", 11) == 0)
+  else if (Ustrncmp(t, "addresslist", 11) == 0)
     read_named_list(&addresslist_anchor, &addresslist_count,
-      MAX_NAMED_LIST, s+11, US"address list");
+      MAX_NAMED_LIST, t+11, US"address list", hide);
 
-  else if (Ustrncmp(s, US"localpartlist", 13) == 0)
+  else if (Ustrncmp(t, "localpartlist", 13) == 0)
     read_named_list(&localpartlist_anchor, &localpartlist_count,
-      MAX_NAMED_LIST, s+13, US"local part list");
+      MAX_NAMED_LIST, t+13, US"local part list", hide);
 
   else
     (void) readconf_handle_option(s, optionlist_config, optionlist_config_size,
@@ -3359,7 +3389,7 @@ if (local_sender_retain && local_from_check)
 /* If the timezone string is empty, set it to NULL, implying no TZ variable
 wanted. */
 
-if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL;
+if (timezone_string && !*timezone_string) timezone_string = NULL;
 
 /* The max retry interval must not be greater than 24 hours. */
 
@@ -3380,10 +3410,11 @@ but if that yields an unqualified value, make a FQDN by using gethostbyname to
 canonize it. Some people like upper case letters in their host names, so we
 don't force the case. */
 
-if (primary_hostname == NULL)
+if (!primary_hostname)
   {
-  const uschar *hostname;
+  const uschar * hostname;
   struct utsname uts;
+
   if (uname(&uts) < 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "uname() failed to yield host name");
   hostname = US uts.nodename;
@@ -3393,33 +3424,29 @@ if (primary_hostname == NULL)
     int af = AF_INET;
     struct hostent *hostdata;
 
-    #if HAVE_IPV6
-    if (!disable_ipv6 && (dns_ipv4_lookup == NULL ||
-         match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
+#if HAVE_IPV6
+    if (  !disable_ipv6
+       && (  !dns_ipv4_lookup
+         || match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
            MCL_DOMAIN, TRUE, NULL) != OK))
       af = AF_INET6;
-    #else
-    af = AF_INET;
-    #endif
+#endif
 
     for (;;)
       {
-      #if HAVE_IPV6
-        #if HAVE_GETIPNODEBYNAME
+#if HAVE_IPV6
+if HAVE_GETIPNODEBYNAME
         int error_num;
         hostdata = getipnodebyname(CS hostname, af, 0, &error_num);
         #else
         hostdata = gethostbyname2(CS hostname, af);
-        #endif
-      #else
+endif
+#else
       hostdata = gethostbyname(CS hostname);
-      #endif
+#endif
 
-      if (hostdata != NULL)
-        {
-        hostname = US hostdata->h_name;
-        break;
-        }
+      if (hostdata)
+        { hostname = US hostdata->h_name; break; }
 
       if (af == AF_INET) break;
       af = AF_INET;
@@ -3437,15 +3464,14 @@ smtp_active_hostname = primary_hostname;
 got set above. Of course, writing to the log may not work if log_file_path is
 not set, but it will at least get to syslog or somewhere, with any luck. */
 
-if (*spool_directory == 0)
+if (!*spool_directory)
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "spool_directory undefined: cannot "
     "proceed");
 
 /* Expand the spool directory name; it may, for example, contain the primary
 host name. Same comment about failure. */
 
-s = expand_string(spool_directory);
-if (s == NULL)
+if (!(s = expand_string(spool_directory)))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand spool_directory "
     "\"%s\": %s", spool_directory, expand_string_message);
 spool_directory = s;
@@ -3454,32 +3480,28 @@ spool_directory = s;
 the null string or "syslog". It is also allowed to contain one instance of %D
 or %M. However, it must NOT contain % followed by anything else. */
 
-if (*log_file_path != 0)
+if (*log_file_path)
   {
   const uschar *ss, *sss;
   int sep = ':';                       /* Fixed for log file path */
-  s = expand_string(log_file_path);
-  if (s == NULL)
+  if (!(s = expand_string(log_file_path)))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand log_file_path "
       "\"%s\": %s", log_file_path, expand_string_message);
 
   ss = s;
-  while ((sss = string_nextinlist(&ss,&sep,big_buffer,big_buffer_size)) != NULL)
+  /* should never be a tainted list */
+  while ((sss = string_nextinlist(&ss, &sep, big_buffer, big_buffer_size)))
     {
     uschar *t;
     if (sss[0] == 0 || Ustrcmp(sss, "syslog") == 0) continue;
-    t = Ustrstr(sss, "%s");
-    if (t == NULL)
+    if (!(t = Ustrstr(sss, "%s")))
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_file_path \"%s\" does not "
         "contain \"%%s\"", sss);
     *t = 'X';
-    t = Ustrchr(sss, '%');
-    if (t != NULL)
-      {
+    if ((t = Ustrchr(sss, '%')))
       if ((t[1] != 'D' && t[1] != 'M') || Ustrchr(t+2, '%') != NULL)
         log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_file_path \"%s\" contains "
           "unexpected \"%%\" character", s);
-      }
     }
 
   log_file_path = s;
@@ -3492,7 +3514,7 @@ leading "log_". */
 if (syslog_facility_str)
   {
   int i;
-  uschar *s = syslog_facility_str;
+  uschar * s = syslog_facility_str;
 
   if ((Ustrlen(syslog_facility_str) >= 4) &&
         (strncmpic(syslog_facility_str, US"log_", 4) == 0))
@@ -3512,23 +3534,24 @@ if (syslog_facility_str)
 
 /* Expand pid_file_path */
 
-if (*pid_file_path != 0)
+if (*pid_file_path)
   {
-  if (!(s = expand_string(pid_file_path)))
+  const uschar * t = expand_cstring(pid_file_path);
+  if (!t)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path "
       "\"%s\": %s", pid_file_path, expand_string_message);
-  pid_file_path = s;
+  pid_file_path = t;
   }
 
 /* Set default value of process_log_path */
 
-if (!process_log_path || *process_log_path =='\0')
+if (!process_log_path || !*process_log_path)
   process_log_path = string_sprintf("%s/exim-process.info", spool_directory);
 
 /* Compile the regex for matching a UUCP-style "From_" line in an incoming
 message. */
 
-regex_From = regex_must_compile(uucp_from_pattern, FALSE, TRUE);
+regex_From = regex_must_compile(uucp_from_pattern, MCS_NOFLAGS, TRUE);
 
 /* Unpick the SMTP rate limiting options, if set */
 
@@ -3574,7 +3597,7 @@ if (errors_reply_to)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "error in errors_reply_to (%s): %s", errors_reply_to, errmess);
 
-  if (domain == 0)
+  if (!domain)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "errors_reply_to (%s) does not contain a domain", errors_reply_to);
   }
@@ -3582,8 +3605,7 @@ if (errors_reply_to)
 /* If smtp_accept_queue or smtp_accept_max_per_host is set, then
 smtp_accept_max must also be set. */
 
-if (smtp_accept_max == 0 &&
-    (smtp_accept_queue > 0 || smtp_accept_max_per_host != NULL))
+if (smtp_accept_max == 0 && (smtp_accept_queue > 0 || smtp_accept_max_per_host))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "smtp_accept_max must be set if smtp_accept_queue or "
     "smtp_accept_max_per_host is set");
@@ -3603,8 +3625,7 @@ if (host_number_string)
         "failed to expand localhost_number \"%s\": %s",
         host_number_string, expand_string_message);
   n = Ustrtol(s, &end, 0);
-  while (isspace(*end)) end++;
-  if (*end != 0)
+  if (Uskip_whitespace(&end))
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "localhost_number value is not a number: %s", s);
   if (n > LOCALHOST_MAX)
@@ -3614,7 +3635,7 @@ if (host_number_string)
   host_number = n;
   }
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /* If tls_verify_hosts is set, tls_verify_certificates must also be set */
 
 if ((tls_verify_hosts || tls_try_verify_hosts) && !tls_verify_certificates)
@@ -3622,11 +3643,6 @@ if ((tls_verify_hosts || tls_try_verify_hosts) && !tls_verify_certificates)
     "tls_%sverify_hosts is set, but tls_verify_certificates is not set",
     tls_verify_hosts ? "" : "try_");
 
-/* This also checks that the library linkage is working and we can call
-routines in it, so call even if tls_require_ciphers is unset */
-if (!tls_dropprivs_validate_require_cipher(nowarn))
-  exit(1);
-
 /* Magic number: at time of writing, 1024 has been the long-standing value
 used by so many clients, and what Exim used to use always, that it makes
 sense to just min-clamp this max-clamp at that. */
@@ -3647,7 +3663,7 @@ if (openssl_options)
       "openssl_options parse error: %s", openssl_options);
 # endif
   }
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
 
 if (!nowarn && !keep_environment && environ && *environ)
   log_write(0, LOG_MAIN,
@@ -3680,23 +3696,18 @@ static driver_info *
 init_driver(driver_instance *d, driver_info *drivers_available,
   int size_of_info, uschar *class)
 {
-driver_info *dd;
-
-for (dd = drivers_available; dd->driver_name[0] != 0;
+for (driver_info * dd = drivers_available; dd->driver_name[0] != 0;
      dd = (driver_info *)((US dd) + size_of_info))
-  {
   if (Ustrcmp(d->driver_name, dd->driver_name) == 0)
     {
-    int i;
     int len = dd->options_len;
     d->info = dd;
-    d->options_block = store_get(len);
+    d->options_block = store_get_perm(len, FALSE);
     memcpy(d->options_block, dd->options_block, len);
-    for (i = 0; i < *(dd->options_count); i++)
+    for (int i = 0; i < *(dd->options_count); i++)
       dd->options[i].type &= ~opt_set;
     return dd;
     }
-  }
 
 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
   "%s %s: cannot find %s driver \"%s\"", class, d->name, class, d->driver_name);
@@ -3707,6 +3718,16 @@ return NULL;   /* never obeyed */
 
 
 
+static void
+driver_init_fini(driver_instance * d, const uschar * class)
+{
+if (!d->driver_name)
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+    "no driver defined for %s \"%s\"", class, d->name);
+(d->info->init)(d);
+}
+
+
 /*************************************************
 *             Initialize driver list             *
 *************************************************/
@@ -3745,14 +3766,14 @@ readconf_driver_init(
   optionlist *driver_optionlist,
   int  driver_optionlist_count)
 {
-driver_instance **p = anchor;
-driver_instance *d = NULL;
-uschar *buffer;
+driver_instance ** p = anchor;
+driver_instance * d = NULL;
+uschar * buffer;
 
-while ((buffer = get_config_line()) != NULL)
+while ((buffer = get_config_line()))
   {
-  uschar name[64];
-  uschar *s;
+  uschar name[EXIM_DRIVERNAME_MAX];
+  const uschar * s;
 
   /* Read the first name on the line and test for the start of a new driver. A
   macro definition indicates the end of the previous driver. If this isn't the
@@ -3767,13 +3788,11 @@ while ((buffer = get_config_line()) != NULL)
     {
     if (d)
       {
-      if (!d->driver_name)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-          "no driver defined for %s \"%s\"", class, d->name);
-      (d->info->init)(d);
+      /* s is using big_buffer, so this call had better not */
+      driver_init_fini(d, class);
       d = NULL;
       }
-    if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE, US"");
+    if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE);
     continue;
     }
 
@@ -3783,17 +3802,10 @@ while ((buffer = get_config_line()) != NULL)
 
   if (*s++ == ':')
     {
-    int i;
-
     /* Finish off initializing the previous driver. */
 
     if (d)
-      {
-      if (!d->driver_name)
-        log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-          "no driver defined for %s \"%s\"", class, d->name);
-      (d->info->init)(d);
-      }
+      driver_init_fini(d, class);
 
     /* Check that we haven't already got a driver of this name */
 
@@ -3805,21 +3817,23 @@ while ((buffer = get_config_line()) != NULL)
     /* Set up a new driver instance data block on the chain, with
     its default values installed. */
 
-    d = store_get(instance_size);
+    d = store_get_perm(instance_size, FALSE);
     memcpy(d, instance_default, instance_size);
     *p = d;
     p = &d->next;
     d->name = string_copy(name);
+    d->srcfile = config_filename;
+    d->srcline = config_lineno; 
 
     /* Clear out the "set" bits in the generic options */
 
-    for (i = 0; i < driver_optionlist_count; i++)
+    for (int i = 0; i < driver_optionlist_count; i++)
       driver_optionlist[i].type &= ~opt_set;
 
     /* Check nothing more on this line, then do the next loop iteration. */
 
-    while (isspace(*s)) s++;
-    if (*s != 0) extra_chars_error(s, US"driver name ", name, US"");
+    Uskip_whitespace(&s);
+    if (*s) extra_chars_error(s, US"driver name ", name, US"");
     continue;
     }
 
@@ -3857,12 +3871,7 @@ while ((buffer = get_config_line()) != NULL)
 /* Run the initialization function for the final driver. */
 
 if (d)
-  {
-  if (!d->driver_name)
-    log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-      "no driver defined for %s \"%s\"", class, d->name);
-  (d->info->init)(d);
-  }
+  driver_init_fini(d, class);
 }
 
 
@@ -3886,26 +3895,23 @@ BOOL
 readconf_depends(driver_instance *d, uschar *s)
 {
 int count = *(d->info->options_count);
-optionlist *ol;
 uschar *ss;
 
-for (ol = d->info->options; ol < d->info->options + count; ol++)
-  {
-  void *options_block;
-  uschar *value;
-  int type = ol->type & opt_mask;
-  if (type != opt_stringptr) continue;
-  options_block = ((ol->type & opt_public) == 0)? d->options_block : (void *)d;
-  value = *(uschar **)(US options_block + (long int)(ol->value));
-  if (value != NULL && (ss = Ustrstr(value, s)) != NULL)
+for (optionlist * ol = d->info->options; ol < d->info->options + count; ol++)
+  if ((ol->type & opt_mask) == opt_stringptr)
     {
-    if (ss <= value || (ss[-1] != '$' && ss[-1] != '{') ||
-      isalnum(ss[Ustrlen(s)])) continue;
-    DEBUG(D_transport) debug_printf("driver %s: \"%s\" option depends on %s\n",
-      d->name, ol->name, s);
-    return TRUE;
+    void * options_block = ol->type & opt_public ? (void *)d : d->options_block;
+    uschar * value = *USS(US options_block + ol->v.offset);
+
+    if (value && (ss = Ustrstr(value, s)) != NULL)
+      {
+      if (ss <= value || (ss[-1] != '$' && ss[-1] != '{') ||
+       isalnum(ss[Ustrlen(s)])) continue;
+      DEBUG(D_transport) debug_printf("driver %s: \"%s\" option depends on %s\n",
+       d->name, ol->name, s);
+      return TRUE;
+      }
     }
-  }
 
 DEBUG(D_transport) debug_printf("driver %s does not depend on %s\n", d->name, s);
 return FALSE;
@@ -4067,18 +4073,17 @@ Returns:    time in seconds or fixed point number * 1000
 */
 
 static int
-retry_arg(const uschar **paddr, int type)
+retry_arg(const uschar ** paddr, int type)
 {
-const uschar *p = *paddr;
-const uschar *pp;
+const uschar * p = *paddr, * pp;
 
 if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected");
 
-while (isspace(*p)) p++;
+Uskip_whitespace(&p);
 pp = p;
 while (isalnum(*p) || (type == 1 && *p == '.')) p++;
 
-if (*p != 0 && !isspace(*p) && *p != ',' && *p != ';')
+if (*p && !isspace(*p) && *p != ',' && *p != ';')
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma or semicolon expected");
 
 *paddr = p;
@@ -4105,7 +4110,7 @@ while ((p = get_config_line()))
   const uschar *pp;
   uschar *error;
 
-  next = store_get(sizeof(retry_config));
+  next = store_get(sizeof(retry_config), GET_UNTAINTED);
   next->next = NULL;
   *chain = next;
   chain = &(next->next);
@@ -4115,7 +4120,7 @@ while ((p = get_config_line()))
   rchain = &(next->rules);
 
   next->pattern = string_dequote(&p);
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
   pp = p;
   while (mac_isgraph(*p)) p++;
   if (p - pp <= 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -4132,24 +4137,24 @@ while ((p = get_config_line()))
   fudge. Anything that is not a retry rule starting "F," or "G," is treated as
   an address list. */
 
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
   if (Ustrncmp(p, "senders", 7) == 0)
     {
     p += 7;
-    while (isspace(*p)) p++;
+    Uskip_whitespace(&p);
     if (*p++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "\"=\" expected after \"senders\" in retry rule");
-    while (isspace(*p)) p++;
+    Uskip_whitespace(&p);
     next->senders = string_dequote(&p);
     }
 
   /* Now the retry rules. Keep the maximum timeout encountered. */
 
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
 
-  while (*p != 0)
+  while (*p)
     {
-    retry_rule *rule = store_get(sizeof(retry_rule));
+    retry_rule * rule = store_get(sizeof(retry_rule), GET_UNTAINTED);
     *rchain = rule;
     rchain = &(rule->next);
     rule->next = NULL;
@@ -4180,13 +4185,12 @@ while ((p = get_config_line()))
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
         "bad parameters for retry rule");
 
-    while (isspace(*p)) p++;
-    if (*p == ';')
+    if (Uskip_whitespace(&p) == ';')
       {
       p++;
-      while (isspace(*p)) p++;
+      Uskip_whitespace(&p);
       }
-    else if (*p != 0)
+    else if (*p)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "semicolon expected");
     }
   }
@@ -4207,7 +4211,9 @@ Returns:     nothing
 static void
 auths_init(void)
 {
-auth_instance *au, *bu;
+#ifndef DISABLE_PIPE_CONNECT
+int nauths = 0;
+#endif
 
 readconf_driver_init(US"authenticator",
   (driver_instance **)(&auths),      /* chain anchor */
@@ -4218,25 +4224,44 @@ readconf_driver_init(US"authenticator",
   optionlist_auths,                  /* generic options */
   optionlist_auths_size);
 
-for (au = auths; au; au = au->next)
+for (auth_instance * au = auths; au; au = au->next)
   {
   if (!au->public_name)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no public name specified for "
       "the %s authenticator", au->name);
 
-  for (bu = au->next; bu; bu = bu->next)
+  for (auth_instance * bu = au->next; bu; bu = bu->next)
     if (strcmpic(au->public_name, bu->public_name) == 0)
-      if ((au->client && bu->client) || (au->server && bu->server))
+      if (  au->client && bu->client
+        || au->server && bu->server)
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "two %s authenticators "
           "(%s and %s) have the same public name (%s)",
-          au->client ? US"client" : US"server", au->name, bu->name,
-          au->public_name);
+          au->client && bu->client ? US"client" : US"server",
+         au->name, bu->name, au->public_name);
+#ifndef DISABLE_PIPE_CONNECT
+  nauths++;
+#endif
   }
+#ifndef DISABLE_PIPE_CONNECT
+f.smtp_in_early_pipe_no_auth = nauths > 16;
+#endif
+}
+
+
+/* For error messages, a string describing the config location associated
+with current processing.  NULL if we are not in an authenticator. */
+
+uschar *
+authenticator_current_name(void)
+{
+if (!authenticator_name) return NULL;
+return string_sprintf(" (authenticator %s, %s %d)", authenticator_name, driver_srcfile, driver_srcline);
 }
 
 
 
 
+
 /*************************************************
 *             Read ACL information               *
 *************************************************/
@@ -4267,8 +4292,6 @@ Returns:      nothing
 static void
 readconf_acl(void)
 {
-uschar *p;
-
 /* Read each ACL and add it into the tree. Macro (re)definitions are allowed
 between ACLs. */
 
@@ -4276,14 +4299,14 @@ acl_line = get_config_line();
 
 while(acl_line)
   {
-  uschar name[64];
-  tree_node *node;
-  uschar *error;
+  uschar name[EXIM_DRIVERNAME_MAX];
+  tree_node * node;
+  uschar * error;
+  const uschar * p = readconf_readname(name, sizeof(name), acl_line);
 
-  p = readconf_readname(name, sizeof(name), acl_line);
   if (isupper(*name) && *p == '=')
     {
-    if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE, US"");
+    if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE);
     acl_line = get_config_line();
     continue;
     }
@@ -4291,7 +4314,7 @@ while(acl_line)
   if (*p != ':' || name[0] == 0)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
 
-  node = store_get(sizeof(tree_node) + Ustrlen(name));
+  node = store_get_perm(sizeof(tree_node) + Ustrlen(name), name);
   Ustrcpy(node->name, name);
   if (!tree_insertnode(&acl_anchor, node))
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -4328,10 +4351,8 @@ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "local_scan() options not supported: "
 
 uschar *p;
 while ((p = get_config_line()))
-  {
   (void) readconf_handle_option(p, local_scan_options, local_scan_options_count,
     NULL, US"local_scan option \"%s\" unknown");
-  }
 #endif
 }
 
@@ -4379,7 +4400,7 @@ while(next_section[0] != 0)
   int mid = last/2;
   int n = Ustrlen(next_section);
 
-  if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, "s");
+  if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, US"s");
 
   for (;;)
     {
@@ -4438,7 +4459,7 @@ save_config_line(const uschar* line)
 static config_line_item *current;
 config_line_item *next;
 
-next = (config_line_item*) store_get(sizeof(config_line_item));
+next = (config_line_item*) store_get(sizeof(config_line_item), GET_UNTAINTED);
 next->line = string_copy(line);
 next->next = NULL;
 
@@ -4453,32 +4474,31 @@ hide the <hide> values unless we're the admin user */
 void
 print_config(BOOL admin, BOOL terse)
 {
-config_line_item *i;
 const int TS = terse ? 0 : 2;
 int indent = 0;
+rmark r = NULL;
 
-for (i = config_lines; i; i = i->next)
+for (const config_line_item * i = config_lines; i; i = i->next)
   {
-  uschar *current;
-  uschar *p;
+  uschar * current, * p;
 
-  /* skip over to the first non-space */
-  for (current = i->line; *current && isspace(*current); ++current)
-    ;
+  if (r) store_reset(r);
+  r = store_mark();
 
-  if (*current == '\0')
+  /* skip over to the first non-space */
+  current = string_copy(i->line);
+  if (!Uskip_whitespace(&current))
     continue;
 
   /* Collapse runs of spaces. We stop this if we encounter one of the
-   * following characters: "'$, as this may indicate careful formatting */
-  for (p = current; *p; ++p)
+  following characters: "'$, as this may indicate careful formatting */
+
+  for (p = current; *p; p++) if (isspace(*p))
     {
-    uschar *next;
-    if (!isspace(*p)) continue;
+    uschar * next = p;
     if (*p != ' ') *p = ' ';
 
-    for (next = p; isspace(*next); ++next)
-      ;
+    Uskip_whitespace(&p);
 
     if (next - p > 1)
       memmove(p+1, next, Ustrlen(next)+1);
@@ -4517,17 +4537,18 @@ for (i = config_lines; i; i = i->next)
     if ((p = Ustrchr(current, '=')))
       {
       *p = '\0';
-      printf("%*s%s= %s\n", indent, "", current, hidden);
+      printf("%*s%s= %s\n", indent, "", current, CCS hidden);
       }
     /* e.g.: hide split_spool_directory */
     else
-      printf("%*s\n", indent, hidden);
+      printf("%*s\n", indent, CCS hidden);
     }
 
   else
     /* rest is public */
     printf("%*s%s\n", indent, "", current);
   }
+if (r) store_reset(r);
 }
 
 #endif /*!MACRO_PREDEF*/