From 042eb971b8c1af6d257f207d453b30afeed529b8 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 10 Jun 2012 17:53:01 +0100 Subject: Add ${list:name} and ${nlist:string} expansion operators. --- src/src/expand.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) (limited to 'src') diff --git a/src/src/expand.c b/src/src/expand.c index 05361a3ef..2a9e6b4fc 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -182,10 +182,12 @@ static uschar *op_table_main[] = { US"l", US"lc", US"length", + US"list", US"mask", US"md5", US"nh", US"nhash", + US"nlist", US"quote", US"randint", US"rfc2047", @@ -215,10 +217,12 @@ enum { EOP_L, EOP_LC, EOP_LENGTH, + EOP_LIST, EOP_MASK, EOP_MD5, EOP_NH, EOP_NHASH, + EOP_NLIST, EOP_QUOTE, EOP_RANDINT, EOP_RFC2047, @@ -5470,6 +5474,107 @@ while (*s != 0) continue; } + /* expand a named list given the name */ + /* handles nested named lists but will confuse the separators in the result */ + + case EOP_LIST: + { + 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("${list%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; + } + + /* count the number of list elements */ + + case EOP_NLIST: + { + 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; + } + /* 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. */ -- cgit v1.2.3 From a64a3dfa73b829669d838c8d129b0974360b95a5 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 12 Jun 2012 21:41:05 +0100 Subject: Change names to "listnamed" and "listcount". --- doc/doc-docbook/spec.xfpt | 29 +++++++++++++++-------------- doc/doc-txt/ChangeLog | 2 +- doc/doc-txt/NewStuff | 4 ++-- src/src/expand.c | 44 ++++++++++++++++++++++---------------------- test/scripts/0000-Basic/0002 | 28 ++++++++++++++-------------- test/stdout/0002 | 20 ++++++++++---------- 6 files changed, 64 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 7c0a400d2..675d3f956 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,17 +9727,17 @@ See the description of the general &%length%& item above for details. Note that when &%length%& is used as an operator. -.vitem &*${local_part:*&<&'string'&>&*}*& -.cindex "expansion" "local part extraction" -.cindex "&%local_part%& expansion item" -The string is interpreted as an RFC 2822 address and the local part is -extracted from it. If the string does not parse successfully, the result is -empty. +.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 &*${list:*&<&'name'&>&*}*&&~and&~&*${list_*&<&'type'&>&*name'&>&*}*& +.vitem &*${listnamed:*&<&'name'&>&*}*&&~and&~&*${list_*&<&'type'&>&*name'&>&*}*& .cindex "expansion" "named list" -.cindex "&%list%& expansion item" +.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" @@ -9745,12 +9746,12 @@ Otherwise all types are searched in an undefined order and the first matching list is returned. -.vitem &*${nlist:*&<&'string'&>&*}*& -.cindex "expansion" "list item count" -.cindex "list" "item count" -.cindex "list" "count of items" -.cindex "&%nlist%& expansion item" -The string is interpreted as a list and the number of items is returned. +.vitem &*${local_part:*&<&'string'&>&*}*& +.cindex "expansion" "local part extraction" +.cindex "&%local_part%& expansion item" +The string is interpreted as an RFC 2822 address and the local part is +extracted from it. If the string does not parse successfully, the result is +empty. .vitem &*${mask:*&<&'IP&~address'&>&*/*&<&'bit&~count'&>&*}*& diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index af6080985..1bf2cd06b 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -39,7 +39,7 @@ JH/02 Support "G" suffix to numbers in ${if comparisons. PP/08 Handle smtp transport tls_sni option forced-fail for OpenSSL. -JH/03 Add expansion operators ${list:name} and ${nlist:string} +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 64c1c14b7..6d64faa00 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -84,8 +84,8 @@ Version 4.81 Not yet supported: IGNOREQUOTA, SIZE, PIPELINING, AUTH. - 8. New expansion operators ${list:name} to get the content of a named list - and ${nlist:string} to count the items in a list. + 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 2a9e6b4fc..965842611 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -182,12 +182,12 @@ static uschar *op_table_main[] = { US"l", US"lc", US"length", - US"list", + US"listcount", + US"listnamed", US"mask", US"md5", US"nh", US"nhash", - US"nlist", US"quote", US"randint", US"rfc2047", @@ -217,12 +217,12 @@ enum { EOP_L, EOP_LC, EOP_LENGTH, - EOP_LIST, + EOP_LISTCOUNT, + EOP_LISTNAMED, EOP_MASK, EOP_MD5, EOP_NH, EOP_NHASH, - EOP_NLIST, EOP_QUOTE, EOP_RANDINT, EOP_RFC2047, @@ -5474,10 +5474,25 @@ 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 but will confuse the separators in the result */ + /* handles nested named lists; requotes as colon-sep list */ - case EOP_LIST: + case EOP_LISTNAMED: { tree_node *t = NULL; uschar * list; @@ -5531,7 +5546,7 @@ while (*s != 0) if (*item == '+') /* list item is itself a named list */ { - uschar * sub = string_sprintf("${list%s:%s}", suffix, item); + 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 */ @@ -5560,21 +5575,6 @@ while (*s != 0) continue; } - /* count the number of list elements */ - - case EOP_NLIST: - { - 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; - } - /* 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/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index 41e0d0064..652891615 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -60,20 +60,20 @@ 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}}}} -list: ${list:dlist} -list: ${list:+dlist} -list: ${list:hlist} -list: ${list:elist} -list: ${list:flist} -list: ${list:nolist} -list: ${list_d:dlist} -list: ${list_d:hlist} -list: ${list_z:dlist} - -nlist: ${nlist:a:b:c} -nlist: ${nlist:} -nlist: ${nlist:<;a;b;c} -nlist: ${nlist:${list:dlist}} +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 diff --git a/test/stdout/0002 b/test/stdout/0002 index a7c876229..de67f99fc 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -49,20 +49,20 @@ > reduce: 6 > reduce: 9 > -> list: *.aa.bb : ^\Nxxx(.*) -> list: *.aa.bb : ^\Nxxx(.*) -> list: V4NET.11.12.13 : iplsearch;TESTSUITE/aux-fixed/0002.iplsearch -> list: *.aa.bb : ^\Nxxx(.*) : ;; -> list: a : b;c : *.aa.bb : ^\Nxxx(.*) : ;; : 2001::630::212::8::204::::b664 +> 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 -> list: *.aa.bb : ^\Nxxx(.*) +> listnamed: *.aa.bb : ^\Nxxx(.*) > Failed: "hlist" is not a domain named list > Failed: bad suffix on "list" operator > -> nlist: 3 -> nlist: 0 -> nlist: 3 -> nlist: 2 +> listcount: 3 +> listcount: 0 +> listcount: 3 +> listcount: 2 > > # Tests with iscntrl() and illegal separators > -- cgit v1.2.3