Taint: When a non-wildcarded localpart affix is matched in a router,
[exim.git] / src / src / exim.c
index 3be3bf039a23caf98e49bd18df0dfc55fafaef4a..6bc95d241061f9f9d64f52ae7f7fac2f1b22c94f 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -309,6 +310,7 @@ milliwait(struct itimerval *itval)
 {
 sigset_t sigmask;
 sigset_t old_sigmask;
+int save_errno = errno;
 
 if (itval->it_value.tv_usec < 50 && itval->it_value.tv_sec == 0)
   return;
@@ -322,6 +324,8 @@ if (setitimer(ITIMER_REAL, itval, NULL) < 0)           /* Start timer */
 (void)sigdelset(&sigmask, SIGALRM);                    /* Remove SIGALRM */
 (void)sigsuspend(&sigmask);                            /* Until SIGALRM */
 (void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL);    /* Restore mask */
+errno = save_errno;
+sigalrm_seen = FALSE;
 }
 
 
@@ -342,11 +346,9 @@ Returns:     nothing
 void
 millisleep(int msec)
 {
-struct itimerval itval;
-itval.it_interval.tv_sec = 0;
-itval.it_interval.tv_usec = 0;
-itval.it_value.tv_sec = msec/1000;
-itval.it_value.tv_usec = (msec % 1000) * 1000;
+struct itimerval itval = {.it_interval = {.tv_sec = 0, .tv_usec = 0},
+                         .it_value = {.tv_sec = msec/1000,
+                                      .tv_usec = (msec % 1000) * 1000}};
 milliwait(&itval);
 }
 
@@ -714,14 +716,14 @@ Returns:     does not return
 */
 
 void
-exim_exit(int rc, const uschar * process)
+exim_exit(int rc)
 {
 search_tidyup();
 store_exit();
 DEBUG(D_any)
-  debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
-    ">>>>>>>>>>>>>>>>\n", (int)getpid(),
-    process ? "(" : "", process, process ? ") " : "", rc);
+  debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d (%s) terminating with rc=%d "
+    ">>>>>>>>>>>>>>>>\n",
+    (int)getpid(), process_purpose, rc);
 exit(rc);
 }
 
@@ -730,6 +732,10 @@ void
 exim_underbar_exit(int rc)
 {
 store_exit();
+DEBUG(D_any)
+  debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d (%s) terminating with rc=%d "
+    ">>>>>>>>>>>>>>>>\n",
+    (int)getpid(), process_purpose, rc);
 _exit(rc);
 }
 
@@ -826,7 +832,7 @@ int start, end, domain;
 uschar *parse_error = NULL;
 uschar *address = parse_extract_address(s, &parse_error, &start, &end, &domain,
   FALSE);
-if (address == NULL)
+if (!address)
   {
   fprintf(stdout, "syntax error: %s\n", parse_error);
   *exit_value = 2;
@@ -890,192 +896,198 @@ Returns:    nothing
 static void
 show_whats_supported(FILE * fp)
 {
+rmark reset_point = store_mark();
+gstring * g;
 DEBUG(D_any) {} else show_db_version(fp);
 
-fprintf(fp, "Support for:");
+g = string_cat(NULL, US"Support for:");
 #ifdef SUPPORT_CRYPTEQ
-  fprintf(fp, " crypteq");
+  g = string_cat(g, US" crypteq");
 #endif
 #if HAVE_ICONV
-  fprintf(fp, " iconv()");
+  g = string_cat(g, US" iconv()");
 #endif
 #if HAVE_IPV6
-  fprintf(fp, " IPv6");
+  g = string_cat(g, US" IPv6");
 #endif
 #ifdef HAVE_SETCLASSRESOURCES
-  fprintf(fp, " use_setclassresources");
+  g = string_cat(g, US" use_setclassresources");
 #endif
 #ifdef SUPPORT_PAM
-  fprintf(fp, " PAM");
+  g = string_cat(g, US" PAM");
 #endif
 #ifdef EXIM_PERL
-  fprintf(fp, " Perl");
+  g = string_cat(g, US" Perl");
 #endif
 #ifdef EXPAND_DLFUNC
-  fprintf(fp, " Expand_dlfunc");
+  g = string_cat(g, US" Expand_dlfunc");
 #endif
 #ifdef USE_TCP_WRAPPERS
-  fprintf(fp, " TCPwrappers");
+  g = string_cat(g, US" TCPwrappers");
 #endif
 #ifdef USE_GNUTLS
-  fprintf(fp, " GnuTLS");
+  g = string_cat(g, US" GnuTLS");
 #endif
 #ifdef USE_OPENSSL
-  fprintf(fp, " OpenSSL");
+  g = string_cat(g, US" OpenSSL");
 #endif
 #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
-  fprintf(fp, " translate_ip_address");
+  g = string_cat(g, US" translate_ip_address");
 #endif
 #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
-  fprintf(fp, " move_frozen_messages");
+  g = string_cat(g, US" move_frozen_messages");
 #endif
 #ifdef WITH_CONTENT_SCAN
-  fprintf(fp, " Content_Scanning");
+  g = string_cat(g, US" Content_Scanning");
 #endif
 #ifdef SUPPORT_DANE
-  fprintf(fp, " DANE");
+  g = string_cat(g, US" DANE");
 #endif
 #ifndef DISABLE_DKIM
-  fprintf(fp, " DKIM");
+  g = string_cat(g, US" DKIM");
 #endif
 #ifndef DISABLE_DNSSEC
-  fprintf(fp, " DNSSEC");
+  g = string_cat(g, US" DNSSEC");
 #endif
 #ifndef DISABLE_EVENT
-  fprintf(fp, " Event");
+  g = string_cat(g, US" Event");
 #endif
 #ifdef SUPPORT_I18N
-  fprintf(fp, " I18N");
+  g = string_cat(g, US" I18N");
 #endif
 #ifndef DISABLE_OCSP
-  fprintf(fp, " OCSP");
+  g = string_cat(g, US" OCSP");
 #endif
 #ifndef DISABLE_PIPE_CONNECT
-  fprintf(fp, " PIPE_CONNECT");
+  g = string_cat(g, US" PIPE_CONNECT");
 #endif
 #ifndef DISABLE_PRDR
-  fprintf(fp, " PRDR");
+  g = string_cat(g, US" PRDR");
 #endif
 #ifdef SUPPORT_PROXY
-  fprintf(fp, " PROXY");
+  g = string_cat(g, US" PROXY");
 #endif
 #ifdef SUPPORT_SOCKS
-  fprintf(fp, " SOCKS");
+  g = string_cat(g, US" SOCKS");
 #endif
 #ifdef SUPPORT_SPF
-  fprintf(fp, " SPF");
+  g = string_cat(g, US" SPF");
 #endif
 #ifdef SUPPORT_DMARC
-  fprintf(fp, " DMARC");
+  g = string_cat(g, US" DMARC");
 #endif
 #ifdef TCP_FASTOPEN
   tcp_init();
-  if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open");
-#endif
-#ifdef EXPERIMENTAL_LMDB
-  fprintf(fp, " Experimental_LMDB");
-#endif
-#ifdef EXPERIMENTAL_QUEUEFILE
-  fprintf(fp, " Experimental_QUEUEFILE");
-#endif
-#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE)
-  fprintf(fp, " Experimental_SRS");
+  if (f.tcp_fastopen_ok) g = string_cat(g, US" TCP_Fast_Open");
 #endif
 #ifdef EXPERIMENTAL_ARC
-  fprintf(fp, " Experimental_ARC");
+  g = string_cat(g, US" Experimental_ARC");
 #endif
 #ifdef EXPERIMENTAL_BRIGHTMAIL
-  fprintf(fp, " Experimental_Brightmail");
+  g = string_cat(g, US" Experimental_Brightmail");
 #endif
 #ifdef EXPERIMENTAL_DCC
-  fprintf(fp, " Experimental_DCC");
+  g = string_cat(g, US" Experimental_DCC");
 #endif
 #ifdef EXPERIMENTAL_DSN_INFO
-  fprintf(fp, " Experimental_DSN_info");
+  g = string_cat(g, US" Experimental_DSN_info");
+#endif
+#ifdef EXPERIMENTAL_LMDB
+  g = string_cat(g, US" Experimental_LMDB");
+#endif
+#ifdef EXPERIMENTAL_QUEUE_RAMP
+  g = string_cat(g, US" Experimental_Queue_Ramp");
+#endif
+#ifdef EXPERIMENTAL_QUEUEFILE
+  g = string_cat(g, US" Experimental_QUEUEFILE");
+#endif
+#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE)
+  g = string_cat(g, US" Experimental_SRS");
 #endif
 #ifdef EXPERIMENTAL_TLS_RESUME
-  fprintf(fp, " Experimental_TLS_resume");
+  g = string_cat(g, US" Experimental_TLS_resume");
 #endif
-fprintf(fp, "\n");
+g = string_cat(g, US"\n");
 
-fprintf(fp, "Lookups (built-in):");
+g = string_cat(g, US"Lookups (built-in):");
 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
-  fprintf(fp, " lsearch wildlsearch nwildlsearch iplsearch");
+  g = string_cat(g, US" lsearch wildlsearch nwildlsearch iplsearch");
 #endif
 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
-  fprintf(fp, " cdb");
+  g = string_cat(g, US" cdb");
 #endif
 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
-  fprintf(fp, " dbm dbmjz dbmnz");
+  g = string_cat(g, US" dbm dbmjz dbmnz");
 #endif
 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
-  fprintf(fp, " dnsdb");
+  g = string_cat(g, US" dnsdb");
 #endif
 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
-  fprintf(fp, " dsearch");
+  g = string_cat(g, US" dsearch");
 #endif
 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
-  fprintf(fp, " ibase");
+  g = string_cat(g, US" ibase");
 #endif
 #if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
-  fprintf(fp, " json");
+  g = string_cat(g, US" json");
 #endif
 #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
-  fprintf(fp, " ldap ldapdn ldapm");
+  g = string_cat(g, US" ldap ldapdn ldapm");
 #endif
 #ifdef EXPERIMENTAL_LMDB
-  fprintf(fp, " lmdb");
+  g = string_cat(g, US" lmdb");
 #endif
 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
-  fprintf(fp, " mysql");
+  g = string_cat(g, US" mysql");
 #endif
 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
-  fprintf(fp, " nis nis0");
+  g = string_cat(g, US" nis nis0");
 #endif
 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
-  fprintf(fp, " nisplus");
+  g = string_cat(g, US" nisplus");
 #endif
 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
-  fprintf(fp, " oracle");
+  g = string_cat(g, US" oracle");
 #endif
 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
-  fprintf(fp, " passwd");
+  g = string_cat(g, US" passwd");
 #endif
 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
-  fprintf(fp, " pgsql");
+  g = string_cat(g, US" pgsql");
 #endif
 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
-  fprintf(fp, " redis");
+  g = string_cat(g, US" redis");
 #endif
 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
-  fprintf(fp, " sqlite");
+  g = string_cat(g, US" sqlite");
 #endif
 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
-  fprintf(fp, " testdb");
+  g = string_cat(g, US" testdb");
 #endif
 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
-  fprintf(fp, " whoson");
+  g = string_cat(g, US" whoson");
 #endif
-fprintf(fp, "\n");
+g = string_cat(g, US"\n");
 
-auth_show_supported(fp);
-route_show_supported(fp);
-transport_show_supported(fp);
+g = auth_show_supported(g);
+g = route_show_supported(g);
+g = transport_show_supported(g);
 
 #ifdef WITH_CONTENT_SCAN
-malware_show_supported(fp);
+g = malware_show_supported(g);
 #endif
 
 if (fixed_never_users[0] > 0)
   {
   int i;
-  fprintf(fp, "Fixed never_users: ");
+  g = string_cat(g, US"Fixed never_users: ");
   for (i = 1; i <= (int)fixed_never_users[0] - 1; i++)
-    fprintf(fp, "%d:", (unsigned int)fixed_never_users[i]);
-  fprintf(fp, "%d\n", (unsigned int)fixed_never_users[i]);
+    string_fmt_append(g, "%u:", (unsigned)fixed_never_users[i]);
+  g = string_fmt_append(g, "%u\n", (unsigned)fixed_never_users[i]);
   }
 
-fprintf(fp, "Configure owner: %d:%d\n", config_uid, config_gid);
+g = string_fmt_append(g, "Configure owner: %d:%d\n", config_uid, config_gid);
+fputs(CS string_from_gstring(g), fp);
 
 fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
 
@@ -1155,6 +1167,7 @@ show_db_version(fp);
 #endif
 
 } while (0);
+store_reset(reset_point);
 }
 
 
