-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.142 2007/02/14 15:33:40 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.143 2007/02/26 14:07:04 ph10 Exp $
New Features in Exim
--------------------
continue = <some expansion>
condition = ${if eq{0}{<some expansion>}{true}{true}}
+18. It is now possible to use newline and other control characters (those with
+ values less than 32, plus DEL) as separators in lists. Such separators must
+ be provided literally at the time the list is processed, but the string
+ expansion that happens first means that you can write them using normal
+ escape sequences. For example, if a new-line separated list of domains is
+ generated by a lookup, you can now process it directly by a line such as
+ this:
+
+ domains = <\n ${lookup mysql{.....}}
+
+ This avoids having to change the list separator in such data. Unlike
+ printing character separators, which can be included in list items by
+ doubling, it is not possible to include a control character as data when it
+ is set as the separator. Two such characters in succession are interpreted
+ as enclosing an empty list item.
+
Version 4.66
------------
-/* $Cambridge: exim/src/src/string.c,v 1.12 2007/02/07 11:24:56 ph10 Exp $ */
+/* $Cambridge: exim/src/src/string.c,v 1.13 2007/02/26 14:07:04 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
/* Leading and trailing space is removed from each item. The separator in the
list is controlled by the int pointed to by the separator argument as follows:
- If its value is > 0 it is used as the delimiter.
- (If its value is actually > UCHAR_MAX there is only one item in the list.
+ If the value is > 0 it is used as the separator. This is typically used for
+ sublists such as slash-separated options. The value is always a printing
+ character.
+
+ (If the value is actually > UCHAR_MAX there is only one item in the list.
This is used for some cases when called via functions that sometimes
plough through lists, and sometimes are given single items.)
- If its value is <= 0, the string is inspected for a leading <x, where
- x is an ispunct() value. If found, it is used as the delimiter. If not
- found: (a) if separator == 0, ':' is used
- (b) if separator <0, then -separator is used
- In all cases the value of the separator that is used is written back to
- the int so that it is used on subsequent calls as we progress through
- the list.
-The separator can always be represented in the string by doubling.
+ If the value is <= 0, the string is inspected for a leading <x, where x is an
+ ispunct() or an iscntrl() character. If found, x is used as the separator. If
+ not found:
+
+ (a) if separator == 0, ':' is used
+ (b) if separator <0, -separator is used
+
+ In all cases the value of the separator that is used is written back to the
+ int so that it is used on subsequent calls as we progress through the list.
+
+A literal ispunct() separator can be represented in an item by doubling, but
+there is no way to include an iscntrl() separator as part of the data.
Arguments:
listptr points to a pointer to the current start of the list; the
uschar *
string_nextinlist(uschar **listptr, int *separator, uschar *buffer, int buflen)
{
-register int p = 0;
register int sep = *separator;
register uschar *s = *listptr;
+BOOL sep_is_special;
if (s == NULL) return NULL;
-while (isspace(*s)) s++;
+
+/* This allows for a fixed specified separator to be an iscntrl() character,
+but at the time of implementation, this is never the case. However, it's best
+to be conservative. */
+
+while (isspace(*s) && *s != sep) s++;
+
+/* A change of separator is permitted, so look for a leading '<' followed by an
+allowed character. */
if (sep <= 0)
{
- if (*s == '<' && ispunct(s[1]))
+ if (*s == '<' && (ispunct(s[1]) || iscntrl(s[1])))
{
sep = s[1];
s += 2;
- while (isspace(*s)) s++;
+ while (isspace(*s) && *s != sep) s++;
}
else
{
*separator = sep;
}
+/* An empty string has no list elements */
+
if (*s == 0) return NULL;
+/* Note whether whether or not the separator is an iscntrl() character. */
+
+sep_is_special = iscntrl(sep);
+
/* Handle the case when a buffer is provided. */
if (buffer != NULL)
{
+ register int p = 0;
for (; *s != 0; s++)
{
- if (*s == sep && *(++s) != sep) break;
+ if (*s == sep && (*(++s) != sep || sep_is_special)) break;
if (p < buflen - 1) buffer[p++] = *s;
}
while (p > 0 && isspace(buffer[p-1])) p--;
else
{
+ int size = 0;
+ int ptr = 0;
+ uschar *ss;
+
/* We know that *s != 0 at this point. However, it might be pointing to a
- separator, which could indicate an empty string, or could be doubled to
- indicate a separator character as data at the start of a string. */
+ separator, which could indicate an empty string, or (if an ispunct()
+ character) could be doubled to indicate a separator character as data at the
+ start of a string. Avoid getting working memory for an empty item. */
if (*s == sep)
{
s++;
- if (*s != sep) buffer = string_copy(US"");
+ if (*s != sep || sep_is_special)
+ {
+ *listptr = s;
+ return string_copy(US"");
+ }
}
- if (buffer == NULL)
+ /* Not an empty string; the first character is guaranteed to be a data
+ character. */
+
+ for (;;)
{
- int size = 0;
- int ptr = 0;
- uschar *ss;
- for (;;)
- {
- for (ss = s + 1; *ss != 0 && *ss != sep; ss++);
- buffer = string_cat(buffer, &size, &ptr, s, ss-s);
- s = ss;
- if (*s == 0 || *(++s) != sep) break;
- }
- while (ptr > 0 && isspace(buffer[ptr-1])) ptr--;
- buffer[ptr] = 0;
+ for (ss = s + 1; *ss != 0 && *ss != sep; ss++);
+ buffer = string_cat(buffer, &size, &ptr, s, ss-s);
+ s = ss;
+ if (*s == 0 || *(++s) != sep || sep_is_special) break;
}
+ while (ptr > 0 && isspace(buffer[ptr-1])) ptr--;
+ buffer[ptr] = 0;
}
/* Update the current pointer and return the new string */