+ /* Convert octets outside 0x21..0x7E to \xXX form */
+
+ case EOP_HEXQUOTE:
+ {
+ uschar *t = sub - 1;
+ while (*(++t) != 0)
+ {
+ if (*t < 0x21 || 0x7E < *t)
+ yield = string_cat(yield, &size, &ptr,
+ string_sprintf("\\x%02x", *t), 4);
+ else
+ yield = string_cat(yield, &size, &ptr, t, 1);
+ }
+ 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 = US"";
+ 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 = US"_a"; break;
+ case 'd': t = tree_search(domainlist_anchor, sub); suffix = US"_d"; break;
+ case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break;
+ case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_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;
+ }
+
+ 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 = (uschar *)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 = (uschar *)cp;
+ }
+ }
+ }
+ yield = string_cat(yield, &size, &ptr, item, Ustrlen(item));
+ }
+ continue;
+ }
+