@@ -1621,7 +1634,6 @@ uschar *malware_test_file = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
 size_t sz;
-rmark reset_point;
 
 struct passwd *pw;
 struct stat statbuf;
@@ -1940,15 +1952,20 @@ running in an unprivileged state. */
 
 unprivileged = (real_uid != root_uid && original_euid != root_uid);
 
+/* For most of the args-parsing we need to use permanent pool memory */
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+
 /* Scan the program's arguments. Some can be dealt with right away; others are
 simply recorded for checking and handling afterwards. Do a high-level switch
 on the second character (the one after '-'), to save some effort. */
 
-for (i = 1; i < argc; i++)
+ for (i = 1; i < argc; i++)
   {
   BOOL badarg = FALSE;
-  uschar *arg = argv[i];
-  uschar *argrest;
+  uschar * arg = argv[i];
+  uschar * argrest;
   int switchchar;
 
   /* An argument not starting with '-' is the start of a recipients list;
@@ -2027,7 +2044,7 @@ for (i = 1; i < argc; i++)
     /* sendmail uses -Ac and -Am to control which .cf file is used;
     we ignore them. */
     case 'A':
-    if (*argrest == '\0') { badarg = TRUE; break; }
+    if (!*argrest) { badarg = TRUE; break; }
     else
       {
       BOOL ignore = FALSE;
@@ -2039,7 +2056,7 @@ for (i = 1; i < argc; i++)
             ignore = TRUE;
           break;
         }
-      if (!ignore) { badarg = TRUE; break; }
+      if (!ignore) badarg = TRUE;
       }
     break;
 
@@ -2047,293 +2064,298 @@ for (i = 1; i < argc; i++)
     so has no need of it. */
 
     case 'B':
-    if (*argrest == 0) i++;       /* Skip over the type */
+    if (!*argrest) i++;       /* Skip over the type */
     break;
 
 
     case 'b':
-    receiving_message = FALSE;    /* Reset TRUE for -bm, -bS, -bs below */
-
-    /* -bd:  Run in daemon mode, awaiting SMTP connections.
-       -bdf: Ditto, but in the foreground.
-    */
-
-    if (*argrest == 'd')
-      {
-      f.daemon_listen = TRUE;
-      if (*(++argrest) == 'f') f.background_daemon = FALSE;
-        else if (*argrest != 0) { badarg = TRUE; break; }
-      }
-
-    /* -be:  Run in expansion test mode
-       -bem: Ditto, but read a message from a file first
-    */
-
-    else if (*argrest == 'e')
-      {
-      expansion_test = checking = TRUE;
-      if (argrest[1] == 'm')
-        {
-        if (++i >= argc) { badarg = TRUE; break; }
-        expansion_test_message = argv[i];
-        argrest++;
-        }
-      if (argrest[1] != 0) { badarg = TRUE; break; }
-      }
-
-    /* -bF:  Run system filter test */
-
-    else if (*argrest == 'F')
-      {
-      filter_test |= checking = FTEST_SYSTEM;
-      if (*(++argrest) != 0) { badarg = TRUE; break; }
-      if (++i < argc) filter_test_sfile = argv[i]; else
-        exim_fail("exim: file name expected after %s\n", argv[i-1]);
-      }
-
-    /* -bf:  Run user filter test
-       -bfd: Set domain for filter testing
-       -bfl: Set local part for filter testing
-       -bfp: Set prefix for filter testing
-       -bfs: Set suffix for filter testing
-    */
-
-    else if (*argrest == 'f')
-      {
-      if (*(++argrest) == 0)
-        {
-        filter_test |= checking = FTEST_USER;
-        if (++i < argc) filter_test_ufile = argv[i]; else
-          exim_fail("exim: file name expected after %s\n", argv[i-1]);
-        }
-      else
-        {
-        if (++i >= argc)
-          exim_fail("exim: string expected after %s\n", arg);
-        if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
-        else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i];
-        else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i];
-        else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
-        else { badarg = TRUE; break; }
-        }
-      }
-
-    /* -bh: Host checking - an IP address must follow. */
-
-    else if (Ustrcmp(argrest, "h") == 0 || Ustrcmp(argrest, "hc") == 0)
-      {
-      if (++i >= argc) { badarg = TRUE; break; }
-      sender_host_address = argv[i];
-      host_checking = checking = f.log_testing_mode = TRUE;
-      f.host_checking_callout = argrest[1] == 'c';
-      message_logs = FALSE;
-      }
-
-    /* -bi: This option is used by sendmail to initialize *the* alias file,
-    though it has the -oA option to specify a different file. Exim has no
-    concept of *the* alias file, but since Sun's YP make script calls
-    sendmail this way, some support must be provided. */
-
-    else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE;
-
-    /* -bI: provide information, of the type to follow after a colon.
-    This is an Exim flag. */
-
-    else if (argrest[0] == 'I' && Ustrlen(argrest) >= 2 && argrest[1] == ':')
-      {
-      uschar *p = &argrest[2];
-      info_flag = CMDINFO_HELP;
-      if (Ustrlen(p))
-        {
-        if (strcmpic(p, CUS"sieve") == 0)
-          {
-          info_flag = CMDINFO_SIEVE;
-          info_stdout = TRUE;
-          }
-        else if (strcmpic(p, CUS"dscp") == 0)
-          {
-          info_flag = CMDINFO_DSCP;
-          info_stdout = TRUE;
-          }
-        else if (strcmpic(p, CUS"help") == 0)
-          {
-          info_stdout = TRUE;
-          }
-        }
-      }
-
-    /* -bm: Accept and deliver message - the default option. Reinstate
-    receiving_message, which got turned off for all -b options. */
-
-    else if (Ustrcmp(argrest, "m") == 0) receiving_message = TRUE;
-
-    /* -bmalware: test the filename given for malware */
-
-    else if (Ustrcmp(argrest, "malware") == 0)
-      {
-      if (++i >= argc) { badarg = TRUE; break; }
-      checking = TRUE;
-      malware_test_file = argv[i];
-      }
-
-    /* -bnq: For locally originating messages, do not qualify unqualified
-    addresses. In the envelope, this causes errors; in header lines they
-    just get left. */
-
-    else if (Ustrcmp(argrest, "nq") == 0)
-      {
-      f.allow_unqualified_sender = FALSE;
-      f.allow_unqualified_recipient = FALSE;
-      }
-
-    /* -bpxx: List the contents of the mail queue, in various forms. If
-    the option is -bpc, just a queue count is needed. Otherwise, if the
-    first letter after p is r, then order is random. */
-
-    else if (*argrest == 'p')
-      {
-      if (*(++argrest) == 'c')
-        {
-        count_queue = TRUE;
-        if (*(++argrest) != 0) badarg = TRUE;
-        break;
-        }
-
-      if (*argrest == 'r')
-        {
-        list_queue_option = 8;
-        argrest++;
-        }
-      else list_queue_option = 0;
-
-      list_queue = TRUE;
-
-      /* -bp: List the contents of the mail queue, top-level only */
-
-      if (*argrest == 0) {}
-
-      /* -bpu: List the contents of the mail queue, top-level undelivered */
-
-      else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1;
-
-      /* -bpa: List the contents of the mail queue, including all delivered */
-
-      else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2;
-
-      /* Unknown after -bp[r] */
-
-      else
-        {
-        badarg = TRUE;
-        break;
-        }
-      }
-
-
-    /* -bP: List the configuration variables given as the address list.
-    Force -v, so configuration errors get displayed. */
-
-    else if (Ustrcmp(argrest, "P") == 0)
       {
-      /* -bP config: we need to setup here, because later,
-       * when list_options is checked, the config is read already */
-      if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
-        {
-        list_config = TRUE;
-        readconf_save_config(version_string);
-        }
-      else
-        {
-        list_options = TRUE;
-        debug_selector |= D_v;
-        debug_file = stderr;
-        }
-      }
-
-    /* -brt: Test retry configuration lookup */
+      receiving_message = FALSE;    /* Reset TRUE for -bm, -bS, -bs below */
 
