Update version number and copyright year.
[exim.git] / src / src / string.c
index 2f69cd494ff5c1bcf57a04a7350a37759f5c5371..c0a8805feafb3454a480abb077eb19792c2821c3 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/string.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
+/* $Cambridge: exim/src/src/string.c,v 1.11 2007/01/08 10:50:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Miscellaneous string-handling functions. Some are not required for
@@ -28,6 +28,7 @@ Arguments:
   s         a string
   maskptr   NULL if no mask is permitted to follow
             otherwise, points to an int where the offset of '/' is placed
+            if there is no / followed by trailing digits, *maskptr is set 0
 
 Returns:    0 if the string is not a textual representation of an IP address
             4 if it is an IPv4 address
@@ -127,7 +128,9 @@ if (Ustrchr(s, ':') != NULL)
   sign, which introduces the interface specifier (scope id) of a link local
   address. */
 
-  if (!v4end) return (*s == 0 || *s == '%' || *s == '/')? yield : 0;
+  if (!v4end)
+    return (*s == 0 || *s == '%' ||
+           (*s == '/' && maskptr != NULL && *maskptr != 0))? yield : 0;
   }
 
 /* Test for IPv4 address, which may be the tail-end of an IPv6 address. */
@@ -139,7 +142,8 @@ for (i = 0; i < 4; i++)
   if (isdigit(*s) && isdigit(*(++s))) s++;
   }
 
-return (*s == 0 || *s == '/')? yield : 0;
+return (*s == 0 || (*s == '/' && maskptr != NULL && *maskptr != 0))?
+  yield : 0;
 }
 #endif  /* COMPILE_UTILITY */
 
@@ -944,9 +948,9 @@ 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, %#s or %#S prints nothing for
-a NULL string. Without the # "NULL" is printed (useful in debugging). There is
-also the addition of %D, which inserts the date in the form used for
+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, which inserts the date in the form used for
 datestamped log files.
 
 Arguments:
@@ -973,6 +977,8 @@ return yield;
 BOOL
 string_vformat(uschar *buffer, int buflen, char *format, va_list ap)
 {
+enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };
+
 BOOL yield = TRUE;
 int width, precision;
 char *fp = format;             /* Deliberately not unsigned */
@@ -985,6 +991,7 @@ string_datestamp_offset = -1;  /* Datestamp not inserted */
 
 while (*fp != 0)
   {
+  int length = L_NORMAL;
   int *nptr;
   int slen;
   char *null = "NULL";         /* ) These variables */
@@ -1038,7 +1045,25 @@ while (*fp != 0)
       }
     }
 
-  if (strchr("hlL", *fp) != NULL) fp++;
+  /* Skip over 'h', 'L', 'l', and 'll', remembering the item length */
+
+  if (*fp == 'h')
+    { fp++; length = L_SHORT; }
+  else if (*fp == 'L')
+    { fp++; length = L_LONGDOUBLE; }
+  else if (*fp == 'l')
+    {
+    if (fp[1] == 'l')
+      {
+      fp += 2;
+      length = L_LONGLONG;
+      }
+    else
+      {
+      fp++;
+      length = L_LONG;
+      }
+    }
 
   /* Handle each specific format type. */
 
@@ -1054,10 +1079,21 @@ while (*fp != 0)
     case 'u':
     case 'x':
     case 'X':
-    if (p >= last - 12) { yield = FALSE; goto END_FORMAT; }
+    if (p >= last - ((length > L_LONG)? 24 : 12))
+      { yield = FALSE; goto END_FORMAT; }
     strncpy(newformat, item_start, fp - item_start);
     newformat[fp - item_start] = 0;
-    sprintf(CS p, newformat, va_arg(ap, int));
+
+    /* Short int is promoted to int when passing through ..., so we must use
+    int for va_arg(). */
+
+    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;
+      }
     while (*p) p++;
     break;
 
@@ -1070,9 +1106,11 @@ while (*fp != 0)
     break;
 
     /* %f format is inherently insecure if the numbers that it may be
-    handed are unknown (e.g. 1e300). However, in Exim, the only use of %f
-    is for printing load averages, and these are actually stored as integers
-    (load average * 1000) so the size of the numbers is constrained. */
+    handed are unknown (e.g. 1e300). However, in Exim, %f is used for
+    printing load averages, and these are actually stored as integers
+    (load average * 1000) so the size of the numbers is constrained.
+    It is also used for formatting sending rates, where the simplicity
+    of the format prevents overflow. */
 
     case 'f':
     case 'e':
@@ -1083,7 +1121,10 @@ while (*fp != 0)
     if (p >= last - precision - 8) { yield = FALSE; goto END_FORMAT; }
     strncpy(newformat, item_start, fp - item_start);
     newformat[fp-item_start] = 0;
-    sprintf(CS p, newformat, va_arg(ap, double));
+    if (length == L_LONGDOUBLE)
+      sprintf(CS p, newformat, va_arg(ap, long double));
+    else
+      sprintf(CS p, newformat, va_arg(ap, double));
     while (*p) p++;
     break;
 
@@ -1431,8 +1472,10 @@ printf("Testing string_format\n");
 while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
   {
   void *args[3];
+  long long llargs[3];
   double dargs[3];
   int dflag = 0;
+  int llflag = 0;
   int n = 0;
   int count;
   int countset = 0;
@@ -1463,6 +1506,11 @@ while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
         dflag = 1;
         dargs[n++] = Ustrtod(outbuf, NULL);
         }
+      else if (Ustrstr(outbuf, "ll") != NULL)
+        {
+        llflag = 1;
+        llargs[n++] = strtoull(CS outbuf, NULL, 10);
+        }
       else
         {
         args[n++] = (void *)Uatoi(outbuf);
@@ -1485,11 +1533,16 @@ while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
     if (*s == ',') s++;
     }
 
-  if (!dflag) printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
-    args[0], args[1], args[2])? "True" : "False");
+  if (!dflag && !llflag)
+    printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
+      args[0], args[1], args[2])? "True" : "False");
+
+  else if (dflag)
+    printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
+      dargs[0], dargs[1], dargs[2])? "True" : "False");
 
   else printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
-    dargs[0], dargs[1], dargs[2])? "True" : "False");
+    llargs[0], llargs[1], llargs[2])? "True" : "False");
 
   printf("%s\n", CS outbuf);
   if (countset) printf("count=%d\n", count);