summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2012-06-12 21:43:58 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2012-06-12 21:43:58 +0100
commit585121e2682545b7afa599e039a7a1e2b1804570 (patch)
treeb4074602a1cc3b876fee896d6d5446e4548d51a6
parentd7148a072a5fee80ffa8d1aeebf9bc05d0e903b4 (diff)
parenta64a3dfa73b829669d838c8d129b0974360b95a5 (diff)
Merge branch 'lists'
-rw-r--r--doc/doc-docbook/spec.xfpt20
-rw-r--r--doc/doc-txt/ChangeLog2
-rw-r--r--doc/doc-txt/NewStuff4
-rw-r--r--src/src/expand.c105
-rw-r--r--test/confs/00022
-rw-r--r--test/scripts/0000-Basic/000215
-rw-r--r--test/stdout/000215
7 files changed, 162 insertions, 1 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 0cdc98657..dcf6b6cfb 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -9567,6 +9567,7 @@ environments where Exim uses base 36 instead of base 62 for its message
identifiers, base-36 digits. The number is converted to decimal and output as a
string.
+
.vitem &*${domain:*&<&'string'&>&*}*&
.cindex "domain" "extraction"
.cindex "expansion" "domain extraction"
@@ -9726,6 +9727,25 @@ See the description of the general &%length%& item above for details. Note that
when &%length%& is used as an operator.
+.vitem &*${listcount:*&<&'string'&>&*}*&
+.cindex "expansion" "list item count"
+.cindex "list" "item count"
+.cindex "list" "count of items"
+.cindex "&%listcount%& expansion item"
+The string is interpreted as a list and the number of items is returned.
+
+
+.vitem &*${listnamed:*&<&'name'&>&*}*&&~and&~&*${list_*&<&'type'&>&*name'&>&*}*&
+.cindex "expansion" "named list"
+.cindex "&%listnamed%& expansion item"
+The name is interpreted as a named list and the content of the list is returned,
+expanding any referenced lists, re-quoting as needed for colon-separation.
+If the optional type if given it must be one of "a", "d", "h" or "l"
+and selects address-, domain-, host- or localpart- lists to search among respectively.
+Otherwise all types are searched in an undefined order and the first
+matching list is returned.
+
+
.vitem &*${local_part:*&<&'string'&>&*}*&
.cindex "expansion" "local part extraction"
.cindex "&%local_part%& expansion item"
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index bd0628637..34521098e 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -42,6 +42,8 @@ PP/08 Handle smtp transport tls_sni option forced-fail for OpenSSL.
NM/01 Bugzilla 1197 - Spec typo
Bugzilla 1196 - Spec examples corrections
+JH/03 Add expansion operators ${listnamed:name} and ${listcount:string}
+
Exim version 4.80
-----------------
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index aae58c631..6d64faa00 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -70,7 +70,7 @@ Version 4.81
system not your own.
The Recieved-by: header on items delivered by cutthrough is generated
- early in of reception rather than at the end; this will affect any timestamp
+ early in reception rather than at the end; this will affect any timestamp
included. The log line showing delivery is recorded before that showing
reception; it uses a new ">>" tag instead of "=>".
@@ -84,6 +84,8 @@ Version 4.81
Not yet supported: IGNOREQUOTA, SIZE, PIPELINING, AUTH.
+ 8. New expansion operators ${listnamed:name} to get the content of a named list
+ and ${listcount:string} to count the items in a list.
Version 4.80
------------
diff --git a/src/src/expand.c b/src/src/expand.c
index 05361a3ef..965842611 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -182,6 +182,8 @@ static uschar *op_table_main[] = {
US"l",
US"lc",
US"length",
+ US"listcount",
+ US"listnamed",
US"mask",
US"md5",
US"nh",
@@ -215,6 +217,8 @@ enum {
EOP_L,
EOP_LC,
EOP_LENGTH,
+ EOP_LISTCOUNT,
+ EOP_LISTNAMED,
EOP_MASK,
EOP_MD5,
EOP_NH,
@@ -5470,6 +5474,107 @@ while (*s != 0)
continue;
}
+ /* count the number of list elements */
+
+ case EOP_LISTCOUNT:
+ {
+ int cnt = 0;
+ int sep = 0;
+ uschar * cp;
+ uschar buffer[256];
+
+ while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
+ cp = string_sprintf("%d", cnt);
+ yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
+ continue;
+ }
+
+ /* expand a named list given the name */
+ /* handles nested named lists; requotes as colon-sep list */
+
+ case EOP_LISTNAMED:
+ {
+ tree_node *t = NULL;
+ uschar * list;
+ int sep = 0;
+ uschar * item;
+ uschar * suffix = "";
+ BOOL needsep = FALSE;
+ uschar buffer[256];
+
+ if (*sub == '+') sub++;
+ if (arg == NULL) /* no-argument version */
+ {
+ if (!(t = tree_search(addresslist_anchor, sub)) &&
+ !(t = tree_search(domainlist_anchor, sub)) &&
+ !(t = tree_search(hostlist_anchor, sub)))
+ t = tree_search(localpartlist_anchor, sub);
+ }
+ else switch(*arg) /* specific list-type version */
+ {
+ case 'a': t = tree_search(addresslist_anchor, sub); suffix = "_a"; break;
+ case 'd': t = tree_search(domainlist_anchor, sub); suffix = "_d"; break;
+ case 'h': t = tree_search(hostlist_anchor, sub); suffix = "_h"; break;
+ case 'l': t = tree_search(localpartlist_anchor, sub); suffix = "_l"; break;
+ default:
+ expand_string_message = string_sprintf("bad suffix on \"list\" operator");
+ goto EXPAND_FAILED;
+ }
+
+ if(!t)
+ {
+ expand_string_message = string_sprintf("\"%s\" is not a %snamed list",
+ sub, !arg?""
+ : *arg=='a'?"address "
+ : *arg=='d'?"domain "
+ : *arg=='h'?"host "
+ : *arg=='l'?"localpart "
+ : 0);
+ goto EXPAND_FAILED;
+ }
+
+ if (skipping) continue;
+ list = ((namedlist_block *)(t->data.ptr))->string;
+
+ while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+ {
+ uschar * buf = US" : ";
+ if (needsep)
+ yield = string_cat(yield, &size, &ptr, buf, 3);
+ else
+ needsep = TRUE;
+
+ if (*item == '+') /* list item is itself a named list */
+ {
+ uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item);
+ item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE);
+ }
+ else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */
+ {
+ char * cp;
+ char tok[3];
+ tok[0] = sep; tok[1] = ':'; tok[2] = 0;
+ while ((cp= strpbrk((const char *)item, tok)))
+ {
+ yield = string_cat(yield, &size, &ptr, item, cp-(char *)item);
+ if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */
+ {
+ yield = string_cat(yield, &size, &ptr, US"::", 2);
+ item = cp;
+ }
+ else /* sep in item; should already be doubled; emit once */
+ {
+ yield = string_cat(yield, &size, &ptr, (uschar *)tok, 1);
+ if (*cp == sep) cp++;
+ item = cp;
+ }
+ }
+ }
+ yield = string_cat(yield, &size, &ptr, item, Ustrlen(item));
+ }
+ continue;
+ }
+
/* mask applies a mask to an IP address; for example the result of
${mask:131.111.10.206/28} is 131.111.10.192/28. */
diff --git a/test/confs/0002 b/test/confs/0002
index af680500c..6983fd87f 100644
--- a/test/confs/0002
+++ b/test/confs/0002
@@ -15,6 +15,8 @@ gecos_name = CALLER_NAME
# ----- Main settings -----
domainlist dlist = *.aa.bb : ^\Nxxx(.*)
+domainlist elist = +dlist : ;;
+domainlist flist = <; a ; b;;c ; +elist ; 2001:630:212:8:204::b664 ;
hostlist hlist = V4NET.11.12.13 : iplsearch;DIR/aux-fixed/0002.iplsearch
headers_charset = iso-8859-8
diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002
index 3b485ee97..652891615 100644
--- a/test/scripts/0000-Basic/0002
+++ b/test/scripts/0000-Basic/0002
@@ -60,6 +60,21 @@ reduce: ${reduce{a:b:c}{+}{$value$item}}
reduce: ${reduce {<, 1,2,3}{0}{${eval:$value+$item}}}
reduce: ${reduce {3:0:9:4:6}{0}{${if >{$item}{$value}{$item}{$value}}}}
+listnamed: ${listnamed:dlist}
+listnamed: ${listnamed:+dlist}
+listnamed: ${listnamed:hlist}
+listnamed: ${listnamed:elist}
+listnamed: ${listnamed:flist}
+listnamed: ${listnamed:nolist}
+listnamed: ${listnamed_d:dlist}
+listnamed: ${listnamed_d:hlist}
+listnamed: ${listnamed_z:dlist}
+
+listcount: ${listcount:a:b:c}
+listcount: ${listcount:}
+listcount: ${listcount:<;a;b;c}
+listcount: ${listcount:${listnamed:dlist}}
+
# 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 2acfb63ea..de67f99fc 100644
--- a/test/stdout/0002
+++ b/test/stdout/0002
@@ -49,6 +49,21 @@
> reduce: 6
> reduce: 9
>
+> listnamed: *.aa.bb : ^\Nxxx(.*)
+> listnamed: *.aa.bb : ^\Nxxx(.*)
+> listnamed: V4NET.11.12.13 : iplsearch;TESTSUITE/aux-fixed/0002.iplsearch
+> listnamed: *.aa.bb : ^\Nxxx(.*) : ;;
+> listnamed: a : b;c : *.aa.bb : ^\Nxxx(.*) : ;; : 2001::630::212::8::204::::b664
+> Failed: "nolist" is not a named list
+> listnamed: *.aa.bb : ^\Nxxx(.*)
+> Failed: "hlist" is not a domain named list
+> Failed: bad suffix on "list" operator
+>
+> listcount: 3
+> listcount: 0
+> listcount: 3
+> listcount: 2
+>
> # Tests with iscntrl() and illegal separators
>
> map: 'a'