X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4fab92fbc2b63bac2d89c1dae69fa1845cb640b7..25beaee4c28ed29a37078e6c68fa3d18ced6ec8c:/src/src/string.c diff --git a/src/src/string.c b/src/src/string.c index be1a1d7a4..3abe2a3bd 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 @@ -42,7 +42,7 @@ int yield = 4; /* If an optional mask is permitted, check for it. If found, pass back the offset. */ -if (maskptr != NULL) +if (maskptr) { const uschar *ss = s + Ustrlen(s); *maskptr = 0; @@ -79,7 +79,7 @@ if (Ustrchr(s, ':') != NULL) if we hit the / that introduces a mask or the % that introduces the interface specifier (scope id) of a link-local address. */ - if (*s == 0 || *s == '%' || *s == '/') return had_double_colon? yield : 0; + if (*s == 0 || *s == '%' || *s == '/') return had_double_colon ? yield : 0; /* If a component starts with an additional colon, we have hit a double colon. This is permitted to appear once only, and counts as at least @@ -135,13 +135,16 @@ if (Ustrchr(s, ':') != NULL) for (i = 0; i < 4; i++) { + long n; + uschar * end; + if (i != 0 && *s++ != '.') return 0; - if (!isdigit(*s++)) return 0; - if (isdigit(*s) && isdigit(*(++s))) s++; + n = strtol(CCS s, CSS &end, 10); + if (n > 255 || n < 0 || end <= s || end > s+3) return 0; + s = end; } -return (*s == 0 || (*s == '/' && maskptr != NULL && *maskptr != 0))? - yield : 0; +return !*s || (*s == '/' && maskptr && *maskptr != 0) ? yield : 0; } #endif /* COMPILE_UTILITY */ @@ -306,7 +309,7 @@ expanded string. */ ss = store_get(length + nonprintcount * 3 + 1); -/* Copy everying, escaping non printers. */ +/* Copy everything, escaping non printers. */ t = s; tt = ss; @@ -911,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++) @@ -927,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() @@ -951,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 */ @@ -965,104 +968,176 @@ 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 seperator 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 seperator charactoer - ele new lement to be appended to the list + 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. */ -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) +{ +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 @@ -1072,15 +1147,9 @@ 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 -1 given, strlen(s) is used. - -If string is given as NULL, *size and *ptr should both be zero. + is a C string. 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 @@ -1090,74 +1159,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 * *************************************************/ @@ -1166,12 +1201,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 @@ -1180,17 +1211,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); @@ -1213,7 +1243,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. @@ -1359,20 +1389,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 @@ -1392,10 +1430,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 */