constification
[exim.git] / src / src / string.c
index 4726fbe4e30e0de5b7fa5256dd4c10a6199d1dbd..1e7922457cee09a2b6e0f6f175600bf2552d517b 100644 (file)
@@ -3,7 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Miscellaneous string-handling functions. Some are not required for
@@ -298,7 +298,7 @@ int length = 0;
 const uschar *t = s;
 uschar *ss, *tt;
 
-while (*t != 0)
+while (*t)
   {
   int c = *t++;
   if (  !mac_isprint(c)
@@ -433,17 +433,13 @@ string_copy_function(const uschar *s)
 return string_copy_taint(s, is_tainted(s));
 }
 
-/* This function assumes that memcpy() is faster than strcpy().
-As above, but explicitly specifying the result taint status
+/* As above, but explicitly specifying the result taint status
 */
 
 uschar *
 string_copy_taint_function(const uschar * s, BOOL tainted)
 {
-int len = Ustrlen(s) + 1;
-uschar *ss = store_get(len, tainted);
-memcpy(ss, s, len);
-return ss;
+return string_copy_taint(s, tainted);
 }
 
 
@@ -463,12 +459,9 @@ Returns:    copy of string in new store
 */
 
 uschar *
-string_copyn_function(const uschar *s, int n)
+string_copyn_function(const uschar * s, int n)
 {
-uschar *ss = store_get(n + 1, is_tainted(s));
-Ustrncpy(ss, s, n);
-ss[n] = 0;
-return ss;
+return string_copyn(s, n);
 }
 #endif
 
@@ -484,10 +477,10 @@ Returns:  copy of string in new store
 */
 
 uschar *
-string_copy_malloc(const uschar *s)
+string_copy_malloc(const uschar * s)
 {
 int len = Ustrlen(s) + 1;
-uschar *ss = store_malloc(len);
+uschar * ss = store_malloc(len);
 memcpy(ss, s, len);
 return ss;
 }
@@ -506,37 +499,37 @@ Returns:   pointer to the possibly altered string
 */
 
 uschar *
-string_split_message(uschar *msg)
+string_split_message(uschar * msg)
 {
 uschar *s, *ss;
 
-if (msg == NULL || Ustrlen(msg) <= 75) return msg;
+if (!msg || Ustrlen(msg) <= 75) return msg;
 s = ss = msg = string_copy(msg);
 
 for (;;)
   {
   int i = 0;
-  while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++;
-  if (*ss == 0) break;
+  while (i < 75 && *ss && *ss != '\n') ss++, i++;
+  if (!*ss) break;
   if (*ss == '\n')
     s = ++ss;
   else
     {
-    uschar *t = ss + 1;
-    uschar *tt = NULL;
+    uschar * t = ss + 1;
+    uschar * tt = NULL;
     while (--t > s + 35)
       {
       if (*t == ' ')
         {
         if (t[-1] == ':') { tt = t; break; }
-        if (tt == NULL) tt = t;
+        if (!tt) tt = t;
         }
       }
 
-    if (tt == NULL)          /* Can't split behind - try ahead */
+    if (!tt)          /* Can't split behind - try ahead */
       {
       t = ss + 1;
-      while (*t != 0)
+      while (*t)
         {
         if (*t == ' ' || *t == '\n')
           { tt = t; break; }
@@ -544,7 +537,7 @@ for (;;)
         }
       }
 
-    if (tt == NULL) break;   /* Can't find anywhere to split */
+    if (!tt) break;   /* Can't find anywhere to split */
     *tt = '\n';
     s = ss = tt+1;
     }
@@ -572,12 +565,12 @@ Returns:    copy of string in new store, de-escaped
 */
 
 uschar *
-string_copy_dnsdomain(uschar *s)
+string_copy_dnsdomain(uschar * s)
 {
-uschar *yield;
-uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s));
+uschar * yield;
+uschar * ss = yield = store_get(Ustrlen(s) + 1, TRUE); /* always treat as tainted */
 
-while (*s != 0)
+while (*s)
   {
   if (*s != '\\')
     *ss++ = *s++;
@@ -586,7 +579,7 @@ while (*s != 0)
     *ss++ = (s[1] - '0')*100 + (s[2] - '0')*10 + s[3] - '0';
     s += 4;
     }
-  else if (*(++s) != 0)
+  else if (*++s)
     *ss++ = *s++;
   }
 
@@ -611,15 +604,15 @@ Returns:   the new string
 */
 
 uschar *
-string_dequote(const uschar **sptr)
+string_dequote(const uschar ** sptr)
 {
-const uschar *s = *sptr;
-uschar *t, *yield;
+const uschar * s = * sptr;
+uschar * t, * yield;
 
 /* First find the end of the string */
 
 if (*s != '\"')
-  while (*s != 0 && !isspace(*s)) s++;
+  while (*s && !isspace(*s)) s++;
 else
   {
   s++;
@@ -639,11 +632,11 @@ s = *sptr;
 /* Do the copy */
 
 if (*s != '\"')
-  while (*s != 0 && !isspace(*s)) *t++ = *s++;
+  while (*s && !isspace(*s)) *t++ = *s++;
 else
   {
   s++;
-  while (*s != 0 && *s != '\"')
+  while (*s && *s != '\"')
     {
     *t++ = *s == '\\' ? string_interpret_escape(&s) : *s;
     s++;
@@ -671,13 +664,15 @@ everything.  Taint is taken from the worst of the arguments.
 Arguments:
   format    a printf() format - deliberately char * rather than uschar *
               because it will most usually be a literal string
+  func     caller, for debug
+  line     caller, for debug
   ...       arguments for format
 
 Returns:    pointer to fresh piece of store containing sprintf'ed string
 */
 
 uschar *
-string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...)
+string_sprintf_trc(const char * format, const uschar * func, unsigned line, ...)
 {
 #ifdef COMPILE_UTILITY
 uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
@@ -725,7 +720,7 @@ Returns:    < 0, = 0, or > 0, according to the comparison
 */
 
 int
-strncmpic(const uschar *s, const uschar *t, int n)
+strncmpic(const uschar * s, const uschar * t, int n)
 {
 while (n--)
   {
@@ -749,9 +744,9 @@ Returns:    < 0, = 0, or > 0, according to the comparison
 */
 
 int
-strcmpic(const uschar *s, const uschar *t)
+strcmpic(const uschar * s, const uschar * t)
 {
-while (*s != 0)
+while (*s)
   {
   int c = tolower(*s++) - tolower(*t++);
   if (c != 0) return c;
@@ -775,11 +770,11 @@ Arguments:
 Returns:         pointer to substring in string, or NULL if not found
 */
 
-uschar *
-strstric(uschar *s, uschar *t, BOOL space_follows)
+const uschar *
+strstric_c(const uschar * s, const uschar * t, BOOL space_follows)
 {
-uschar *p = t;
-uschar *yield = NULL;
+const uschar * p = t;
+const uschar * yield = NULL;
 int cl = tolower(*p);
 int cu = toupper(*p);
 
@@ -787,8 +782,8 @@ while (*s)
   {
   if (*s == cl || *s == cu)
     {
-    if (yield == NULL) yield = s;
-    if (*(++p) == 0)
+    if (!yield) yield = s;
+    if (!*++p)
       {
       if (!space_follows || s[1] == ' ' || s[1] == '\n' ) return yield;
       yield = NULL;
@@ -798,7 +793,7 @@ while (*s)
     cu = toupper(*p);
     s++;
     }
-  else if (yield != NULL)
+  else if (yield)
     {
     yield = NULL;
     p = t;
@@ -810,6 +805,11 @@ while (*s)
 return NULL;
 }
 
+uschar *
+strstric(uschar * s, uschar * t, BOOL space_follows)
+{
+return US strstric_c(s, t, space_follows);
+}
 
 
 #ifdef COMPILE_UTILITY
@@ -863,16 +863,19 @@ Arguments:
             If we do the allocation, taint is handled there.
   buflen     when buffer is not NULL, the size of buffer; otherwise ignored
 
+  func      caller, for debug
+  line      caller, for debug
+
 Returns:     pointer to buffer, containing the next substring,
              or NULL if no more substrings
 */
 
 uschar *
-string_nextinlist_trc(const uschar **listptr, int *separator, uschar *buffer, int buflen,
- const uschar * func, int line)
+string_nextinlist_trc(const uschar ** listptr, int * separator, uschar * buffer,
 int buflen, const uschar * func, int line)
 {
 int sep = *separator;
-const uschar *s = *listptr;
+const uschar * s = *listptr;
 BOOL sep_is_special;
 
 if (!s) return NULL;
@@ -952,12 +955,15 @@ else
     s = ss;
     if (!*s || *++s != sep || sep_is_special) break;
     }
+
+  /* Trim trailing spaces from the returned string */
+
   /* while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; */
   while (  g->ptr > 0 && isspace(g->s[g->ptr-1])
        && (g->ptr == 1 || g->s[g->ptr-2] != '\\') )
     g->ptr--;
   buffer = string_from_gstring(g);
-  gstring_release_unused(g);
+  gstring_release_unused_trc(g, CCS func, line);
   }
 
 /* Update the current pointer and return the new string */
@@ -1091,7 +1097,16 @@ existing length of the string. */
 
 unsigned inc = oldsize < 4096 ? 127 : 1023;
 
+if (g->ptr < 0 || g->ptr > g->size || g->size >= INT_MAX/2)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "internal error in gstring_grow (ptr %d size %d)", g->ptr, g->size);
+
 if (count <= 0) return;
+
+if (count >= INT_MAX/2 - g->ptr)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "internal error in gstring_grow (ptr %d count %d)", g->ptr, count);
+
 g->size = (p + count + inc + 1) & ~inc;                /* one for a NUL */
 
 /* Try to extend an existing allocation. If the result of calling
@@ -1120,16 +1135,16 @@ terminated, because the number of characters to add is given explicitly. It is
 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
+  g       growable-string that is being built, or NULL if not assigned yet
   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.
 
-Returns:   pointer to the start of the string, changed if copied for expansion.
+Returns:   growable 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
            string - there's no point adding the NUL till the end.
+          NULL is a possible return.
 
 */
 /* coverity[+alloc] */
@@ -1140,17 +1155,26 @@ string_catn(gstring * g, const uschar *s, int count)
 int p;
 BOOL srctaint = is_tainted(s);
 
+if (count < 0)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "internal error in string_catn (count %d)", count);
+if (count == 0) return g;
+
 if (!g)
   {
   unsigned inc = count < 4096 ? 127 : 1023;
-  unsigned size = ((count + inc) &  ~inc) + 1;
+  unsigned size = ((count + inc) &  ~inc) + 1; /* round up requested count */
   g = string_get_tainted(size, srctaint);
   }
 else if (srctaint && !is_tainted(g->s))
   gstring_rebuffer(g);
 
+if (g->ptr < 0 || g->ptr > g->size)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "internal error in string_catn (ptr %d size %d)", g->ptr, g->size);
+
 p = g->ptr;
-if (p + count >= g->size)
+if (count >= g->size - p)
   gstring_grow(g, count);
 
 /* Because we always specify the exact number of characters to copy, we can
@@ -1164,9 +1188,9 @@ return g;
 
 
 gstring *
-string_cat(gstring *string, const uschar *s)
+string_cat(gstring * g, const uschar * s)
 {
-return string_catn(string, s, Ustrlen(s));
+return string_catn(g, s, Ustrlen(s));
 }
 
 
@@ -1179,30 +1203,29 @@ return string_catn(string, s, Ustrlen(s));
 It calls string_cat() to do the dirty work.
 
 Arguments:
-  string   expanding-string that is being built, or NULL
-             if this is a new string that has no contents yet
+  g       growable-string that is being built, or NULL if not yet assigned
   count    the number of strings to append
   ...      "count" uschar* arguments, which must be valid zero-terminated
              C strings
 
-Returns:   pointer to the start of the string, changed if copied for expansion.
+Returns:   growable string, changed if copied for expansion.
            The string is not zero-terminated - see string_cat() above.
 */
 
 __inline__ gstring *
-string_append(gstring *string, int count, ...)
+string_append(gstring * g, int count, ...)
 {
 va_list ap;
 
 va_start(ap, count);
 while (count-- > 0)
   {
-  uschar *t = va_arg(ap, uschar *);
-  string = string_cat(string, t);
+  uschar * t = va_arg(ap, uschar *);
+  g = string_cat(g, t);
   }
 va_end(ap);
 
-return string;
+return g;
 }
 #endif
 
@@ -1238,7 +1261,7 @@ BOOL
 string_format_trc(uschar * buffer, int buflen,
   const uschar * func, unsigned line, const char * format, ...)
 {
-gstring g = { .size = buflen, .ptr = 0, .s = buffer }, *gp;
+gstring g = { .size = buflen, .ptr = 0, .s = buffer }, * gp;
 va_list ap;
 va_start(ap, format);
 gp = string_vformat_trc(&g, func, line, STRING_SPRINTF_BUFFER_SIZE,
@@ -1281,7 +1304,7 @@ string, not nul-terminated.
 
 gstring *
 string_vformat_trc(gstring * g, const uschar * func, unsigned line,
-  unsigned size_limit, unsigned flags, const char *format, va_list ap)
+  unsigned size_limit, unsigned flags, const char * format, va_list ap)
 {
 enum ltypes { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_SIZE=6 };
 
@@ -1321,7 +1344,7 @@ off = g->ptr;             /* remember initial offset in gstring */
 while (*fp)
   {
   int length = L_NORMAL;
-  int *nptr;
+  int * nptr;
   int slen;
   const char *null = "NULL";           /* ) These variables */
   const char *item_start, *s;          /* ) are deliberately */
@@ -1624,6 +1647,8 @@ string supplied as data, adds the strerror() text, and if the failure was
 
 Arguments:
   format        a text format string - deliberately not uschar *
+  func         caller, for debug
+  line         caller, for debug
   ...           arguments for the format string
 
 Returns:        a message, in dynamic store
@@ -1631,7 +1656,7 @@ Returns:        a message, in dynamic store
 
 uschar *
 string_open_failed_trc(const uschar * func, unsigned line,
-  const char *format, ...)
+  const char * format, ...)
 {
 va_list ap;
 gstring * g = string_get(1024);
@@ -1692,6 +1717,7 @@ int main(void)
 uschar buffer[256];
 
 printf("Testing is_ip_address\n");
+store_init();
 
 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
   {