[exim-cvs] Add ${listextract {n}{list}...}

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Add ${listextract {n}{list}...}
Gitweb: http://git.exim.org/exim.git/commitdiff/aa26e1378803587c24924ad0055318959d597802
Commit:     aa26e1378803587c24924ad0055318959d597802
Parent:     c5430c20d5c3b6fd293b96761ef850d6b301d791
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Sun Oct 27 15:18:44 2013 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Sun Dec 22 17:38:14 2013 +0000


    Add ${listextract {n}{list}...}
---
 doc/doc-docbook/spec.xfpt    |  38 +++++++++++++++
 doc/doc-txt/ChangeLog        |   2 +
 src/src/expand.c             | 113 ++++++++++++++++++++++++++++++++++++++++++-
 test/scripts/0000-Basic/0002 |   7 +++
 test/stdout/0002             |   7 +++
 5 files changed, 166 insertions(+), 1 deletion(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 17bdcc9..f29c387 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -9181,6 +9181,44 @@ of <&'string2'&>, whichever is the shorter. Do not confuse &%length%& with
&%strlen%&, which gives the length of a string.


+.vitem "&*${listextract{*&<&'number'&>&*}&&&
+        {*&<&'string1'&>&*}{*&<&'string2'&>&*}{*&<&'string3'&>&*}}*&"
+.cindex "expansion" "extracting list elements by number"
+.cindex "&%listextract%&" "extract list elements by number"
+.cindex "list" "extracting elements by number"
+The <&'number'&> argument must consist entirely of decimal digits,
+apart from an optional leading minus,
+and leading and trailing white space (which is ignored).
+
+After expansion, <&'string1'&> is interpreted as a list, colon-separated by
+default, but the separator can be changed in the usual way.
+
+The first field of the list is numbered one.
+If the number is negative, the fields are
+counted from the end of the list, with the rightmost one numbered -1.
+The numbered element of the list is extracted and placed in &$value$&,
+then <&'string2'&> is expanded as the result.
+
+If the modulus of the
+number is zero or greater than the number of fields in the string,
+the result is the expansion of <&'string3'&>.
+
+For example:
+.code
+${listextract{2}{x:42:99}}
+.endd
+yields &"42"&, and
+.code
+${listextract{-3}{<, x,42,99,& Mailer,,/bin/bash}{result: $value}}
+.endd
+yields &"result: 99"&.
+
+If {<&'string3'&>} is omitted, an empty string is used for string3.
+If {<&'string2'&>} is also omitted, the value that was
+extracted is used.
+You can use &`fail`& instead of {<&'string3'&>} as in a string extract.
+
+
 .vitem "&*${lookup{*&<&'key'&>&*}&~*&<&'search&~type'&>&*&~&&&
         {*&<&'file'&>&*}&~{*&<&'string1'&>&*}&~{*&<&'string2'&>&*}}*&"
 This is the first of one of two different types of lookup item, which are both
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 12369e4..e2b33cf 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -27,6 +27,8 @@ TL/02 Experimental Proxy Protocol support: allows a proxied SMTP connection
       to extract and use the src ip:port in logging and expansions as if it
       were a direct connection from the outside internet.


+JH/02 Add ${listextract {number}{list}{success}{fail}}.
+

