X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fstring.c;h=5a8d0e76312950d819594374f3a439f3c39aa364;hp=0d5a09703452a764d4dade82d82108d39bc70659;hb=bce15b62182d356f86e7a0bdbb513cbb22de1a20;hpb=42055a338593d66f0abb6eeb6b03f0eaf4439f57 diff --git a/src/src/string.c b/src/src/string.c index 0d5a09703..5a8d0e763 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Miscellaneous string-handling functions. Some are not required for @@ -914,7 +914,7 @@ sep_is_special = iscntrl(sep); /* Handle the case when a buffer is provided. */ -if (buffer != NULL) +if (buffer) { int p = 0; for (; *s != 0; s++) @@ -930,9 +930,8 @@ if (buffer != NULL) else { - int size = 0; - int ptr = 0; const uschar *ss; + gstring * g = NULL; /* We know that *s != 0 at this point. However, it might be pointing to a separator, which could indicate an empty string, or (if an ispunct() @@ -954,13 +953,14 @@ else for (;;) { - for (ss = s + 1; *ss != 0 && *ss != sep; ss++); - buffer = string_catn(buffer, &size, &ptr, s, ss-s); + for (ss = s + 1; *ss != 0 && *ss != sep; ss++) ; + g = string_catn(g, 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; + while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; + buffer = string_from_gstring(g); + gstring_reset_unused(g); } /* Update the current pointer and return the new string */ @@ -968,20 +968,39 @@ else *listptr = s; return buffer; } -#endif /* COMPILE_UTILITY */ -#ifndef COMPILE_UTILITY +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 +/* 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 points to the start of the list that is being built, or NULL + 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 @@ -989,83 +1008,138 @@ Arguments: Returns: pointer to the start of the list, changed if copied for expansion. */ -uschar * -string_append_listele(uschar * list, uschar sep, const uschar * ele) +gstring * +string_append_listele(gstring * list, uschar sep, const uschar * ele) { -uschar * new = NULL; -int sz = 0, off = 0; uschar * sp; -if (list) - { - new = string_cat (new, &sz, &off, list); - new = string_catn(new, &sz, &off, &sep, 1); - } +if (list && list->ptr) + list = string_catn(list, &sep, 1); while((sp = Ustrchr(ele, sep))) { - new = string_catn(new, &sz, &off, ele, sp-ele+1); - new = string_catn(new, &sz, &off, &sep, 1); + list = string_catn(list, ele, sp-ele+1); + list = string_catn(list, &sep, 1); ele = sp+1; } -new = string_cat(new, &sz, &off, ele); -new[off] = '\0'; -return new; +list = string_cat(list, ele); +(void) string_from_gstring(list); +return list; } -static const uschar * -Ustrnchr(const uschar * s, int c, unsigned * len) +gstring * +string_append_listele_n(gstring * list, uschar sep, const uschar * ele, + unsigned len) { -unsigned siz = *len; -while (siz) - { - if (!*s) return NULL; - if (*s == c) - { - *len = siz; - return s; - } - s++; - siz--; - } -return NULL; -} - -uschar * -string_append_listele_n(uschar * list, uschar sep, const uschar * ele, - unsigned len) -{ -uschar * new = NULL; -int sz = 0, off = 0; const uschar * sp; -if (list) - { - new = string_cat (new, &sz, &off, list); - new = string_catn(new, &sz, &off, &sep, 1); - } +if (list && list->ptr) + list = string_catn(list, &sep, 1); while((sp = Ustrnchr(ele, sep, &len))) { - new = string_catn(new, &sz, &off, ele, sp-ele+1); - new = string_catn(new, &sz, &off, &sep, 1); + list = string_catn(list, ele, sp-ele+1); + list = string_catn(list, &sep, 1); ele = sp+1; len--; } -new = string_catn(new, &sz, &off, ele, len); -new[off] = '\0'; -return new; +list = string_catn(list, ele, len); +(void) string_from_gstring(list); +return list; } -#endif /* COMPILE_UTILITY */ -#ifndef COMPILE_UTILITY +/* 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) +{ +const uschar * sp; + +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 chars to string * *************************************************/ +/* Arguments: + g the grawable-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); +} + + + /* This function is used when building up strings of unknown length. Room is always left for a terminating zero to be added to the string that is being built. This function does not require the string that is being added to be NUL @@ -1075,16 +1149,10 @@ sometimes called to extract parts of other strings. Arguments: string points to the start of the string that is being built, or NULL if this is a new string that has no contents yet - size points to a variable that holds the current capacity of the memory - block (updated if changed) - ptr points to a variable that holds the offset at which to add - characters, updated to the new offset s points to characters to add count count of characters to add; must not exceed the length of s, if s is a C string. -If string is given as NULL, *size and *ptr should both be zero. - Returns: pointer to the start of the string, changed if copied for expansion. Note that a NUL is not added, though space is left for one. This is because string_cat() is often called multiple times to build up a @@ -1093,74 +1161,40 @@ Returns: pointer to the start of the string, changed if copied for expansion. */ /* coverity[+alloc] */ -uschar * -string_catn(uschar *string, int *size, int *ptr, const uschar *s, int count) +gstring * +string_catn(gstring * g, const uschar *s, int count) { -int p = *ptr; +int p; -if (p + count >= *size) +if (!g) { - int oldsize = *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. */ - - int inc = (oldsize < 4096)? 100 : 1024; - while (*size <= p + count) *size += inc; - - /* New string */ - - if (string == NULL) string = store_get(*size); - - /* 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. */ - - else if (!store_extend(string, oldsize, *size)) - { - BOOL release_ok = store_last_get[store_pool] == string; - uschar *newstring = store_get(*size); - memcpy(newstring, string, p); - if (release_ok) store_release(string); - string = newstring; - } + unsigned inc = count < 4096 ? 127 : 1023; + unsigned size = ((count + inc) & ~inc) + 1; + g = string_get(size); } +p = g->ptr; +if (p + count >= g->size) + gstring_grow(g, p, count); + /* Because we always specify the exact number of characters to copy, we can use memcpy(), which is likely to be more efficient than strncopy() because the -latter has to check for zero bytes. +latter has to check for zero bytes. */ -The Coverity annotation deals with the lack of correlated variable tracking; -common use is a null string and zero size and pointer, on first use for a -string being built. The "if" above then allocates, but Coverity assume that -the "if" might not happen and whines for a null-deref done by the memcpy(). */ - -/* coverity[deref_parm_field_in_call] : FALSE */ -memcpy(string + p, s, count); -*ptr = p + count; -return string; +memcpy(g->s + p, s, count); +g->ptr = p + count; +return g; } - - -uschar * -string_cat(uschar *string, int *size, int *ptr, const uschar *s) + + +gstring * +string_cat(gstring *string, const uschar *s) { -return string_catn(string, size, ptr, s, Ustrlen(s)); +return string_catn(string, s, Ustrlen(s)); } -#endif /* COMPILE_UTILITY */ -#ifndef COMPILE_UTILITY /************************************************* * Append strings to another string * *************************************************/ @@ -1169,12 +1203,8 @@ return string_catn(string, size, ptr, s, Ustrlen(s)); It calls string_cat() to do the dirty work. Arguments: - string points to the start of the string that is being built, or NULL + string expanding-string that is being built, or NULL if this is a new string that has no contents yet - size points to a variable that holds the current capacity of the memory - block (updated if changed) - ptr points to a variable that holds the offset at which to add - characters, updated to the new offset count the number of strings to append ... "count" uschar* arguments, which must be valid zero-terminated C strings @@ -1183,17 +1213,16 @@ Returns: pointer to the start of the string, changed if copied for expansion. The string is not zero-terminated - see string_cat() above. */ -uschar * -string_append(uschar *string, int *size, int *ptr, int count, ...) +__inline__ gstring * +string_append(gstring *string, int count, ...) { va_list ap; -int i; va_start(ap, count); -for (i = 0; i < count; i++) +while (count-- > 0) { uschar *t = va_arg(ap, uschar *); - string = string_cat(string, size, ptr, t); + string = string_cat(string, t); } va_end(ap); @@ -1216,7 +1245,7 @@ as a va_list item. The formats are the usual printf() ones, with some omissions (never used) and three additions for strings: %S forces lower case, %T forces upper case, and -%#s or %#S prints nothing for a NULL string. Without thr # "NULL" is printed +%#s or %#S prints nothing for a NULL string. Without the # "NULL" is printed (useful in debugging). There is also the addition of %D and %M, which insert the date in the form used for datestamped log files. @@ -1362,20 +1391,28 @@ while (*fp != 0) switch(length) { case L_SHORT: - case L_NORMAL: sprintf(CS p, newformat, va_arg(ap, int)); break; - case L_LONG: sprintf(CS p, newformat, va_arg(ap, long int)); break; - case L_LONGLONG: sprintf(CS p, newformat, va_arg(ap, LONGLONG_T)); break; - case L_SIZE: sprintf(CS p, newformat, va_arg(ap, size_t)); break; + case L_NORMAL: p += sprintf(CS p, newformat, va_arg(ap, int)); break; + case L_LONG: p += sprintf(CS p, newformat, va_arg(ap, long int)); break; + case L_LONGLONG: p += sprintf(CS p, newformat, va_arg(ap, LONGLONG_T)); break; + case L_SIZE: p += sprintf(CS p, newformat, va_arg(ap, size_t)); break; } - while (*p) p++; break; case 'p': - if (p >= last - 24) { yield = FALSE; goto END_FORMAT; } - strncpy(newformat, item_start, fp - item_start); - newformat[fp - item_start] = 0; - sprintf(CS p, newformat, va_arg(ap, void *)); - while (*p) p++; + { + void * ptr; + if (p >= last - 24) { yield = FALSE; goto END_FORMAT; } + /* sprintf() saying "(nil)" for a null pointer seems unreliable. + Handle it explicitly. */ + if ((ptr = va_arg(ap, void *))) + { + strncpy(newformat, item_start, fp - item_start); + newformat[fp - item_start] = 0; + p += sprintf(CS p, newformat, ptr); + } + else + p += sprintf(CS p, "(nil)"); + } break; /* %f format is inherently insecure if the numbers that it may be @@ -1395,10 +1432,9 @@ while (*fp != 0) strncpy(newformat, item_start, fp - item_start); newformat[fp-item_start] = 0; if (length == L_LONGDOUBLE) - sprintf(CS p, newformat, va_arg(ap, long double)); + p += sprintf(CS p, newformat, va_arg(ap, long double)); else - sprintf(CS p, newformat, va_arg(ap, double)); - while (*p) p++; + p += sprintf(CS p, newformat, va_arg(ap, double)); break; /* String types */