Merge branch 'debian' into hcoop_489
[hcoop/debian/exim4.git] / src / string.c
index 0f657dc..cec5950 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Miscellaneous string-handling functions. Some are not required for
@@ -165,7 +165,7 @@ Returns:      pointer to the buffer
 uschar *
 string_format_size(int size, uschar *buffer)
 {
-if (size == 0) Ustrcpy(CS buffer, "     ");
+if (size == 0) Ustrcpy(buffer, "     ");
 else if (size < 1024) sprintf(CS buffer, "%5d", size);
 else if (size < 10*1024)
   sprintf(CS buffer, "%4.1fK", (double)size / 1024.0);
@@ -224,13 +224,13 @@ Returns:   the value of the character escape
 */
 
 int
-string_interpret_escape(uschar **pp)
+string_interpret_escape(const uschar **pp)
 {
 #ifdef COMPILE_UTILITY
 const uschar *hex_digits= CUS"0123456789abcdef";
 #endif
 int ch;
-uschar *p = *pp;
+const uschar *p = *pp;
 ch = *(++p);
 if (isdigit(ch) && ch != '8' && ch != '9')
   {
@@ -284,12 +284,12 @@ Arguments:
 Returns:        string with non-printers encoded as printing sequences
 */
 
-uschar *
-string_printing2(uschar *s, BOOL allow_tab)
+const uschar *
+string_printing2(const uschar *s, BOOL allow_tab)
 {
 int nonprintcount = 0;
 int length = 0;
-uschar *t = s;
+const uschar *t = s;
 uschar *ss, *tt;
 
 while (*t != 0)
@@ -306,7 +306,7 @@ expanded string. */
 
 ss = store_get(length + nonprintcount * 3 + 1);
 
-/* Copy everying, escaping non printers. */
+/* Copy everything, escaping non printers. */
 
 t = s;
 tt = ss;
@@ -374,7 +374,7 @@ while (*p)
   {
   if (*p == '\\')
     {
-    *q++ = string_interpret_escape(&p);
+    *q++ = string_interpret_escape((const uschar **)&p);
     p++;
     }
   else
@@ -437,7 +437,7 @@ Returns:  copy of string in new store
 */
 
 uschar *
-string_copy_malloc(uschar *s)
+string_copy_malloc(const uschar *s)
 {
 int len = Ustrlen(s) + 1;
 uschar *ss = store_malloc(len);
@@ -457,7 +457,7 @@ Returns:  copy of string in new store, with letters lowercased
 */
 
 uschar *
-string_copylc(uschar *s)
+string_copylc(const uschar *s)
 {
 uschar *ss = store_get(Ustrlen(s) + 1);
 uschar *p = ss;
@@ -483,7 +483,7 @@ Returns:    copy of string in new store
 */
 
 uschar *
-string_copyn(uschar *s, int n)
+string_copyn(const uschar *s, int n)
 {
 uschar *ss = store_get(n + 1);
 Ustrncpy(ss, s, n);
@@ -639,9 +639,9 @@ Returns:   the new string
 */
 
 uschar *
-string_dequote(uschar **sptr)
+string_dequote(const uschar **sptr)
 {
-uschar *s = *sptr;
+const uschar *s = *sptr;
 uschar *t, *yield;
 
 /* First find the end of the string */
@@ -717,8 +717,9 @@ uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
 va_start(ap, format);
 if (!string_vformat(buffer, sizeof(buffer), format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-    "string_sprintf expansion was longer than " SIZE_T_FMT " (%s)",
-    sizeof(buffer), format);
+    "string_sprintf expansion was longer than " SIZE_T_FMT
+    "; format string was (%s)\nexpansion started '%.32s'",
+    sizeof(buffer), format, buffer);
 va_end(ap);
 return string_copy(buffer);
 }
@@ -868,10 +869,10 @@ Returns:     pointer to buffer, containing the next substring,
 */
 
 uschar *
-string_nextinlist(uschar **listptr, int *separator, uschar *buffer, int buflen)
+string_nextinlist(const uschar **listptr, int *separator, uschar *buffer, int buflen)
 {
-register int sep = *separator;
-register uschar *s = *listptr;
+int sep = *separator;
+const uschar *s = *listptr;
 BOOL sep_is_special;
 
 if (s == NULL) return NULL;
@@ -912,7 +913,7 @@ sep_is_special = iscntrl(sep);
 
 if (buffer != NULL)
   {
-  register int p = 0;
+  int p = 0;
   for (; *s != 0; s++)
     {
     if (*s == sep && (*(++s) != sep || sep_is_special)) break;
@@ -928,7 +929,7 @@ else
   {
   int size = 0;
   int ptr = 0;
-  uschar *ss;
+  const 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 (if an ispunct()
@@ -951,7 +952,7 @@ else
   for (;;)
     {
     for (ss = s + 1; *ss != 0 && *ss != sep; ss++);
-    buffer = string_cat(buffer, &size, &ptr, s, ss-s);
+    buffer = string_catn(buffer, &size, &ptr, s, ss-s);
     s = ss;
     if (*s == 0 || *(++s) != sep || sep_is_special) break;
     }
@@ -969,18 +970,18 @@ return buffer;
 
 #ifndef COMPILE_UTILITY
 /************************************************
-*      Add element to seperated list           *
+*      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
+given element has any embedded separator characters
 doubled.
 
 Arguments:
   list points to the start of 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.
 */
@@ -994,17 +995,62 @@ uschar * sp;
 
 if (list)
   {
-  new = string_cat(new, &sz, &off, list, Ustrlen(list));
-  new = string_cat(new, &sz, &off, &sep, 1);
+  new = string_cat (new, &sz, &off, list);
+  new = string_catn(new, &sz, &off, &sep, 1);
   }
 
 while((sp = Ustrchr(ele, sep)))
   {
-  new = string_cat(new, &sz, &off, ele, sp-ele+1);
-  new = string_cat(new, &sz, &off, &sep, 1);
+  new = string_catn(new, &sz, &off, ele, sp-ele+1);
+  new = string_catn(new, &sz, &off, &sep, 1);
+  ele = sp+1;
+  }
+new = string_cat(new, &sz, &off, ele);
+new[off] = '\0';
+return new;
+}
+
+
+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;
+}
+
+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);
+  }
+
+while((sp = Ustrnchr(ele, sep, &len)))
+  {
+  new = string_catn(new, &sz, &off, ele, sp-ele+1);
+  new = string_catn(new, &sz, &off, &sep, 1);
   ele = sp+1;
+  len--;
   }
-new = string_cat(new, &sz, &off, ele, Ustrlen(ele));
+new = string_catn(new, &sz, &off, ele, len);
 new[off] = '\0';
 return new;
 }
@@ -1032,7 +1078,7 @@ Arguments:
              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
+             is a C string.  If -1 given, strlen(s) is used.
 
 If string is given as NULL, *size and *ptr should both be zero.
 
@@ -1040,10 +1086,12 @@ 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
            string - there's no point adding the NUL till the end.
+
 */
+/* coverity[+alloc] */
 
 uschar *
-string_cat(uschar *string, int *size, int *ptr, const uschar *s, int count)
+string_catn(uschar *string, int *size, int *ptr, const uschar *s, int count)
 {
 int p = *ptr;
 
@@ -1086,12 +1134,25 @@ if (p + count >= *size)
 
 /* 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;
 }
+
+
+uschar *
+string_cat(uschar *string, int *size, int *ptr, const uschar *s)
+{
+return string_catn(string, size, ptr, s, Ustrlen(s));
+}
 #endif  /* COMPILE_UTILITY */
 
 
@@ -1129,7 +1190,7 @@ va_start(ap, count);
 for (i = 0; i < count; i++)
   {
   uschar *t = va_arg(ap, uschar *);
-  string = string_cat(string, size, ptr, t, Ustrlen(t));
+  string = string_cat(string, size, ptr, t);
   }
 va_end(ap);
 
@@ -1151,10 +1212,10 @@ on whether the variable length list of data arguments are given explicitly or
 as a va_list item.
 
 The formats are the usual printf() ones, with some omissions (never used) and
-two additions for strings: %S forces lower case, and %#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.
+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
+(useful in debugging). There is also the addition of %D and %M, which insert
+the date in the form used for datestamped log files.
 
 Arguments:
   buffer       a buffer in which to put the formatted string
@@ -1367,6 +1428,7 @@ while (*fp != 0)
 
     case 's':
     case 'S':                   /* Forces *lower* case */
+    case 'T':                   /* Forces *upper* case */
     s = va_arg(ap, char *);
 
     if (s == NULL) s = null;
@@ -1414,6 +1476,8 @@ while (*fp != 0)
     sprintf(CS p, "%*.*s", width, precision, s);
     if (fp[-1] == 'S')
       while (*p) { *p = tolower(*p); p++; }
+    else if (fp[-1] == 'T')
+      while (*p) { *p = toupper(*p); p++; }
     else
       while (*p) p++;
     if (!yield) goto END_FORMAT;
@@ -1472,6 +1536,7 @@ specified messages. If it does, the message just gets truncated, and there
 doesn't seem much we can do about that. */
 
 (void)string_vformat(buffer+15, sizeof(buffer) - 15, format, ap);
+va_end(ap);
 
 return (eno == EACCES)?
   string_sprintf("%s: %s (euid=%ld egid=%ld)", buffer, strerror(eno),
@@ -1482,159 +1547,19 @@ return (eno == EACCES)?
 
 
 
-#ifndef COMPILE_UTILITY
-/*************************************************
-*        Generate local prt for logging          *
-*************************************************/
 
-/* This function is a subroutine for use in string_log_address() below.
 
-Arguments:
-  addr        the address being logged
-  yield       the current dynamic buffer pointer
-  sizeptr     points to current size
-  ptrptr      points to current insert pointer
-
-Returns:      the new value of the buffer pointer
-*/
-
-static uschar *
-string_get_localpart(address_item *addr, uschar *yield, int *sizeptr,
-  int *ptrptr)
-{
-if (testflag(addr, af_include_affixes) && addr->prefix != NULL)
-  yield = string_cat(yield, sizeptr, ptrptr, addr->prefix,
-    Ustrlen(addr->prefix));
-yield = string_cat(yield, sizeptr, ptrptr, addr->local_part,
-  Ustrlen(addr->local_part));
-if (testflag(addr, af_include_affixes) && addr->suffix != NULL)
-  yield = string_cat(yield, sizeptr, ptrptr, addr->suffix,
-    Ustrlen(addr->suffix));
-return yield;
-}
-
-
-/*************************************************
-*          Generate log address list             *
-*************************************************/
-
-/* This function generates a list consisting of an address and its parents, for
-use in logging lines. For saved onetime aliased addresses, the onetime parent
-field is used. If the address was delivered by a transport with rcpt_include_
-affixes set, the af_include_affixes bit will be set in the address. In that
-case, we include the affixes here too.
-
-Arguments:
-  addr          bottom (ultimate) address
-  all_parents   if TRUE, include all parents
-  success       TRUE for successful delivery
-
-Returns:        a string in dynamic store
-*/
+#ifndef COMPILE_UTILITY
+/* qsort(3), currently used to sort the environment variables
+for -bP environment output, needs a function to compare two pointers to string
+pointers. Here it is. */
 
-uschar *
-string_log_address(address_item *addr, BOOL all_parents, BOOL success)
+int
+string_compare_by_pointer(const void *a, const void *b)
 {
-int size = 64;
-int ptr = 0;
-BOOL add_topaddr = TRUE;
-uschar *yield = store_get(size);
-address_item *topaddr;
-
-/* Find the ultimate parent */
-
-for (topaddr = addr; topaddr->parent != NULL; topaddr = topaddr->parent);
-
-/* We start with just the local part for pipe, file, and reply deliveries, and
-for successful local deliveries from routers that have the log_as_local flag
-set. File deliveries from filters can be specified as non-absolute paths in
-cases where the transport is goin to complete the path. If there is an error
-before this happens (expansion failure) the local part will not be updated, and
-so won't necessarily look like a path. Add extra text for this case. */
-
-if (testflag(addr, af_pfr) ||
-      (success &&
-       addr->router != NULL && addr->router->log_as_local &&
-       addr->transport != NULL && addr->transport->info->local))
-  {
-  if (testflag(addr, af_file) && addr->local_part[0] != '/')
-    yield = string_cat(yield, &size, &ptr, CUS"save ", 5);
-  yield = string_get_localpart(addr, yield, &size, &ptr);
-  }
-
-/* Other deliveries start with the full address. It we have split it into local
-part and domain, use those fields. Some early failures can happen before the
-splitting is done; in those cases use the original field. */
-
-else
-  {
-  if (addr->local_part != NULL)
-    {
-    yield = string_get_localpart(addr, yield, &size, &ptr);
-    yield = string_cat(yield, &size, &ptr, US"@", 1);
-    yield = string_cat(yield, &size, &ptr, addr->domain,
-      Ustrlen(addr->domain) );
-    }
-  else
-    {
-    yield = string_cat(yield, &size, &ptr, addr->address, Ustrlen(addr->address));
-    }
-  yield[ptr] = 0;
-
-  /* If the address we are going to print is the same as the top address,
-  and all parents are not being included, don't add on the top address. First
-  of all, do a caseless comparison; if this succeeds, do a caseful comparison
-  on the local parts. */
-
-  if (strcmpic(yield, topaddr->address) == 0 &&
-      Ustrncmp(yield, topaddr->address, Ustrchr(yield, '@') - yield) == 0 &&
-      addr->onetime_parent == NULL &&
-      (!all_parents || addr->parent == NULL || addr->parent == topaddr))
-    add_topaddr = FALSE;
-  }
-
-/* If all parents are requested, or this is a local pipe/file/reply, and
-there is at least one intermediate parent, show it in brackets, and continue
-with all of them if all are wanted. */
-
-if ((all_parents || testflag(addr, af_pfr)) &&
-    addr->parent != NULL &&
-    addr->parent != topaddr)
-  {
-  uschar *s = US" (";
-  address_item *addr2;
-  for (addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent)
-    {
-    yield = string_cat(yield, &size, &ptr, s, 2);
-    yield = string_cat(yield, &size, &ptr, addr2->address, Ustrlen(addr2->address));
-    if (!all_parents) break;
-    s = US", ";
-    }
-  yield = string_cat(yield, &size, &ptr, US")", 1);
-  }
-
-/* Add the top address if it is required */
-
-if (add_topaddr)
-  {
-  yield = string_cat(yield, &size, &ptr, US" <", 2);
-
-  if (addr->onetime_parent == NULL)
-    yield = string_cat(yield, &size, &ptr, topaddr->address,
-      Ustrlen(topaddr->address));
-  else
-    yield = string_cat(yield, &size, &ptr, addr->onetime_parent,
-      Ustrlen(addr->onetime_parent));
-
-  yield = string_cat(yield, &size, &ptr, US">", 1);
-  }
-
-yield[ptr] = 0;  /* string_cat() leaves space */
-return yield;
+return Ustrcmp(* CUSS a, * CUSS b);
 }
-#endif  /* COMPILE_UTILITY */
-
-
+#endif /* COMPILE_UTILITY */