GnuTLS: Debug output keying info. OpenSSL: TLS1.2 keying.
[exim.git] / src / src / transport.c
index 6dcc0c7d42f25cb10d1da632a2b7681b10ea200c..0fa90cb041fc79cb29fd8193704f63fb6e47f013 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* General functions concerned with transportation, and generic options for all
@@ -17,6 +17,7 @@ are other options living inside this structure which can be set only from
 certain transports. */
 
 optionlist optionlist_transports[] = {
+  /*   name            type                                    value */
   { "*expand_group",    opt_stringptr|opt_hidden|opt_public,
                  (void *)offsetof(transport_instance, expand_gid) },
   { "*expand_user",     opt_stringptr|opt_hidden|opt_public,
@@ -94,12 +95,11 @@ int optionlist_transports_size = nelem(optionlist_transports);
 void
 options_transports(void)
 {
-struct transport_info * ti;
 uschar buf[64];
 
 options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
 
-for (ti = transports_available; ti->driver_name[0]; ti++)
+for (transport_info * ti = transports_available; ti->driver_name[0]; ti++)
   {
   spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", ti->driver_name);
   builtin_macro_create(buf);
@@ -141,8 +141,6 @@ the work. */
 void
 transport_init(void)
 {
-transport_instance *t;
-
 readconf_driver_init(US"transport",
   (driver_instance **)(&transports),     /* chain anchor */
   (driver_info *)transports_available,   /* available drivers */
@@ -155,7 +153,7 @@ readconf_driver_init(US"transport",
 /* Now scan the configured transports and check inconsistencies. A shadow
 transport is permitted only for local transports. */
 
-for (t = transports; t; t = t->next)
+for (transport_instance * t = transports; t; t = t->next)
   {
   if (!t->info->local && t->shadow)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
@@ -219,14 +217,14 @@ Returns:    TRUE on success, FALSE on failure (with errno preserved);
 static BOOL
 transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
 {
-int i, rc, save_errno;
+int rc, save_errno;
 int local_timeout = transport_write_timeout;
 int fd = tctx->u.fd;
 
 /* This loop is for handling incomplete writes and other retries. In most
 normal cases, it is only ever executed once. */
 
-for (i = 0; i < 100; i++)
+for (int i = 0; i < 100; i++)
   {
   DEBUG(D_transport)
     debug_printf("writing data block fd=%d size=%d timeout=%d%s\n",
@@ -241,10 +239,11 @@ for (i = 0; i < 100; i++)
     {
     rc =
 #ifdef SUPPORT_TLS
-       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
 #endif
 #ifdef MSG_MORE
-       more ? send(fd, block, len, MSG_MORE) :
+       more && !(tctx->options & topt_not_socket)
+         ? send(fd, block, len, MSG_MORE) :
 #endif
        write(fd, block, len);
     save_errno = errno;
@@ -254,19 +253,20 @@ for (i = 0; i < 100; i++)
 
   else
     {
-    alarm(local_timeout);
+    ALARM(local_timeout);
 
     rc =
 #ifdef SUPPORT_TLS
-       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
 #endif
 #ifdef MSG_MORE
-       more ? send(fd, block, len, MSG_MORE) :
+       more && !(tctx->options & topt_not_socket)
+         ? send(fd, block, len, MSG_MORE) :
 #endif
        write(fd, block, len);
 
     save_errno = errno;
-    local_timeout = alarm(0);
+    local_timeout = ALARM_CLR(0);
     if (sigalrm_seen)
       {
       errno = ETIMEDOUT;
@@ -345,12 +345,9 @@ if (!(tctx->options & topt_output_string))
 /* Write to expanding-string.  NOTE: not NUL-terminated */
 
 if (!tctx->u.msg)
-  {
-  tctx->u.msg = store_get(tctx->msg_size = 1024);
-  tctx->msg_ptr = 0;
-  }
+  tctx->u.msg = string_get(1024);
 
-tctx->u.msg = string_catn(tctx->u.msg, &tctx->msg_size, &tctx->msg_ptr, block, len);
+tctx->u.msg = string_catn(tctx->u.msg, block, len);
 return TRUE;
 }
 
@@ -375,13 +372,15 @@ BOOL
 transport_write_string(int fd, const char *format, ...)
 {
 transport_ctx tctx = {{0}};
+gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
 va_list ap;
+
 va_start(ap, format);
-if (!string_vformat(big_buffer, big_buffer_size, format, ap))
+if (!string_vformat(&gs, FALSE, format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
 va_end(ap);
 tctx.u.fd = fd;
-return transport_write_block(&tctx, big_buffer, Ustrlen(big_buffer), FALSE);
+return transport_write_block(&tctx, gs.s, gs.ptr, FALSE);
 }
 
 
@@ -425,7 +424,6 @@ write_chunk(transport_ctx * tctx, uschar *chunk, int len)
 {
 uschar *start = chunk;
 uschar *end = chunk + len;
-uschar *ptr;
 int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
 
 /* The assumption is made that the check string will never stretch over move
@@ -462,7 +460,7 @@ if (nl_partial_match >= 0)
 for possible escaping. The code for the non-NL route should be as fast as
 possible. */
 
-for (ptr = start; ptr < end; ptr++)
+for (uschar * ptr = start; ptr < end; ptr++)
   {
   int ch, len;
 
@@ -495,7 +493,7 @@ for (ptr = start; ptr < end; ptr++)
 
   if (  *ptr == '\r' && ptr[1] == '\n'
      && !(tctx->options & topt_use_crlf)
-     && spool_file_wireformat
+     && f.spool_file_wireformat
      )
     ptr++;
 
@@ -505,7 +503,7 @@ for (ptr = start; ptr < end; ptr++)
 
     /* Insert CR before NL if required */
 
-    if (tctx->options & topt_use_crlf && !spool_file_wireformat)
+    if (tctx->options & topt_use_crlf && !f.spool_file_wireformat)
       *chunk_ptr++ = '\r';
     *chunk_ptr++ = '\n';
     transport_newlines++;
@@ -590,7 +588,7 @@ at = Ustrrchr(addr->address, '@');
 plen = (addr->prefix == NULL)? 0 : Ustrlen(addr->prefix);
 slen = Ustrlen(addr->suffix);
 
-return string_sprintf("%.*s@%s", (at - addr->address - plen - slen),
+return string_sprintf("%.*s@%s", (int)(at - addr->address - plen - slen),
    addr->address + plen, at + 1);
 }
 
@@ -694,7 +692,6 @@ BOOL
 transport_headers_send(transport_ctx * tctx,
   BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
 {
-header_line *h;
 const uschar *list;
 transport_instance * tblock = tctx ? tctx->tblock : NULL;
 address_item * addr = tctx ? tctx->addr : NULL;
@@ -706,13 +703,12 @@ match any entries therein.  It is a colon-sep list; expand the items
 separately and squash any empty ones.
 Then check addr->prop.remove_headers too, provided that addr is not NULL. */
 
-for (h = header_list; h; h = h->next) if (h->type != htype_old)
+for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old)
   {
-  int i;
   BOOL include_header = TRUE;
 
   list = tblock ? tblock->remove_headers : NULL;
-  for (i = 0; i < 2; i++)    /* For remove_headers && addr->prop.remove_headers */
+  for (int i = 0; i < 2; i++)    /* For remove_headers && addr->prop.remove_headers */
     {
     if (list)
       {
@@ -723,7 +719,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old)
        int len;
 
        if (i == 0)
-         if (!(s = expand_string(s)) && !expand_string_forcedfail)
+         if (!(s = expand_string(s)) && !f.expand_string_forcedfail)
            {
            errno = ERRNO_CHHEADER_FAIL;
            return FALSE;
@@ -782,10 +778,9 @@ Headers added to an address by a router are guaranteed to end with a newline.
 
 if (addr)
   {
-  int i;
   header_line *hprev = addr->prop.extra_headers;
-  header_line *hnext;
-  for (i = 0; i < 2; i++)
+  header_line *hnext, * h;
+  for (int i = 0; i < 2; i++)
     for (h = hprev, hprev = NULL; h; h = hnext)
       {
       hnext = h->next;
@@ -829,7 +824,7 @@ if (tblock && (list = CUS tblock->add_headers))
          }
        }
       }
-    else if (!expand_string_forcedfail)
+    else if (!f.expand_string_forcedfail)
       { errno = ERRNO_CHHEADER_FAIL; return FALSE; }
   }
 
@@ -934,8 +929,8 @@ so temporarily hide the global that adjusts for its format. */
 
 if (!(tctx->options & topt_no_headers))
   {
-  BOOL save_wireformat = spool_file_wireformat;
-  spool_file_wireformat = FALSE;
+  BOOL save_wireformat = f.spool_file_wireformat;
+  f.spool_file_wireformat = FALSE;
 
   /* Add return-path: if requested. */
 
@@ -952,7 +947,6 @@ if (!(tctx->options & topt_no_headers))
   if (tctx->options & topt_add_envelope_to)
     {
     BOOL first = TRUE;
-    address_item *p;
     struct aci *plist = NULL;
     struct aci *dlist = NULL;
     void *reset_point = store_get(0);
@@ -963,8 +957,9 @@ if (!(tctx->options & topt_no_headers))
     anchors for lists of addresses already handled; they have to be defined at
     this level because write_env_to() calls itself recursively. */
 
-    for (p = tctx->addr; p; p = p->next)
-      if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad;
+    for (address_item * p = tctx->addr; p; p = p->next)
+      if (!write_env_to(p, &plist, &dlist, &first, tctx))
+       goto bad;
 
     /* Add a final newline and reset the store used for tracking duplicates */
 
@@ -992,11 +987,11 @@ if (!(tctx->options & topt_no_headers))
   if (!transport_headers_send(tctx, &write_chunk))
     {
 bad:
-    spool_file_wireformat = save_wireformat;
+    f.spool_file_wireformat = save_wireformat;
     return FALSE;
     }
 
-  spool_file_wireformat = save_wireformat;
+  f.spool_file_wireformat = save_wireformat;
   }
 
 /* When doing RFC3030 CHUNKING output, work out how much data would be in a
@@ -1025,7 +1020,7 @@ if (tctx->options & topt_use_bdat)
     if (size_limit > 0  &&  fsize > size_limit)
       fsize = size_limit;
     size = hsize + fsize;
-    if (tctx->options & topt_use_crlf  &&  !spool_file_wireformat)
+    if (tctx->options & topt_use_crlf  &&  !f.spool_file_wireformat)
       size += body_linecount;  /* account for CRLF-expansion */
 
     /* With topt_use_bdat we never do dot-stuffing; no need to
@@ -1072,10 +1067,10 @@ This should get used for CHUNKING output and also for writing the -K file for
 dkim signing,  when we had CHUNKING input.  */
 
 #ifdef OS_SENDFILE
-if (  spool_file_wireformat
+if (  f.spool_file_wireformat
    && !(tctx->options & (topt_no_body | topt_end_dot))
    && !nl_check_length
-   && tls_out.active != tctx->u.fd
+   && tls_out.active.sock != tctx->u.fd
    )
   {
   ssize_t copied = 0;
@@ -1106,7 +1101,7 @@ DEBUG(D_transport) debug_printf("cannot use sendfile for body: no support\n");
 DEBUG(D_transport)
   if (!(tctx->options & topt_no_body))
     debug_printf("cannot use sendfile for body: %s\n",
-      !spool_file_wireformat ? "spoolfile not wireformat"
+      !f.spool_file_wireformat ? "spoolfile not wireformat"
       : tctx->options & topt_end_dot ? "terminating dot wanted"
       : nl_check_length ? "dot- or From-stuffing wanted"
       : "TLS output wanted");
@@ -1132,9 +1127,10 @@ if (!(tctx->options & topt_no_body))
   if (len != 0) return FALSE;
   }
 
-/* Finished with the check string */
+/* Finished with the check string, and spool-format consideration */
 
 nl_check_length = nl_escape_length = 0;
+f.spool_file_wireformat = FALSE;
 
 /* If requested, add a terminating "." line (SMTP output). */
 
@@ -1171,12 +1167,12 @@ BOOL
 transport_write_message(transport_ctx * tctx, int size_limit)
 {
 BOOL last_filter_was_NL = TRUE;
-BOOL save_spool_file_wireformat = spool_file_wireformat;
+BOOL save_spool_file_wireformat = f.spool_file_wireformat;
 int rc, len, yield, fd_read, fd_write, save_errno;
 int pfd[2] = {-1, -1};
 pid_t filter_pid, write_pid;
 
-transport_filter_timed_out = FALSE;
+f.transport_filter_timed_out = FALSE;
 
 /* If there is no filter command set up, call the internal function that does
 the actual work, passing it the incoming fd, and return its result. */
@@ -1276,7 +1272,7 @@ if (write_pid < 0)
 
 /* When testing, let the subprocess get going */
 
-if (running_in_test_harness) millisleep(250);
+if (f.running_in_test_harness) millisleep(250);
 
 DEBUG(D_transport)
   debug_printf("process %d writing to transport filter\n", (int)write_pid);
@@ -1293,19 +1289,19 @@ no data is returned, that counts as "ended with NL" (default setting of the
 variable is TRUE).  The output should always be unix-format as we converted
 any wireformat source on writing input to the filter. */
 
-spool_file_wireformat = FALSE;
+f.spool_file_wireformat = FALSE;
 chunk_ptr = deliver_out_buffer;
 
 for (;;)
   {
   sigalrm_seen = FALSE;
-  alarm(transport_filter_timeout);
+  ALARM(transport_filter_timeout);
   len = read(fd_read, deliver_in_buffer, DELIVER_IN_BUFFER_SIZE);
-  alarm(0);
+  ALARM_CLR(0);
   if (sigalrm_seen)
     {
     errno = ETIMEDOUT;
-    transport_filter_timed_out = TRUE;
+    f.transport_filter_timed_out = TRUE;
     goto TIDY_UP;
     }
 
@@ -1333,7 +1329,7 @@ there has been an error, kill the processes before waiting for them, just to be
 sure. Also apply a paranoia timeout. */
 
 TIDY_UP:
-spool_file_wireformat = save_spool_file_wireformat;
+f.spool_file_wireformat = save_spool_file_wireformat;
 save_errno = errno;
 
 (void)close(fd_read);
@@ -1401,6 +1397,7 @@ filter was not NL, insert a NL to make the SMTP protocol work. */
 if (yield)
   {
   nl_check_length = nl_escape_length = 0;
+  f.spool_file_wireformat = FALSE;
   if (  tctx->options & topt_end_dot
      && ( last_filter_was_NL
         ? !write_chunk(tctx, US".\n", 2)
@@ -1470,7 +1467,6 @@ void
 transport_update_waiting(host_item *hostlist, uschar *tpname)
 {
 const uschar *prevname = US"";
-host_item *host;
 open_db dbblock;
 open_db *dbm_file;
 
@@ -1485,12 +1481,11 @@ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
 /* Scan the list of hosts for which this message is waiting, and ensure
 that the message id is in each host record. */
 
-for (host = hostlist; host; host = host->next)
+for (host_item * host = hostlist; host; host = host->next)
   {
   BOOL already = FALSE;
   dbdata_wait *host_record;
-  uschar *s;
-  int i, host_length;
+  int host_length;
   uschar buffer[256];
 
   /* Skip if this is the same host as we just processed; otherwise remember
@@ -1513,7 +1508,7 @@ for (host = hostlist; host; host = host->next)
 
   /* Search the record to see if the current message is already in it. */
 
-  for (s = host_record->text; s < host_record->text + host_length;
+  for (uschar * s = host_record->text; s < host_record->text + host_length;
        s += MESSAGE_ID_LENGTH)
     if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
       { already = TRUE; break; }
@@ -1521,14 +1516,14 @@ for (host = hostlist; host; host = host->next)
   /* If we haven't found this message in the main record, search any
   continuation records that exist. */
 
-  for (i = host_record->sequence - 1; i >= 0 && !already; i--)
+  for (int i = host_record->sequence - 1; i >= 0 && !already; i--)
     {
     dbdata_wait *cont;
     sprintf(CS buffer, "%.200s:%d", host->name, i);
     if ((cont = dbfn_read(dbm_file, buffer)))
       {
       int clen = cont->count * MESSAGE_ID_LENGTH;
-      for (s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
+      for (uschar * s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
         if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
           { already = TRUE; break; }
       }
@@ -1774,13 +1769,12 @@ while (1)
 
   while (host_length <= 0)
     {
-    int i;
     dbdata_wait * newr = NULL;
     uschar buffer[256];
 
     /* Search for a continuation */
 
-    for (i = host_record->sequence - 1; i >= 0 && !newr; i--)
+    for (int i = host_record->sequence - 1; i >= 0 && !newr; i--)
       {
       sprintf(CS buffer, "%.200s:%d", hostname, i);
       newr = dbfn_read(dbm_file, buffer);
@@ -1868,19 +1862,19 @@ but we have a number of extras that may be added. */
 
 argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
 
-if (smtp_authenticated)                                argv[i++] = US"-MCA";
+if (f.smtp_authenticated)                      argv[i++] = US"-MCA";
 if (smtp_peer_options & OPTION_CHUNKING)       argv[i++] = US"-MCK";
 if (smtp_peer_options & OPTION_DSN)            argv[i++] = US"-MCD";
 if (smtp_peer_options & OPTION_PIPE)           argv[i++] = US"-MCP";
 if (smtp_peer_options & OPTION_SIZE)           argv[i++] = US"-MCS";
 #ifdef SUPPORT_TLS
 if (smtp_peer_options & OPTION_TLS)
-  if (tls_out.active >= 0 || continue_proxy_cipher)
+  if (tls_out.active.sock >= 0 || continue_proxy_cipher)
     {
     argv[i++] = US"-MCt";
     argv[i++] = sending_ip_address;
     argv[i++] = string_sprintf("%d", sending_port);
-    argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
+    argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher;
     }
   else
     argv[i++] = US"-MCT";
@@ -1954,7 +1948,7 @@ if ((pid = fork()) == 0)
     DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
     _exit(EXIT_SUCCESS);
     }
-  if (running_in_test_harness) sleep(1);
+  if (f.running_in_test_harness) sleep(1);
 
   transport_do_pass_socket(transport_name, hostname, hostaddress,
     id, socket_fd);
@@ -2011,19 +2005,18 @@ transport_set_up_command(const uschar ***argvptr, uschar *cmd,
   BOOL expand_arguments, int expand_failed, address_item *addr,
   uschar *etext, uschar **errptr)
 {
-address_item *ad;
 const uschar **argv;
 uschar *s, *ss;
 int address_count = 0;
 int argcount = 0;
-int i, max_args;
+int max_args;
 
 /* Get store in which to build an argument list. Count the number of addresses
 supplied, and allow for that many arguments, plus an additional 60, which
 should be enough for anybody. Multiple addresses happen only when the local
 delivery batch option is set. */
 
-for (ad = addr; ad != NULL; ad = ad->next) address_count++;
+for (address_item * ad = addr; ad; ad = ad->next) address_count++;
 max_args = address_count + 60;
 *argvptr = argv = store_get((max_args+1)*sizeof(uschar *));
 
@@ -2050,7 +2043,7 @@ while (*s != 0 && argcount < max_args)
   while (isspace(*s)) s++;
   }
 
-argv[argcount] = (uschar *)0;
+argv[argcount] = US 0;
 
 /* If *s != 0 we have run out of argument slots. */
 
@@ -2086,7 +2079,7 @@ $recipients. */
 DEBUG(D_transport)
   {
   debug_printf("direct command:\n");
-  for (i = 0; argv[i] != (uschar *)0; i++)
+  for (int i = 0; argv[i] != US 0; i++)
     debug_printf("  argv[%d] = %s\n", i, string_printing(argv[i]));
   }
 
@@ -2096,7 +2089,7 @@ if (expand_arguments)
     addr->parent != NULL &&
     Ustrcmp(addr->parent->address, "system-filter") == 0;
 
-  for (i = 0; argv[i] != (uschar *)0; i++)
+  for (int i = 0; argv[i] != US 0; i++)
     {
 
     /* Handle special fudge for passing an address list */
@@ -2120,10 +2113,11 @@ if (expand_arguments)
         memmove(argv + i + 1 + additional, argv + i + 1,
           (argcount - i)*sizeof(uschar *));
 
-      for (ad = addr; ad != NULL; ad = ad->next) {
-          argv[i++] = ad->address;
-          argcount++;
-      }
+      for (address_item * ad = addr; ad; ad = ad->next)
+        {
+       argv[i++] = ad->address;
+       argcount++;
+       }
 
       /* Subtract one since we replace $pipe_addresses */
       argcount--;
@@ -2136,7 +2130,6 @@ if (expand_arguments)
         (Ustrcmp(argv[i], "$address_pipe") == 0 ||
          Ustrcmp(argv[i], "${address_pipe}") == 0))
       {
-      int address_pipe_i;
       int address_pipe_argcount = 0;
       int address_pipe_max_args;
       uschar **address_pipe_argv;
@@ -2180,7 +2173,7 @@ if (expand_arguments)
         while (isspace(*s)) s++; /* strip space after arg */
         }
 
-      address_pipe_argv[address_pipe_argcount] = (uschar *)0;
+      address_pipe_argv[address_pipe_argcount] = US 0;
 
       /* If *s != 0 we have run out of argument slots. */
       if (*s != 0)
@@ -2227,8 +2220,8 @@ if (expand_arguments)
       /* Now we fill in the slots we just moved argv out of
        * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
        */
-      for (address_pipe_i = 0;
-           address_pipe_argv[address_pipe_i] != (uschar *)0;
+      for (int address_pipe_i = 0;
+           address_pipe_argv[address_pipe_i] != US 0;
            address_pipe_i++)
         {
         argv[i++] = address_pipe_argv[address_pipe_i];
@@ -2245,9 +2238,9 @@ if (expand_arguments)
     else
       {
       const uschar *expanded_arg;
-      enable_dollar_recipients = allow_dollar_recipients;
+      f.enable_dollar_recipients = allow_dollar_recipients;
       expanded_arg = expand_cstring(argv[i]);
-      enable_dollar_recipients = FALSE;
+      f.enable_dollar_recipients = FALSE;
 
       if (expanded_arg == NULL)
         {
@@ -2269,7 +2262,7 @@ if (expand_arguments)
   DEBUG(D_transport)
     {
     debug_printf("direct command after expansion:\n");
-    for (i = 0; argv[i] != (uschar *)0; i++)
+    for (int i = 0; argv[i] != US 0; i++)
       debug_printf("  argv[%d] = %s\n", i, string_printing(argv[i]));
     }
   }