-    else if (Ustrcmp(argrest, "rt") == 0)
-      {
-      checking = TRUE;
-      test_retry_arg = i + 1;
-      goto END_ARG;
-      }
-
-    /* -brw: Test rewrite configuration */
+      switch (*argrest++)
+       {
+       /* -bd:  Run in daemon mode, awaiting SMTP connections.
+          -bdf: Ditto, but in the foreground.
+       */
+       case 'd':
+         f.daemon_listen = TRUE;
+         if (*argrest == 'f') f.background_daemon = FALSE;
+         else if (*argrest) badarg = TRUE;
+         break;
+
+       /* -be:  Run in expansion test mode
+          -bem: Ditto, but read a message from a file first
+       */
+       case 'e':
+         expansion_test = checking = TRUE;
+         if (*argrest == 'm')
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           expansion_test_message = argv[i];
+           argrest++;
+           }
+         if (*argrest) badarg = TRUE;
+         break;
+
+       /* -bF:  Run system filter test */
+       case 'F':
+         filter_test |= checking = FTEST_SYSTEM;
+         if (*argrest) badarg = TRUE;
+         else if (++i < argc) filter_test_sfile = argv[i];
+         else exim_fail("exim: file name expected after %s\n", argv[i-1]);
+         break;
+
+       /* -bf:  Run user filter test
+          -bfd: Set domain for filter testing
+          -bfl: Set local part for filter testing
+          -bfp: Set prefix for filter testing
+          -bfs: Set suffix for filter testing
+       */
+       case 'f':
+         if (!*argrest)
+           {
+           filter_test |= checking = FTEST_USER;
+           if (++i < argc) filter_test_ufile = argv[i];
+           else exim_fail("exim: file name expected after %s\n", argv[i-1]);
+           }
+         else
+           {
+           if (++i >= argc)
+             exim_fail("exim: string expected after %s\n", arg);
+           if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
+           else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i];
+           else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i];
+           else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
+           else badarg = TRUE;
+           }
+         break;
+
+       /* -bh: Host checking - an IP address must follow. */
+       case 'h':
+         if (!*argrest || Ustrcmp(argrest, "c") == 0)
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           sender_host_address = string_copy_taint(argv[i], TRUE);
+           host_checking = checking = f.log_testing_mode = TRUE;
+           f.host_checking_callout = *argrest == 'c';
+           message_logs = FALSE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bi: This option is used by sendmail to initialize *the* alias file,
+       though it has the -oA option to specify a different file. Exim has no
+       concept of *the* alias file, but since Sun's YP make script calls
+       sendmail this way, some support must be provided. */
+       case 'i':
+         if (!*++argrest) bi_option = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bI: provide information, of the type to follow after a colon.
+       This is an Exim flag. */
+       case 'I':
+         if (Ustrlen(argrest) >= 1 && *argrest == ':')
+           {
+           uschar *p = argrest+1;
+           info_flag = CMDINFO_HELP;
+           if (Ustrlen(p))
+             if (strcmpic(p, CUS"sieve") == 0)
+               {
+               info_flag = CMDINFO_SIEVE;
+               info_stdout = TRUE;
+               }
+             else if (strcmpic(p, CUS"dscp") == 0)
+               {
+               info_flag = CMDINFO_DSCP;
+               info_stdout = TRUE;
+               }
+             else if (strcmpic(p, CUS"help") == 0)
+               info_stdout = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bm: Accept and deliver message - the default option. Reinstate
+       receiving_message, which got turned off for all -b options.
+          -bmalware: test the filename given for malware */
+       case 'm':
+         if (!*argrest) receiving_message = TRUE;
+         else if (Ustrcmp(argrest, "alware") == 0)
+           {
+           if (++i >= argc) { badarg = TRUE; break; }
+           checking = TRUE;
+           malware_test_file = argv[i];
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bnq: For locally originating messages, do not qualify unqualified
+       addresses. In the envelope, this causes errors; in header lines they
+       just get left. */
+       case 'n':
+         if (Ustrcmp(argrest, "q") == 0)
+           {
+           f.allow_unqualified_sender = FALSE;
+           f.allow_unqualified_recipient = FALSE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bpxx: List the contents of the mail queue, in various forms. If
+       the option is -bpc, just a queue count is needed. Otherwise, if the
+       first letter after p is r, then order is random. */
+       case 'p':
+         if (*argrest == 'c')
+           {
+           count_queue = TRUE;
+           if (*++argrest) badarg = TRUE;
+           break;
+           }
 
-    else if (Ustrcmp(argrest, "rw") == 0)
-      {
-      checking = TRUE;
-      test_rewrite_arg = i + 1;
-      goto END_ARG;
-      }
+         if (*argrest == 'r')
+           {
+           list_queue_option = 8;
+           argrest++;
+           }
+         else list_queue_option = 0;
 
-    /* -bS: Read SMTP commands on standard input, but produce no replies -
-    all errors are reported by sending messages. */
+         list_queue = TRUE;
 
-    else if (Ustrcmp(argrest, "S") == 0)
-      smtp_input = smtp_batched_input = receiving_message = TRUE;
+         /* -bp: List the contents of the mail queue, top-level only */
 
-    /* -bs: Read SMTP commands on standard input and produce SMTP replies
-    on standard output. */
+         if (!*argrest) {}
 
-    else if (Ustrcmp(argrest, "s") == 0) smtp_input = receiving_message = TRUE;
+         /* -bpu: List the contents of the mail queue, top-level undelivered */
 
-    /* -bt: address testing mode */
+         else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1;
 
-    else if (Ustrcmp(argrest, "t") == 0)
-      f.address_test_mode = checking = f.log_testing_mode = TRUE;
+         /* -bpa: List the contents of the mail queue, including all delivered */
 
-    /* -bv: verify addresses */
+         else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2;
 
-    else if (Ustrcmp(argrest, "v") == 0)
-      verify_address_mode = checking = f.log_testing_mode = TRUE;
+         /* Unknown after -bp[r] */
 
-    /* -bvs: verify sender addresses */
+         else badarg = TRUE;
+         break;
 
-    else if (Ustrcmp(argrest, "vs") == 0)
-      {
-      verify_address_mode = checking = f.log_testing_mode = TRUE;
-      verify_as_sender = TRUE;
-      }
 
-    /* -bV: Print version string and support details */
+       /* -bP: List the configuration variables given as the address list.
+       Force -v, so configuration errors get displayed. */
+       case 'P':
 
-    else if (Ustrcmp(argrest, "V") == 0)
-      {
-      printf("Exim version %s #%s built %s\n", version_string,
-        version_cnumber, version_date);
-      printf("%s\n", CS version_copyright);
-      version_printed = TRUE;
-      show_whats_supported(stdout);
-      f.log_testing_mode = TRUE;
-      }
+         /* -bP config: we need to setup here, because later,
+          * when list_options is checked, the config is read already */
+         if (*argrest)
+           badarg = TRUE;
+         else if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
+           {
+           list_config = TRUE;
+           readconf_save_config(version_string);
+           }
+         else
+           {
+           list_options = TRUE;
+           debug_selector |= D_v;
+           debug_file = stderr;
+           }
+         break;
+
+       /* -brt: Test retry configuration lookup */
+       case 'r':
+         if (Ustrcmp(argrest, "t") == 0)
+           {
+           checking = TRUE;
+           test_retry_arg = i + 1;
+           goto END_ARG;
+           }
 
-    /* -bw: inetd wait mode, accept a listening socket as stdin */
+         /* -brw: Test rewrite configuration */
 
-    else if (*argrest == 'w')
-      {
-      f.inetd_wait_mode = TRUE;
-      f.background_daemon = FALSE;
-      f.daemon_listen = TRUE;
-      if (*(++argrest) != '\0')
-        if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
-          exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+         else if (Ustrcmp(argrest, "w") == 0)
+           {
+           checking = TRUE;
+           test_rewrite_arg = i + 1;
+           goto END_ARG;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bS: Read SMTP commands on standard input, but produce no replies -
+       all errors are reported by sending messages. */
+       case 'S':
+         if (!*argrest)
+           smtp_input = smtp_batched_input = receiving_message = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bs: Read SMTP commands on standard input and produce SMTP replies
+       on standard output. */
+       case 's':
+         if (!*argrest) smtp_input = receiving_message = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bt: address testing mode */
+       case 't':
+         if (!*argrest)
+           f.address_test_mode = checking = f.log_testing_mode = TRUE;
+         else badarg = TRUE;
+         break;
+
+       /* -bv: verify addresses */
+       case 'v':
+         if (!*argrest)
+           verify_address_mode = checking = f.log_testing_mode = TRUE;
+
+       /* -bvs: verify sender addresses */
+
+         else if (Ustrcmp(argrest, "s") == 0)
+           {
+           verify_address_mode = checking = f.log_testing_mode = TRUE;
+           verify_as_sender = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bV: Print version string and support details */
+       case 'V':
+         if (!*argrest)
+           {
+           printf("Exim version %s #%s built %s\n", version_string,
+             version_cnumber, version_date);
+           printf("%s\n", CS version_copyright);
+           version_printed = TRUE;
+           show_whats_supported(stdout);
+           f.log_testing_mode = TRUE;
+           }
+         else badarg = TRUE;
+         break;
+
+       /* -bw: inetd wait mode, accept a listening socket as stdin */
+       case 'w':
+         f.inetd_wait_mode = TRUE;
+         f.background_daemon = FALSE;
+         f.daemon_listen = TRUE;
+         if (*argrest)
+           if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
+             exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+         break;
+
+       default:
+         badarg = TRUE;
+         break;
+       }
+      break;
       }
 
-    else badarg = TRUE;
-    break;
-
 
     /* -C: change configuration file list; ignore if it isn't really
     a change! Enforce a prefix check if required. */
 
     case 'C':
-    if (*argrest == 0)
-      {
-      if(++i < argc) argrest = argv[i]; else
-        { badarg = TRUE; break; }
-      }
+    if (!*argrest)
+      if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; }
     if (Ustrcmp(config_main_filelist, argrest) != 0)
       {
       #ifdef ALT_CONFIG_PREFIX
@@ -2342,14 +2364,14 @@ for (i = 1; i < argc; i++)
       const uschar *list = argrest;
       uschar *filename;
       while((filename = string_nextinlist(&list, &sep, big_buffer,
-             big_buffer_size)) != NULL)
-        {
-        if ((Ustrlen(filename) < len ||
-             Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 ||
-             Ustrstr(filename, "/../") != NULL) &&
-             (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid))
+             big_buffer_size)))
+        if (  (  Ustrlen(filename) < len
+             || Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0
+             || Ustrstr(filename, "/../") != NULL
+             )
+          && (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)
+          )
           exim_fail("-C Permission denied\n");
-        }
       #endif
       if (real_uid != root_uid)
         {
@@ -2388,11 +2410,14 @@ for (i = 1; i < argc; i++)
            else
               {
               /* Well, the trust list at least is up to scratch... */
-              rmark reset_point = store_mark();
+              rmark reset_point;
               uschar *trusted_configs[32];
               int nr_configs = 0;
               int i = 0;
+             int old_pool = store_pool;
+             store_pool = POOL_MAIN;
 
+              reset_point = store_mark();
               while (Ufgets(big_buffer, big_buffer_size, trust_list))
                 {
                 uschar *start = big_buffer, *nl;
@@ -2404,7 +2429,7 @@ for (i = 1; i < argc; i++)
                 if (nl)
                   *nl = 0;
                 trusted_configs[nr_configs++] = string_copy(start);
-                if (nr_configs == 32)
+                if (nr_configs == nelem(trusted_configs))
                   break;
                 }
               fclose(trust_list);
@@ -2415,7 +2440,7 @@ for (i = 1; i < argc; i++)
                 const uschar *list = argrest;
                 uschar *filename;
                 while (f.trusted_config && (filename = string_nextinlist(&list,
-                        &sep, big_buffer, big_buffer_size)) != NULL)
+                        &sep, big_buffer, big_buffer_size)))
                   {
                   for (i=0; i < nr_configs; i++)
                     if (Ustrcmp(filename, trusted_configs[i]) == 0)
@@ -2430,6 +2455,7 @@ for (i = 1; i < argc; i++)
               else     /* No valid prefixes found in trust_list file. */
                 f.trusted_config = FALSE;
               store_reset(reset_point);
+             store_pool = old_pool;
               }
            }
           else         /* Could not open trust_list file. */