Exim version 4.82
-----------------
diff --git a/src/src/expand.c b/src/src/expand.c
index bf1f82c..325b051 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -110,6 +110,7 @@ static uschar *item_table[] = {
US"hmac",
US"if",
US"length",
+ US"listextract",
US"lookup",
US"map",
US"nhash",
@@ -133,6 +134,7 @@ enum {
EITEM_HMAC,
EITEM_IF,
EITEM_LENGTH,
+ EITEM_LISTEXTRACT,
EITEM_LOOKUP,
EITEM_MAP,
EITEM_NHASH,
@@ -1131,6 +1133,22 @@ return fieldtext;
}


+static uschar *
+expand_getlistele (int field, uschar *list)
+{
+uschar * tlist= list;
+int sep= 0;
+uschar dummy;
+
+if(field<0)
+{
+ for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++;
+ sep= 0;
+}
+if(field==0) return NULL;
+while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ;
+return string_nextinlist(&list, &sep, NULL, 0);
+}

 /*************************************************
 *        Extract a substring from a string       *
@@ -5149,7 +5167,7 @@ while (*s != 0)
       for (i = 0; i < j; i++)
         {
         while (isspace(*s)) s++;
-        if (*s == '{')
+        if (*s == '{')                         /*}*/
           {
           sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
           if (sub[i] == NULL) goto EXPAND_FAILED;        /*{*/
@@ -5230,6 +5248,99 @@ while (*s != 0)
       continue;
       }


+    /* return the Nth item from a list */
+
+    case EITEM_LISTEXTRACT:
+      {
+      int i;
+      int field_number = 1;
+      uschar *save_lookup_value = lookup_value;
+      uschar *sub[2];
+      int save_expand_nmax =
+        save_expand_strings(save_expand_nstring, save_expand_nlength);
+
+      /* Read the field & list arguments */
+
+      for (i = 0; i < 2; i++)
+        {
+        while (isspace(*s)) s++;
+        if (*s != '{')                    /*}*/
+      goto EXPAND_FAILED_CURLY;
+
+    sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+    if (!sub[i])     goto EXPAND_FAILED;        /*{*/
+    if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+    /* After removal of leading and trailing white space, the first
+    argument must be numeric and nonempty. */
+
+    if (i == 0)
+      {
+      int len;
+      int x = 0;
+      uschar *p = sub[0];
+
+      while (isspace(*p)) p++;
+      sub[0] = p;
+
+      len = Ustrlen(p);
+      while (len > 0 && isspace(p[len-1])) len--;
+      p[len] = 0;
+
+      if (!*p && !skipping)
+        {
+        expand_string_message = US"first argument of \"listextract\" must "
+          "not be empty";
+        goto EXPAND_FAILED;
+        }
+
+      if (*p == '-')
+        {
+        field_number = -1;
+        p++;
+        }
+      while (*p && isdigit(*p)) x = x * 10 + *p++ - '0';
+      if (*p)
+        {
+        expand_string_message = US"first argument of \"listextract\" must "
+          "be numeric";
+        goto EXPAND_FAILED;
+        }
+      field_number *= x;
+      }
+        }
+
+      /* Extract the numbered element into $value. If
+      skipping, just pretend the extraction failed. */
+
+      lookup_value = skipping? NULL : expand_getlistele(field_number, sub[1]);
+
+      /* If no string follows, $value gets substituted; otherwise there can
+      be yes/no strings, as for lookup or if. */
+
+      switch(process_yesno(
+               skipping,                     /* were previously skipping */
+               lookup_value != NULL,         /* success/failure indicator */
+               save_lookup_value,            /* value to reset for string2 */
+               &s,                           /* input pointer */
+               &yield,                       /* output pointer */
+               &size,                        /* output size */
+               &ptr,                         /* output current point */
+               US"extract",                  /* condition type */
+           &resetok))
+        {
+        case 1: goto EXPAND_FAILED;          /* when all is well, the */
+        case 2: goto EXPAND_FAILED_CURLY;    /* returned value is 0 */
+        }
+
+      /* All done - restore numerical variables. */
+
+      restore_expand_strings(save_expand_nmax, save_expand_nstring,
+        save_expand_nlength);
+
+      continue;
+      }
+
     /* Handle list operations */


     case EITEM_FILTER:
diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002
index b924a09..367d558 100644
--- a/test/scripts/0000-Basic/0002
+++ b/test/scripts/0000-Basic/0002
@@ -75,6 +75,13 @@ listcount: ${listcount:}
 listcount: ${listcount:<;a;b;c}
 listcount: ${listcount:${listnamed:dlist}}


+listextract: ${listextract{ 2}{a:b:c:d}}
+listextract: ${listextract{-2}{<,a,b,c,d}{X${value}X}}
+listextract: ${listextract{ 5}{a:b:c:d}}
+listextract: ${listextract{-5}{a:b:c:d}}
+listextract: ${listextract{ 5}{a:b:c:d}{}{fail}}
+listextract: ${listextract{ 5}{a:b:c:d}{}fail}
+
# Tests with iscntrl() and illegal separators

map: ${map{<\n a\n\nb\nc}{'$item'}}
diff --git a/test/stdout/0002 b/test/stdout/0002
index 1cf6a5b..e627097 100644
--- a/test/stdout/0002
+++ b/test/stdout/0002
@@ -64,6 +64,13 @@
> listcount: 3
> listcount: 2
>

+> listextract: b
+> listextract: XcX
+> listextract:
+> listextract:
+> listextract: fail
+> Failed: "extract" failed and "fail" requested
+>
> # Tests with iscntrl() and illegal separators
>
> map: 'a'