+
+
+static const uschar *
+Ustrnchr(const uschar * s, int c, unsigned * len)
+{
+unsigned siz = *len;
+while (siz)
+ {
+ if (!*s) return NULL;
+ if (*s == c)
+ {
+ *len = siz;
+ return s;
+ }
+ s++;
+ siz--;
+ }
+return NULL;
+}
+
+
+/************************************************
+* Add element to separated list *
+************************************************/
+/* This function is used to build a list, returning an allocated null-terminated
+growable string. The given element has any embedded separator characters
+doubled.
+
+Despite having the same growable-string interface as string_cat() the list is
+always returned null-terminated.
+
+Arguments:
+ list expanding-string for the list that is being built, or NULL
+ if this is a new list that has no contents yet
+ sep list separator character
+ ele new element to be appended to the list
+
+Returns: pointer to the start of the list, changed if copied for expansion.
+*/
+
+gstring *
+string_append_listele(gstring * list, uschar sep, const uschar * ele)
+{
+uschar * sp;
+
+if (list && list->ptr)
+ list = string_catn(list, &sep, 1);
+
+while((sp = Ustrchr(ele, sep)))
+ {
+ list = string_catn(list, ele, sp-ele+1);
+ list = string_catn(list, &sep, 1);
+ ele = sp+1;
+ }
+list = string_cat(list, ele);
+(void) string_from_gstring(list);
+return list;
+}
+
+
+gstring *
+string_append_listele_n(gstring * list, uschar sep, const uschar * ele,
+ unsigned len)
+{
+const uschar * sp;
+
+if (list && list->ptr)
+ list = string_catn(list, &sep, 1);
+
+while((sp = Ustrnchr(ele, sep, &len)))
+ {
+ list = string_catn(list, ele, sp-ele+1);
+ list = string_catn(list, &sep, 1);
+ ele = sp+1;
+ len--;
+ }
+list = string_catn(list, ele, len);
+(void) string_from_gstring(list);
+return list;
+}
+
+
+
+/* A slightly-bogus listmaker utility; the separator is a string so
+can be multiple chars - there is no checking for the element content
+containing any of the separator. */
+
+gstring *
+string_append2_listele_n(gstring * list, const uschar * sepstr,
+ const uschar * ele, unsigned len)
+{
+if (list && list->ptr)
+ list = string_cat(list, sepstr);
+
+list = string_catn(list, ele, len);
+(void) string_from_gstring(list);
+return list;
+}
+
+
+
+/************************************************/
+/* Create a growable-string with some preassigned space */
+
+gstring *
+string_get(unsigned size)
+{
+gstring * g = store_get(sizeof(gstring) + size);
+g->size = size;
+g->ptr = 0;
+g->s = US(g + 1);
+return g;
+}
+
+/* NUL-terminate the C string in the growable-string, and return it. */
+
+uschar *
+string_from_gstring(gstring * g)
+{
+if (!g) return NULL;
+g->s[g->ptr] = '\0';
+return g->s;
+}
+
+void
+gstring_reset_unused(gstring * g)
+{
+store_reset(g->s + (g->size = g->ptr + 1));
+}
+
+
+/* Add more space to a growable-string.
+
+Arguments:
+ g the growable-string
+ p current end of data
+ count amount to grow by
+*/
+
+static void
+gstring_grow(gstring * g, int p, int count)
+{
+int oldsize = g->size;
+
+/* Mostly, string_cat() is used to build small strings of a few hundred
+characters at most. There are times, however, when the strings are very much
+longer (for example, a lookup that returns a vast number of alias addresses).
+To try to keep things reasonable, we use increments whose size depends on the
+existing length of the string. */
+
+unsigned inc = oldsize < 4096 ? 127 : 1023;
+g->size = ((p + count + inc) & ~inc) + 1;
+
+/* Try to extend an existing allocation. If the result of calling
+store_extend() is false, either there isn't room in the current memory block,
+or this string is not the top item on the dynamic store stack. We then have
+to get a new chunk of store and copy the old string. When building large
+strings, it is helpful to call store_release() on the old string, to release
+memory blocks that have become empty. (The block will be freed if the string
+is at its start.) However, we can do this only if we know that the old string
+was the last item on the dynamic memory stack. This is the case if it matches
+store_last_get. */
+
+if (!store_extend(g->s, oldsize, g->size))
+ g->s = store_newblock(g->s, g->size, p);
+}