@@ -2517,7 +2543,7 @@ for (i = 1; i < argc; i++)
         f.debug_daemon = TRUE;
         argrest++;
         }
-      if (*argrest != 0)
+      if (*argrest)
         decode_bits(&selector, 1, debug_notall, argrest,
           debug_options, debug_options_count, US"debug", 0);
       debug_selector = selector;
@@ -2564,12 +2590,9 @@ for (i = 1; i < argc; i++)
     the -F or be in the next argument. */
 
     case 'F':
-    if (*argrest == 0)
-      {
-      if(++i < argc) argrest = argv[i]; else
-        { badarg = TRUE; break; }
-      }
-    originator_name = argrest;
+    if (!*argrest)
+      if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; }
+    originator_name = string_copy_taint(argrest, TRUE);
     f.sender_name_forced = TRUE;
     break;
 
@@ -2593,16 +2616,13 @@ for (i = 1; i < argc; i++)
       {
       int dummy_start, dummy_end;
       uschar *errmess;
-      if (*argrest == 0)
-        {
-        if (i+1 < argc) argrest = argv[++i]; else
-          { badarg = TRUE; break; }
-        }
-      if (*argrest == 0)
+      if (!*argrest)
+        if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; }
+      if (!*argrest)
         *(sender_address = store_get(1, FALSE)) = '\0';  /* Ensure writeable memory */
       else
         {
-        uschar *temp = argrest + Ustrlen(argrest) - 1;
+        uschar * temp = argrest + Ustrlen(argrest) - 1;
         while (temp >= argrest && isspace(*temp)) temp--;
         if (temp >= argrest && *temp == '.') f_end_dot = TRUE;
         allow_domain_literals = TRUE;
@@ -2610,8 +2630,10 @@ for (i = 1; i < argc; i++)
 #ifdef SUPPORT_I18N
        allow_utf8_domains = TRUE;
 #endif
-        sender_address = parse_extract_address(argrest, &errmess,
-          &dummy_start, &dummy_end, &sender_address_domain, TRUE);
+        if (!(sender_address = parse_extract_address(argrest, &errmess,
+                 &dummy_start, &dummy_end, &sender_address_domain, TRUE)))
+          exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess);
+
        sender_address = string_copy_taint(sender_address, TRUE);
 #ifdef SUPPORT_I18N
        message_smtputf8 =  string_is_utf8(sender_address);
@@ -2619,8 +2641,6 @@ for (i = 1; i < argc; i++)
 #endif
         allow_domain_literals = FALSE;
         strip_trailing_dot = FALSE;
-        if (!sender_address)
-          exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess);
         }
       f.sender_address_forced = TRUE;
       }
@@ -2640,11 +2660,8 @@ for (i = 1; i < argc; i++)
     To put it in will require a change to the spool header file format. */
 
     case 'h':
-    if (*argrest == 0)
-      {
-      if(++i < argc) argrest = argv[i]; else
-        { badarg = TRUE; break; }
-      }
+    if (!*argrest)
+      if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; }
     if (!isdigit(*argrest)) badarg = TRUE;
     break;
 
@@ -2653,7 +2670,7 @@ for (i = 1; i < argc; i++)
     not to be documented for sendmail but mailx (at least) uses it) */
 
     case 'i':
-    if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE;
+    if (!*argrest) f.dot_ends = FALSE; else badarg = TRUE;
     break;
 
 
@@ -2661,16 +2678,13 @@ for (i = 1; i < argc; i++)
     syslog_processname in the config file, but needs to be an admin option. */
 
     case 'L':
-    if (*argrest == '\0')
-      {
-      if(++i < argc) argrest = argv[i]; else
-        { badarg = TRUE; break; }
-      }
+    if (!*argrest)
+      if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; }
     if ((sz = Ustrlen(argrest)) > 32)
       exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest);
     if (sz < 1)
       exim_fail("exim: the -L syslog name is too short\n");
-    cmdline_syslog_name = argrest;
+    cmdline_syslog_name = string_copy_taint(argrest, TRUE);
     break;
 
     case 'M':
@@ -2700,9 +2714,9 @@ for (i = 1; i < argc; i++)
       if (msg_action_arg >= 0)
         exim_fail("exim: incompatible arguments\n");
 
