String expansions: fix ${extract }, for the numeric/3-string case. Bug 1807
[exim.git] / src / src / expand.c
index b9d78238be9f3194524a4c9612cb8d5bb7c4d339..47f1453bebc2cf89cf906e8d1d75c698f1cfeffd 100644 (file)
@@ -543,6 +543,7 @@ static var_entry var_table[] = {
   { "host_lookup_deferred",vtype_int,         &host_lookup_deferred },
   { "host_lookup_failed",  vtype_int,         &host_lookup_failed },
   { "host_port",           vtype_int,         &deliver_host_port },
+  { "initial_cwd",         vtype_stringptr,   &initial_cwd },
   { "inode",               vtype_ino,         &deliver_inode },
   { "interface_address",   vtype_stringptr,   &interface_address },
   { "interface_port",      vtype_int,         &interface_port },
@@ -1073,6 +1074,8 @@ return s;
 
 Returns:  a pointer to the character after the last digit
 */
+/*XXX consider expanding to int_eximarith_t.  But the test for
+"overbig numbers" in 0002 still needs to overflow it. */
 
 static uschar *
 read_number(int *n, uschar *s)
@@ -1849,7 +1852,9 @@ switch (vp->type)
            start_offset = SPOOL_DATA_START_OFFSET;
          }
        }
-      lseek(deliver_datafile, start_offset, SEEK_SET);
+      if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0)
+       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "deliver_datafile lseek: %s",
+         strerror(errno));
       len = read(deliver_datafile, body, len);
       if (len > 0)
        {
@@ -3209,10 +3214,11 @@ if (*s++ != '}') goto FAILED_CURLY;
 if (yes)
   *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, sub1, Ustrlen(sub1));
 
-/* If this is called from a lookup or an extract, we want to restore $value to
-what it was at the start of the item, so that it has this value during the
-second string expansion. For the call from "if" or "run" to this function,
-save_lookup is set to lookup_value, so that this statement does nothing. */
+/* If this is called from a lookup/env or a (cert)extract, we want to restore
+$value to what it was at the start of the item, so that it has this value
+during the second string expansion. For the call from "if" or "run" to this
+function, save_lookup is set to lookup_value, so that this statement does
+nothing. */
 
 lookup_value = save_lookup;
 
@@ -3402,8 +3408,8 @@ if (Ustrlen(key) > 64)
   return NULL;
 
 hash_source = string_cat(NULL,&size,&offset,key_num,1);
-string_cat(hash_source,&size,&offset,daystamp,3);
-string_cat(hash_source,&size,&offset,address,Ustrlen(address));
+hash_source = string_cat(hash_source,&size,&offset,daystamp,3);
+hash_source = string_cat(hash_source,&size,&offset,address,Ustrlen(address));
 hash_source[offset] = '\0';
 
 DEBUG(D_expand) debug_printf("prvs: hash source is '%s'\n", hash_source);
@@ -3443,9 +3449,9 @@ return finalhash_hex;
 *        Join a file onto the output string      *
 *************************************************/
 
-/* This is used for readfile and after a run expansion. It joins the contents
-of a file onto the output string, globally replacing newlines with a given
-string (optionally). The file is closed at the end.
+/* This is used for readfile/readsock and after a run expansion.
+It joins the contents of a file onto the output string, globally replacing
+newlines with a given string (optionally).
 
 Arguments:
   f            the FILE
@@ -3460,21 +3466,19 @@ Returns:       new value of string pointer
 static uschar *
 cat_file(FILE *f, uschar *yield, int *sizep, int *ptrp, uschar *eol)
 {
-int eollen;
+int eollen = eol ? Ustrlen(eol) : 0;
 uschar buffer[1024];
 
-eollen = (eol == NULL)? 0 : Ustrlen(eol);
-
-while (Ufgets(buffer, sizeof(buffer), f) != NULL)
+while (Ufgets(buffer, sizeof(buffer), f))
   {
   int len = Ustrlen(buffer);
-  if (eol != NULL && buffer[len-1] == '\n') len--;
+  if (eol && buffer[len-1] == '\n') len--;
   yield = string_cat(yield, sizep, ptrp, buffer, len);
   if (buffer[len] != 0)
     yield = string_cat(yield, sizep, ptrp, eol, eollen);
   }
 
-if (yield != NULL) yield[*ptrp] = 0;
+if (yield) yield[*ptrp] = 0;
 
 return yield;
 }
@@ -4870,8 +4874,7 @@ while (*s != 0)
       const uschar **argv;
       pid_t pid;
       int fd_in, fd_out;
-      int lsize = 0;
-      int lptr = 0;
+      int lsize = 0, lptr = 0;
 
       if ((expand_forbid & RDO_RUN) != 0)
         {
@@ -4899,15 +4902,11 @@ while (*s != 0)
             NULL,                               /* no transporting address */
             US"${run} expansion",               /* for error messages */
             &expand_string_message))            /* where to put error message */
-          {
           goto EXPAND_FAILED;
-          }
 
         /* Create the child process, making it a group leader. */
 
-        pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE);
-
-        if (pid < 0)
+        if ((pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE)) < 0)
           {
           expand_string_message =
             string_sprintf("couldn't create child process: %s", strerror(errno));
@@ -4927,7 +4926,7 @@ while (*s != 0)
         f = fdopen(fd_out, "rb");
         sigalrm_seen = FALSE;
         alarm(60);
-        lookup_value = cat_file(f, lookup_value, &lsize, &lptr, NULL);
+        lookup_value = cat_file(f, NULL, &lsize, &lptr, NULL);
         alarm(0);
         (void)fclose(f);
 
@@ -5308,9 +5307,25 @@ while (*s != 0)
       int save_expand_nmax =
         save_expand_strings(save_expand_nstring, save_expand_nlength);
 
-      /* Read the arguments */
+      /* While skipping we cannot rely on the data for expansions being
+      available (eg. $item) hence cannot decide on numeric vs. keyed.
+      Just read as many arguments as there are. */
 
-      for (i = 0; i < j; i++)
+      if (skipping)
+       {
+        while (isspace(*s)) s++;
+        while (*s == '{')
+         {
+          if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
+           goto EXPAND_FAILED;                                 /*{*/
+          if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+         while (isspace(*s)) s++;
+         }
+       if (*s != '}')
+         goto EXPAND_FAILED_CURLY;
+       }
+
+      else for (i = 0; i < j; i++) /* Read the proper number of arguments */
         {
         while (isspace(*s)) s++;
         if (*s == '{')                                                 /*}*/
@@ -5337,27 +5352,24 @@ while (*s != 0)
             while (len > 0 && isspace(p[len-1])) len--;
             p[len] = 0;
 
-            if (!skipping)
+           if (*p == 0)
              {
-             if (*p == 0)
-               {
-               expand_string_message = US"first argument of \"extract\" must "
-                 "not be empty";
-               goto EXPAND_FAILED;
-               }
+             expand_string_message = US"first argument of \"extract\" must "
+               "not be empty";
+             goto EXPAND_FAILED;
+             }
 
-             if (*p == '-')
-               {
-               field_number = -1;
-               p++;
-               }
-             while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0';
-             if (*p == 0)
-               {
-               field_number *= x;
-               j = 3;               /* Need 3 args */
-               field_number_set = TRUE;
-               }
+           if (*p == '-')
+             {
+             field_number = -1;
+             p++;
+             }
+           while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0';
+           if (*p == 0)
+             {
+             field_number *= x;
+             j = 3;               /* Need 3 args */
+             field_number_set = TRUE;
              }
             }
           }