-      continue_transport = argv[++i];
-      continue_hostname = argv[++i];
-      continue_host_address = argv[++i];
+      continue_transport = string_copy_taint(argv[++i], TRUE);
+      continue_hostname = string_copy_taint(argv[++i], TRUE);
+      continue_host_address = string_copy_taint(argv[++i], TRUE);
       continue_sequence = Uatoi(argv[++i]);
       msg_action = MSG_DELIVER;
       msg_action_arg = ++i;
@@ -2744,9 +2758,16 @@ for (i = 1; i < argc; i++)
 
        case 'D': smtp_peer_options |= OPTION_DSN; break;
 
+    /* -MCd: for debug, set a process-purpose string */
+
+       case 'd': if (++i < argc)
+                   process_purpose = string_copy_taint(argv[i], TRUE);
+                 else badarg = TRUE;
+                 break;
+
     /* -MCG: set the queue name, to a non-default value */
 
-       case 'G': if (++i < argc) queue_name = string_copy(argv[i]);
+       case 'G': if (++i < argc) queue_name = string_copy_taint(argv[i], TRUE);
                  else badarg = TRUE;
                  break;
 
@@ -2780,11 +2801,14 @@ for (i = 1; i < argc; i++)
     Require three arguments for the proxied local address and port,
     and the TLS cipher.  */
 
-       case 't': if (++i < argc) sending_ip_address = argv[i];
+       case 't': if (++i < argc)
+                   sending_ip_address = string_copy_taint(argv[i], TRUE);
                  else badarg = TRUE;
-                 if (++i < argc) sending_port = (int)(Uatol(argv[i]));
+                 if (++i < argc)
+                   sending_port = (int)(Uatol(argv[i]));
                  else badarg = TRUE;
-                 if (++i < argc) continue_proxy_cipher = argv[i];
+                 if (++i < argc)
+                   continue_proxy_cipher = string_copy_taint(argv[i], TRUE);
                  else badarg = TRUE;
                  /*FALLTHROUGH*/
 
@@ -2821,7 +2845,7 @@ for (i = 1; i < argc; i++)
        -Mvl  show log
     */
 
-    else if (*argrest == 0)
+    else if (!*argrest)
       {
       msg_action = MSG_DELIVER;
       forced_delivery = f.deliver_force_thaw = TRUE;
@@ -2846,7 +2870,7 @@ for (i = 1; i < argc; i++)
    else if (Ustrcmp(argrest, "G") == 0)
       {
       msg_action = MSG_SETQUEUE;
-      queue_name_dest = argv[++i];
+      queue_name_dest = string_copy_taint(argv[++i], TRUE);
       }
     else if (Ustrcmp(argrest, "mad") == 0)
       {
@@ -2919,7 +2943,7 @@ for (i = 1; i < argc; i++)
     for sendmail it askes for "me too". Exim always does this. */
 
     case 'm':
-    if (*argrest != 0) badarg = TRUE;
+    if (*argrest) badarg = TRUE;
     break;
 
 
@@ -2927,7 +2951,7 @@ for (i = 1; i < argc; i++)
     their thing. It implies debugging at the D_v level. */
 
     case 'N':
-    if (*argrest == 0)
+    if (!*argrest)
       {
       f.dont_deliver = TRUE;
       debug_selector |= D_v;
@@ -2950,217 +2974,228 @@ for (i = 1; i < argc; i++)
     -O option=value and -Ooption=value. */
 
     case 'O':
-    if (*argrest == 0)
-      {
+    if (!*argrest)
       if (++i >= argc)
         exim_fail("exim: string expected after -O\n");
-      }
     break;
 
     case 'o':
-
-    /* -oA: Set an argument for the bi command (sendmail's "alternate alias
-    file" option). */
-
-    if (*argrest == 'A')
-      {
-      alias_arg = argrest + 1;
-      if (alias_arg[0] == 0)
-        {
-        if (i+1 < argc) alias_arg = argv[++i]; else
-          exim_fail("exim: string expected after -oA\n");
-        }
-      }
-
-    /* -oB: Set a connection message max value for remote deliveries */
-
-    else if (*argrest == 'B')
-      {
-      uschar *p = argrest + 1;
-      if (p[0] == 0)
-        {
-        if (i+1 < argc && isdigit((argv[i+1][0]))) p = argv[++i]; else
-          {
-          connection_max_messages = 1;
-          p = NULL;
-          }
-        }
-
-      if (p != NULL)
-        {
-        if (!isdigit(*p))
-          exim_fail("exim: number expected after -oB\n");
-        connection_max_messages = Uatoi(p);
-        }
-      }
-
-    /* -odb: background delivery */
-
-    else if (Ustrcmp(argrest, "db") == 0)
-      {
-      f.synchronous_delivery = FALSE;
-      arg_queue_only = FALSE;
-      queue_only_set = TRUE;
-      }
-
-    /* -odf: foreground delivery (smail-compatible option); same effect as
-       -odi: interactive (synchronous) delivery (sendmail-compatible option)
-    */
-
-    else if (Ustrcmp(argrest, "df") == 0 || Ustrcmp(argrest, "di") == 0)
+    switch (*argrest++)
       {
-      f.synchronous_delivery = TRUE;
-      arg_queue_only = FALSE;
-      queue_only_set = TRUE;
-      }
-
-    /* -odq: queue only */
+      /* -oA: Set an argument for the bi command (sendmail's "alternate alias
+      file" option). */
+      case 'A':
+       if (!*(alias_arg = argrest))
+         if (i+1 < argc) alias_arg = argv[++i];
+         else exim_fail("exim: string expected after -oA\n");
+       break;
 
-    else if (Ustrcmp(argrest, "dq") == 0)
-      {
-      f.synchronous_delivery = FALSE;
-      arg_queue_only = TRUE;
-      queue_only_set = TRUE;
-      }
+      /* -oB: Set a connection message max value for remote deliveries */
+      case 'B':
+       {
+       uschar * p = argrest;
+       if (!*p)
+         if (i+1 < argc && isdigit((argv[i+1][0])))
+           p = argv[++i];
+         else
+           {
+           connection_max_messages = 1;
+           p = NULL;
+           }
 
-    /* -odqs: queue SMTP only - do local deliveries and remote routing,
-    but no remote delivery */
+       if (p)
+         {
+         if (!isdigit(*p))
+           exim_fail("exim: number expected after -oB\n");
+         connection_max_messages = Uatoi(p);
+         }
+       }
+       break;
 
-    else if (Ustrcmp(argrest, "dqs") == 0)
-      {
-      f.queue_smtp = TRUE;
-      arg_queue_only = FALSE;
-      queue_only_set = TRUE;
-      }
+      /* -odb: background delivery */
+
+      case 'd':
+       if (Ustrcmp(argrest, "b") == 0)
+         {
+         f.synchronous_delivery = FALSE;
+         arg_queue_only = FALSE;
+         queue_only_set = TRUE;
+         }
+
+      /* -odd: testsuite-only: add no inter-process delays */
+
+       else if (Ustrcmp(argrest, "d") == 0)
+         f.testsuite_delays = FALSE;
+
+      /* -odf: foreground delivery (smail-compatible option); same effect as
+        -odi: interactive (synchronous) delivery (sendmail-compatible option)
+      */
+
+       else if (Ustrcmp(argrest, "f") == 0 || Ustrcmp(argrest, "i") == 0)
+         {
+         f.synchronous_delivery = TRUE;
+         arg_queue_only = FALSE;
+         queue_only_set = TRUE;
+         }
+
+      /* -odq: queue only */
+
+       else if (Ustrcmp(argrest, "q") == 0)
+         {
+         f.synchronous_delivery = FALSE;
+         arg_queue_only = TRUE;
+         queue_only_set = TRUE;
+         }
+
+      /* -odqs: queue SMTP only - do local deliveries and remote routing,
+      but no remote delivery */
+
+       else if (Ustrcmp(argrest, "qs") == 0)
+         {
+         f.queue_smtp = TRUE;
+         arg_queue_only = FALSE;
+         queue_only_set = TRUE;
+         }
+       else badarg = TRUE;
+       break;
 
-    /* -oex: Sendmail error flags. As these are also accepted without the
-    leading -o prefix, for compatibility with vacation and other callers,
-    they are handled with -e above. */
+      /* -oex: Sendmail error flags. As these are also accepted without the
+      leading -o prefix, for compatibility with vacation and other callers,
+      they are handled with -e above. */
 
-    /* -oi:     Set flag so dot doesn't end non-SMTP input (same as -i)
-       -oitrue: Another sendmail syntax for the same */
+      /* -oi:     Set flag so dot doesn't end non-SMTP input (same as -i)
+        -oitrue: Another sendmail syntax for the same */
 
-    else if (Ustrcmp(argrest, "i") == 0 ||
-             Ustrcmp(argrest, "itrue") == 0)
-      f.dot_ends = FALSE;
+      case 'i':
+       if (!*argrest || Ustrcmp(argrest, "true") == 0)
+         f.dot_ends = FALSE;
+       else badarg = TRUE;
+       break;
 
     /* -oM*: Set various characteristics for an incoming message; actually
     acted on for trusted callers only. */
 
-    else if (*argrest == 'M')
-      {
-      if (i+1 >= argc)
-        exim_fail("exim: data expected after -o%s\n", argrest);
-
-      /* -oMa: Set sender host address */
+      case 'M':
+       {
+       if (i+1 >= argc)
+         exim_fail("exim: data expected after -oM%s\n", argrest);
 
-      if (Ustrcmp(argrest, "Ma") == 0) sender_host_address = argv[++i];
+       /* -oMa: Set sender host address */
 
-      /* -oMaa: Set authenticator name */
+       if (Ustrcmp(argrest, "a") == 0)
+         sender_host_address = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Maa") == 0)
-        sender_host_authenticated = argv[++i];
+       /* -oMaa: Set authenticator name */
 
-      /* -oMas: setting authenticated sender */
+       else if (Ustrcmp(argrest, "aa") == 0)
+         sender_host_authenticated = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Mas") == 0)
-       authenticated_sender = string_copy_taint(argv[++i], TRUE);
+       /* -oMas: setting authenticated sender */
 
-      /* -oMai: setting authenticated id */
+       else if (Ustrcmp(argrest, "as") == 0)
+         authenticated_sender = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Mai") == 0)
-       authenticated_id = string_copy_taint(argv[++i], TRUE);
+       /* -oMai: setting authenticated id */
 
-      /* -oMi: Set incoming interface address */
+       else if (Ustrcmp(argrest, "ai") == 0)
+         authenticated_id = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i];
+       /* -oMi: Set incoming interface address */
 
-      /* -oMm: Message reference */
+       else if (Ustrcmp(argrest, "i") == 0)
+         interface_address = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Mm") == 0)
-        {
-        if (!mac_ismsgid(argv[i+1]))
-            exim_fail("-oMm must be a valid message ID\n");
-        if (!f.trusted_config)
-            exim_fail("-oMm must be called by a trusted user/config\n");
-          message_reference = argv[++i];
-        }
+       /* -oMm: Message reference */
 
-      /* -oMr: Received protocol */
+       else if (Ustrcmp(argrest, "m") == 0)
+         {
+         if (!mac_ismsgid(argv[i+1]))
+             exim_fail("-oMm must be a valid message ID\n");
+         if (!f.trusted_config)
+             exim_fail("-oMm must be called by a trusted user/config\n");
+           message_reference = argv[++i];
+         }
 
-      else if (Ustrcmp(argrest, "Mr") == 0)
+       /* -oMr: Received protocol */
 
-        if (received_protocol)
-          exim_fail("received_protocol is set already\n");
-        else
-         received_protocol = argv[++i];
+       else if (Ustrcmp(argrest, "r") == 0)
 
-      /* -oMs: Set sender host name */
+         if (received_protocol)
+           exim_fail("received_protocol is set already\n");
+         else
+           received_protocol = string_copy_taint(argv[++i], TRUE);
 
-      else if (Ustrcmp(argrest, "Ms") == 0)
-       sender_host_name = string_copy_taint(argv[++i], TRUE);
+       /* -oMs: Set sender host name */
 
-      /* -oMt: Set sender ident */
-
-      else if (Ustrcmp(argrest, "Mt") == 0)
-        {
-        sender_ident_set = TRUE;
-        sender_ident = argv[++i];
-        }
+       else if (Ustrcmp(argrest, "s") == 0)
+         sender_host_name = string_copy_taint(argv[++i], TRUE);
 
-      /* Else a bad argument */
+       /* -oMt: Set sender ident */
 
-      else
-        {
-        badarg = TRUE;
-        break;
-        }
-      }
+       else if (Ustrcmp(argrest, "t") == 0)
+         {
+         sender_ident_set = TRUE;
+         sender_ident = string_copy_taint(argv[++i], TRUE);
+         }
 
-    /* -om: Me-too flag for aliases. Exim always does this. Some programs
-    seem to call this as -m (undocumented), so that is also accepted (see
-    above). */
+       /* Else a bad argument */
 
-    else if (Ustrcmp(argrest, "m") == 0) {}
+       else
+         badarg = TRUE;
+       }
+       break;
 
-    /* -oo: An ancient flag for old-style addresses which still seems to
-    crop up in some calls (see in SCO). */
+      /* -om: Me-too flag for aliases. Exim always does this. Some programs
+      seem to call this as -m (undocumented), so that is also accepted (see
+      above). */
+      /* -oo: An ancient flag for old-style addresses which still seems to
+      crop up in some calls (see in SCO). */
 
-    else if (Ustrcmp(argrest, "o") == 0) {}
+      case 'm':
+      case 'o':
+       if (*argrest) badarg = TRUE;
+       break;
 
-    /* -oP <name>: set pid file path for daemon
-       -oPX:       delete pid file of daemon */
+      /* -oP <name>: set pid file path for daemon
+        -oPX:       delete pid file of daemon */
 
-    else if (Ustrcmp(argrest, "P") == 0)
-      override_pid_file_path = argv[++i];
+      case 'P':
+       if (!*argrest) override_pid_file_path = argv[++i];
+       else if (Ustrcmp(argrest, "X") == 0) delete_pid_file();
+       else badarg = TRUE;
+       break;
 
-    else if (Ustrcmp(argrest, "PX") == 0)
-      delete_pid_file();
 
-    /* -or <n>: set timeout for non-SMTP acceptance
-       -os <n>: set timeout for SMTP acceptance */
+      /* -or <n>: set timeout for non-SMTP acceptance
+        -os <n>: set timeout for SMTP acceptance */
 
-    else if (*argrest == 'r' || *argrest == 's')
-      {
-      int *tp = (*argrest == 'r')?
-        &arg_receive_timeout : &arg_smtp_receive_timeout;
-      if (argrest[1] == 0)
-        {
-        if (i+1 < argc) *tp= readconf_readtime(argv[++i], 0, FALSE);
-        }
-      else *tp = readconf_readtime(argrest + 1, 0, FALSE);
-      if (*tp < 0)
-        exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
-      }
+      case 'r':
+      case 's':
+       {
+       int * tp = argrest[-1] == 'r'
+         ? &arg_receive_timeout : &arg_smtp_receive_timeout;
+       if (*argrest)
+         *tp = readconf_readtime(argrest, 0, FALSE);
+       else if (i+1 < argc)
+         *tp = readconf_readtime(argv[++i], 0, FALSE);
+
+       if (*tp < 0)
+         exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+       }
+       break;
 
-    /* -oX <list>: Override local_interfaces and/or default daemon ports */
+      /* -oX <list>: Override local_interfaces and/or default daemon ports */
 
-    else if (Ustrcmp(argrest, "X") == 0)
-      override_local_interfaces = argv[++i];
+      case 'X':
+       if (*argrest) badarg = TRUE;
+       else override_local_interfaces = string_copy_taint(argv[++i], TRUE);
+       break;
 
-    /* Unknown -o argument */
+      /* Unknown -o argument */
 
-    else badarg = TRUE;
+      default:
+       badarg = TRUE;
+      }
     break;
 
 
@@ -3183,29 +3218,22 @@ for (i = 1; i < argc; i++)
     /* -panythingelse is taken as the Sendmail-compatible argument -prval:sval,
     which sets the host protocol and host name */
 
-    if (*argrest == 0)
-      if (i+1 < argc)
-       argrest = argv[++i];
-      else
-        { badarg = TRUE; break; }
+    if (!*argrest)
+      if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; }
 
-    if (*argrest != 0)
+    if (*argrest)
       {
-      uschar *hn;
+      uschar * hn = Ustrchr(argrest, ':');
 
       if (received_protocol)
         exim_fail("received_protocol is set already\n");
 
-      hn = Ustrchr(argrest, ':');
-      if (hn == NULL)
-        received_protocol = argrest;
+      if (!hn)
+        received_protocol = string_copy_taint(argrest, TRUE);
       else
         {
-       int old_pool = store_pool;
-       store_pool = POOL_PERM;
-        received_protocol = string_copyn(argrest, hn - argrest);
-       store_pool = old_pool;
-        sender_host_name = hn + 1;
+        received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE);
+        sender_host_name = string_copy_taint(hn + 1, TRUE);
         }
       }
     break;
@@ -3268,14 +3296,14 @@ for (i = 1; i < argc; i++)
     only, optionally named, optionally starting from a given message id. */
 
     if (!(list_queue || count_queue))
-      if (*argrest == 0
+      if (  !*argrest
         && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1])))
        {
        queue_interval = 0;
        if (i+1 < argc && mac_ismsgid(argv[i+1]))
-         start_queue_run_id = argv[++i];
+         start_queue_run_id = string_copy_taint(argv[++i], TRUE);
        if (i+1 < argc && mac_ismsgid(argv[i+1]))
-         stop_queue_run_id = argv[++i];
+         stop_queue_run_id = string_copy_taint(argv[++i], TRUE);
        }
 
     /* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally
@@ -3299,7 +3327,7 @@ for (i = 1; i < argc; i++)
     in all cases provided there are no further characters in this
     argument. */
 
-    if (*argrest != 0)
+    if (*argrest)
       for (int i = 0; i < nelem(rsopts); i++)
         if (Ustrcmp(argrest, rsopts[i]) == 0)
           {
@@ -3313,9 +3341,9 @@ for (i = 1; i < argc; i++)
     pick out particular messages. */
 
     if (*argrest)
-      deliver_selectstring = argrest;
+      deliver_selectstring = string_copy_taint(argrest, TRUE);
     else if (i+1 < argc)
-      deliver_selectstring = argv[++i];
+      deliver_selectstring = string_copy_taint(argv[++i], TRUE);
     else
       exim_fail("exim: string expected after -R\n");
     break;
@@ -3352,9 +3380,9 @@ for (i = 1; i < argc; i++)
     pick out particular messages. */
 
     if (*argrest)
-      deliver_selectstring_sender = argrest;
+      deliver_selectstring_sender = string_copy_taint(argrest, TRUE);
     else if (i+1 < argc)
-      deliver_selectstring_sender = argv[++i];
+      deliver_selectstring_sender = string_copy_taint(argv[++i], TRUE);
     else
       exim_fail("exim: string expected after -S\n");
     break;
@@ -3366,7 +3394,7 @@ for (i = 1; i < argc; i++)
 
     case 'T':
     if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0)
-      fudged_queue_times = argv[++i];
+      fudged_queue_times = string_copy_taint(argv[++i], TRUE);
     else badarg = TRUE;
     break;
 
@@ -3374,7 +3402,7 @@ for (i = 1; i < argc; i++)
     /* -t: Set flag to extract recipients from body of message. */
 
     case 't':
-    if (*argrest == 0) extract_recipients = TRUE;
+    if (!*argrest) extract_recipients = TRUE;
 
     /* -ti: Set flag to extract recipients from body of message, and also
     specify that dot does not end the message. */
@@ -3406,7 +3434,7 @@ for (i = 1; i < argc; i++)
     /* -v: verify things - this is a very low-level debugging */
 
     case 'v':
-    if (*argrest == 0)
+    if (!*argrest)
       {
       debug_selector |= D_v;
       debug_file = stderr;
@@ -3426,22 +3454,22 @@ for (i = 1; i < argc; i++)
     As Exim is 8-bit clean, it just ignores this flag. */
 
     case 'x':
-    if (*argrest != 0) badarg = TRUE;
+    if (*argrest) badarg = TRUE;
     break;
 
     /* -X: in sendmail: takes one parameter, logfile, and sends debugging
     logs to that file.  We swallow the parameter and otherwise ignore it. */
 
     case 'X':
-    if (*argrest == '\0')
+    if (!*argrest)
       if (++i >= argc)
         exim_fail("exim: string expected after -X\n");
     break;
 
     case 'z':
-    if (*argrest == '\0')
+    if (!*argrest)
       if (++i < argc)
-       log_oneline = argv[i];
+       log_oneline = string_copy_taint(argv[i], TRUE);
       else
         exim_fail("exim: file name expected after %s\n", argv[i-1]);
     break;
@@ -3463,12 +3491,15 @@ for (i = 1; i < argc; i++)
 
 /* If -R or -S have been specified without -q, assume a single queue run. */
 
-if (  (deliver_selectstring || deliver_selectstring_sender)
-   && queue_interval < 0)
-    queue_interval = 0;
+ if (  (deliver_selectstring || deliver_selectstring_sender)
+    && queue_interval < 0)
+  queue_interval = 0;
 
 
 END_ARG:
+ store_pool = old_pool;
+ }
+
 /* If usage_wanted is set we call the usage function - which never returns */
 if (usage_wanted) exim_usage(called_as);
 
@@ -4051,11 +4082,12 @@ if (  (debug_selector & D_any  ||  LOGGING(arguments))
       p = big_buffer + 3;
       }
     printing = string_printing(argv[i]);
-    if (printing[0] == 0) quote = US"\""; else
+    if (!*printing) quote = US"\"";
+    else
       {
       const uschar *pp = printing;
       quote = US"";
-      while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; }
+      while (*pp) if (isspace(*pp++)) { quote = US"\""; break; }
       }
     p += sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size -
       (p - big_buffer) - 4), printing, quote);
@@ -4091,19 +4123,19 @@ script. */
 if (bi_option)
   {
   (void)fclose(config_file);
-  if (bi_command != NULL)
+  if (bi_command)
     {
     int i = 0;
     uschar *argv[3];
     argv[i++] = bi_command;
-    if (alias_arg != NULL) argv[i++] = alias_arg;
+    if (alias_arg) argv[i++] = alias_arg;
     argv[i++] = NULL;
 
     setgroups(group_count, group_list);
     exim_setugid(real_uid, real_gid, FALSE, US"running bi_command");
 
     DEBUG(D_exec) debug_printf("exec %.256s %.256s\n", argv[0],
-      (argv[1] == NULL)? US"" : argv[1]);
+      argv[1] ? argv[1] : US"");
 
     execv(CS argv[0], (char *const *)argv);
     exim_fail("exim: exec failed: %s\n", strerror(errno));
@@ -4339,7 +4371,7 @@ if (list_queue)
 if (count_queue)
   {
   set_process_info("counting the queue");
-  queue_count();
+  fprintf(stdout, "%u\n", queue_count());
   exit(EXIT_SUCCESS);
   }
 
@@ -4366,7 +4398,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
         yield = EXIT_FAILURE;
     switch (msg_action)
       {
-      case MSG_REMOVE: MSG_DELETE: case MSG_FREEZE: case MSG_THAW: break;
+      case MSG_REMOVE: case MSG_FREEZE: case MSG_THAW: break;
       default: printf("\n"); break;
       }
     }
@@ -4409,7 +4441,7 @@ if (test_retry_arg >= 0)
   if (test_retry_arg >= argc)
     {
     printf("-brt needs a domain or address argument\n");
-    exim_exit(EXIT_FAILURE, US"main");
+    exim_exit(EXIT_FAILURE);
     }
   s1 = argv[test_retry_arg++];
   s2 = NULL;
@@ -4514,7 +4546,7 @@ if (test_retry_arg >= 0)
 
     printf("\n");
     }
-  exim_exit(EXIT_SUCCESS, US"main");
+  exim_exit(EXIT_SUCCESS);
   }
 
 /* Handle a request to list one or more configuration options */
@@ -4541,14 +4573,14 @@ if (list_options)
     else
       fail = !readconf_print(argv[i], NULL, flag_n);
     }
-  exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS, US"main");
+  exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
 if (list_config)
   {
   set_process_info("listing config");
   exim_exit(readconf_print(US"config", NULL, flag_n)
-               ? EXIT_SUCCESS : EXIT_FAILURE, US"main");
+               ? EXIT_SUCCESS : EXIT_FAILURE);
   }
 
 
@@ -4574,7 +4606,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
   if (prod_requires_admin && !f.admin_user)
     {
     fprintf(stderr, "exim: Permission denied\n");
-    exim_exit(EXIT_FAILURE, US"main");
+    exim_exit(EXIT_FAILURE);
     }
   set_process_info("delivering specified messages");
   if (deliver_give_up) forced_delivery = f.deliver_force_thaw = TRUE;
@@ -4582,9 +4614,11 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
     {
     int status;
     pid_t pid;
+    /*XXX This use of argv[i] for msg_id should really be tainted, but doing
+    that runs into a later copy into the untainted global message_id[] */
     if (i == argc - 1)
       (void)deliver_message(argv[i], forced_delivery, deliver_give_up);
-    else if ((pid = fork()) == 0)
+    else if ((pid = exim_fork(US"cmdline-delivery")) == 0)
       {
       (void)deliver_message(argv[i], forced_delivery, deliver_give_up);
       exim_underbar_exit(EXIT_SUCCESS);
@@ -4593,11 +4627,11 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
       {
       fprintf(stderr, "failed to fork delivery process for %s: %s\n", argv[i],
         strerror(errno));
-      exim_exit(EXIT_FAILURE, US"main");
+      exim_exit(EXIT_FAILURE);
       }
     else wait(&status);
     }
-  exim_exit(EXIT_SUCCESS, US"main");
+  exim_exit(EXIT_SUCCESS);
   }
 
 
@@ -4607,16 +4641,16 @@ turn into a queue runner, with an optional starting message id. */
 if (queue_interval == 0 && !f.daemon_listen)
   {
   DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n",
-    (start_queue_run_id == NULL)? US"" : US" starting at ",
-    (start_queue_run_id == NULL)? US"" : start_queue_run_id,
-    (stop_queue_run_id == NULL)?  US"" : US" stopping at ",
-    (stop_queue_run_id == NULL)?  US"" : stop_queue_run_id);
+    start_queue_run_id ? US" starting at " : US"",
+    start_queue_run_id ? start_queue_run_id: US"",
+    stop_queue_run_id ?  US" stopping at " : US"",
+    stop_queue_run_id ?  stop_queue_run_id : US"");
   if (*queue_name)
     set_process_info("running the '%s' queue (single queue run)", queue_name);
   else
     set_process_info("running the queue (single queue run)");
   queue_run(start_queue_run_id, stop_queue_run_id, FALSE);
-  exim_exit(EXIT_SUCCESS, US"main");
+  exim_exit(EXIT_SUCCESS);
   }
 
 
@@ -4786,10 +4820,10 @@ if (test_rewrite_arg >= 0)
   if (test_rewrite_arg >= argc)
     {
     printf("-brw needs an address argument\n");
-    exim_exit(EXIT_FAILURE, US"main");
+    exim_exit(EXIT_FAILURE);
     }
   rewrite_test(argv[test_rewrite_arg]);
-  exim_exit(EXIT_SUCCESS, US"main");
+  exim_exit(EXIT_SUCCESS);
   }
 
 /* A locally-supplied message is considered to be coming from a local user
@@ -4904,7 +4938,7 @@ if (verify_address_mode || f.address_test_mode)
     }
 
   route_tidyup();
-  exim_exit(exit_value, US"main");
+  exim_exit(exit_value);
   }
 
 /* Handle expansion checking. Either expand items on the command line, or read
@@ -4990,7 +5024,7 @@ if (expansion_test)
     deliver_datafile = -1;
     }
 
-  exim_exit(EXIT_SUCCESS, US"main: expansion test");
+  exim_exit(EXIT_SUCCESS);
   }
 
 
@@ -5027,9 +5061,9 @@ if (host_checking)
   if (!sender_ident_set)
     {
     sender_ident = NULL;
-    if (f.running_in_test_harness && sender_host_port != 0 &&
-        interface_address != NULL && interface_port != 0)
-      verify_get_ident(1413);
+    if (f.running_in_test_harness && sender_host_port
+       && interface_address && interface_port)
+      verify_get_ident(1223);          /* note hardwired port number */
     }
 
   /* In case the given address is a non-canonical IPv6 address, canonicalize
@@ -5066,6 +5100,7 @@ if (host_checking)
 
   if (smtp_start_session())
     {
+    rmark reset_point;
     for (; (reset_point = store_mark()); store_reset(reset_point))
       {
       if (smtp_setup_msg() <= 0) break;
@@ -5084,7 +5119,7 @@ if (host_checking)
       }
     smtp_log_no_mail();
     }
-  exim_exit(EXIT_SUCCESS, US"main");
+  exim_exit(EXIT_SUCCESS);
   }
 
 
@@ -5247,7 +5282,7 @@ if (smtp_input)
   if (!smtp_start_session())
     {
     mac_smtp_fflush();
-    exim_exit(EXIT_SUCCESS, US"smtp_start toplevel");
+    exim_exit(EXIT_SUCCESS);
     }
   }
 
@@ -5317,7 +5352,7 @@ collapsed). */
 
 while (more)
   {
-  reset_point = store_mark();
+  rmark reset_point = store_mark();
   message_id[0] = 0;
 
   /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
@@ -5362,14 +5397,14 @@ while (more)
        cancel_cutthrough_connection(TRUE, US"receive dropped");
         if (more) goto moreloop;
         smtp_log_no_mail();               /* Log no mail if configured */
-        exim_exit(EXIT_FAILURE, US"receive toplevel");
+        exim_exit(EXIT_FAILURE);
         }
       }
     else
       {
       cancel_cutthrough_connection(TRUE, US"message setup dropped");
       smtp_log_no_mail();               /* Log no mail if configured */
-      exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS, US"msg setup toplevel");
+      exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
       }
     }
 
@@ -5419,7 +5454,7 @@ while (more)
           if (error_handling == ERRORS_STDERR)
             {
             fprintf(stderr, "exim: too many recipients\n");
-            exim_exit(EXIT_FAILURE, US"main");
+            exim_exit(EXIT_FAILURE);
             }
           else
             return
@@ -5447,13 +5482,12 @@ while (more)
           errmess = US"unqualified recipient address not allowed";
           }
 
-        if (recipient == NULL)
-          {
+        if (!recipient)
           if (error_handling == ERRORS_STDERR)
             {
             fprintf(stderr, "exim: bad recipient address \"%s\": %s\n",
               string_printing(list[i]), errmess);
-            exim_exit(EXIT_FAILURE, US"main");
+            exim_exit(EXIT_FAILURE);
             }
           else
             {
@@ -5465,7 +5499,6 @@ while (more)
               moan_to_sender(ERRMESS_BADARGADDRESS, &eblock, NULL, stdin, TRUE)?
                 errors_sender_rc : EXIT_FAILURE;
             }
-          }
 
         receive_add_recipient(string_copy_taint(recipient, TRUE), -1);
         s = ss;
@@ -5478,8 +5511,8 @@ while (more)
 
     DEBUG(D_receive)
       {
-      if (sender_address != NULL) debug_printf("Sender: %s\n", sender_address);
-      if (recipients_list != NULL)
+      if (sender_address) debug_printf("Sender: %s\n", sender_address);
+      if (recipients_list)
         {
         debug_printf("Recipients:\n");
         for (int i = 0; i < recipients_count; i++)
@@ -5525,7 +5558,7 @@ while (more)
     for real; when reading the headers of a message for filter testing,
     it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
 
-    if (message_id[0] == 0) exim_exit(EXIT_FAILURE, US"main");
+    if (message_id[0] == 0) exim_exit(EXIT_FAILURE);
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
@@ -5537,17 +5570,15 @@ while (more)
 
   if (filter_test != FTEST_NONE)
     {
-    deliver_domain = (ftest_domain != NULL)?
-      ftest_domain : qualify_domain_recipient;
+    deliver_domain = ftest_domain ? ftest_domain : qualify_domain_recipient;
     deliver_domain_orig = deliver_domain;
-    deliver_localpart = (ftest_localpart != NULL)?
-      ftest_localpart : originator_login;
+    deliver_localpart = ftest_localpart ? ftest_localpart : originator_login;
     deliver_localpart_orig = deliver_localpart;
     deliver_localpart_prefix = ftest_prefix;
     deliver_localpart_suffix = ftest_suffix;
     deliver_home = originator_home;
 
-    if (return_path == NULL)
+    if (!return_path)
       {
       printf("Return-path copied from sender\n");
       return_path = string_copy(sender_address);
@@ -5558,19 +5589,19 @@ while (more)
 
     receive_add_recipient(
       string_sprintf("%s%s%s@%s",
-        (ftest_prefix == NULL)? US"" : ftest_prefix,
+        ftest_prefix ? ftest_prefix : US"",
         deliver_localpart,
-        (ftest_suffix == NULL)? US"" : ftest_suffix,
+        ftest_suffix ? ftest_suffix : US"",
         deliver_domain), -1);
 
     printf("Recipient   = %s\n", recipients_list[0].address);
-    if (ftest_prefix != NULL) printf("Prefix    = %s\n", ftest_prefix);
-    if (ftest_suffix != NULL) printf("Suffix    = %s\n", ftest_suffix);
+    if (ftest_prefix) printf("Prefix    = %s\n", ftest_prefix);
+    if (ftest_suffix) printf("Suffix    = %s\n", ftest_suffix);
 
     if (chdir("/"))   /* Get away from wherever the user is running this from */
       {
       DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n");
-      exim_exit(EXIT_FAILURE, US"main");
+      exim_exit(EXIT_FAILURE);
       }
 
     /* Now we run either a system filter test, or a user filter test, or both.
@@ -5578,17 +5609,17 @@ while (more)
     available to the user filter. We need to copy the filter variables
     explicitly. */
 
-    if ((filter_test & FTEST_SYSTEM) != 0)
+    if (filter_test & FTEST_SYSTEM)
       if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more))
-        exim_exit(EXIT_FAILURE, US"main");
+        exim_exit(EXIT_FAILURE);
 
     memcpy(filter_sn, filter_n, sizeof(filter_sn));
 
-    if ((filter_test & FTEST_USER) != 0)
+    if (filter_test & FTEST_USER)
       if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more))
-        exim_exit(EXIT_FAILURE, US"main");
+        exim_exit(EXIT_FAILURE);
 
-    exim_exit(EXIT_SUCCESS, US"main");
+    exim_exit(EXIT_SUCCESS);
     }
 
   /* Else act on the result of message reception. We should not get here unless
@@ -5596,9 +5627,9 @@ while (more)
   will be TRUE. If it is not, check on the number of messages received in this
   connection. */
 
-  if (!session_local_queue_only &&
-      smtp_accept_queue_per_connection > 0 &&
-      receive_messagecount > smtp_accept_queue_per_connection)
+  if (  !session_local_queue_only
+     && smtp_accept_queue_per_connection > 0
+     && receive_messagecount > smtp_accept_queue_per_connection)
     {
     session_local_queue_only = TRUE;
     queue_only_reason = 2;
@@ -5614,16 +5645,12 @@ while (more)
   ones. However, there are odd cases where this is not wanted, so this can be
   changed by setting queue_only_load_latch false. */
 
-  local_queue_only = session_local_queue_only;
-  if (!local_queue_only && queue_only_load >= 0)
-    {
-    local_queue_only = (load_average = OS_GETLOADAVG()) > queue_only_load;
-    if (local_queue_only)
+  if (!(local_queue_only = session_local_queue_only) && queue_only_load >= 0)
+    if ((local_queue_only = (load_average = OS_GETLOADAVG()) > queue_only_load))
       {
       queue_only_reason = 3;
       if (queue_only_load_latch) session_local_queue_only = TRUE;
       }
-    }
 
   /* If running as an MUA wrapper, all queueing options and freezing options
   are ignored. */
@@ -5670,7 +5697,7 @@ while (more)
     pid_t pid;
     search_tidyup();
 
-    if ((pid = fork()) == 0)
+    if ((pid = exim_fork(US"local-accept-delivery")) == 0)
       {
       int rc;
       close_unwanted();      /* Close unwanted file descriptors and TLS */
@@ -5714,7 +5741,7 @@ while (more)
          log_write(0, LOG_MAIN|LOG_PANIC,
            "process %d crashed with signal %d while delivering %s",
            (int)pid, status & 0x00ff, message_id);
-       if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"main");
+       if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
        }
       }
     }
@@ -5746,7 +5773,7 @@ moreloop:
   store_reset(reset_point);
   }
 
-exim_exit(EXIT_SUCCESS, US"main");   /* Never returns */
+exim_exit(EXIT_SUCCESS);   /* Never returns */
 return 0;                  /* To stop compiler warning */
 }