agent: Fix segv running in --server mode
[gnupg.git] / common / stringhelp.c
index 95912e0..0abac8a 100644 (file)
@@ -6,8 +6,8 @@
  *
  * This file is part of GnuPG.
  *
- * GnuPG is free software; you can redistribute it and/or modify it
- * under the terms of either
+ * GnuPG is free software; you can redistribute and/or modify this
+ * part of GnuPG under the terms of either
  *
  *   - the GNU Lesser General Public License as published by the Free
  *     Software Foundation; either version 3 of the License, or (at
@@ -28,7 +28,7 @@
  *
  * You should have received a copies of the GNU General Public License
  * and the GNU Lesser General Public License along with this program;
- * if not, see <http://www.gnu.org/licenses/>.
+ * if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -49,6 +49,7 @@
 # include <windows.h>
 #endif
 #include <assert.h>
+#include <limits.h>
 
 #include "util.h"
 #include "common-defs.h"
@@ -58,6 +59,7 @@
 
 #define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
 
+
 /* Sometimes we want to avoid mixing slashes and backslashes on W32
    and prefer backslashes.  There is usual no problem with mixing
    them, however a very few W32 API calls can't grok plain slashes.
@@ -660,6 +662,25 @@ compare_filenames (const char *a, const char *b)
 }
 
 
+/* Convert a base-10 number in STRING into a 64 bit unsigned int
+ * value.  Leading white spaces are skipped but no error checking is
+ * done.  Thus it is similar to atoi(). */
+uint64_t
+string_to_u64 (const char *string)
+{
+  uint64_t val = 0;
+
+  while (spacep (string))
+    string++;
+  for (; digitp (string); string++)
+    {
+      val *= 10;
+      val += *string - '0';
+    }
+  return val;
+}
+
+
 /* Convert 2 hex characters at S to a byte value.  Return this value
    or -1 if there is an error. */
 int
@@ -687,65 +708,6 @@ hextobyte (const char *s)
   return c;
 }
 
-
-/* Create a string from the buffer P_ARG of length N which is suitable
-   for printing.  Caller must release the created string using xfree.
-   This function terminates the process on memory shortage.  */
-char *
-sanitize_buffer (const void *p_arg, size_t n, int delim)
-{
-  const unsigned char *p = p_arg;
-  size_t save_n, buflen;
-  const unsigned char *save_p;
-  char *buffer, *d;
-
-  /* First count length. */
-  for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ )
-    {
-      if ( *p < 0x20 || *p == 0x7f || *p == delim  || (delim && *p=='\\'))
-        {
-          if ( *p=='\n' || *p=='\r' || *p=='\f'
-               || *p=='\v' || *p=='\b' || !*p )
-            buflen += 2;
-          else
-            buflen += 5;
-       }
-      else
-        buflen++;
-    }
-  p = save_p;
-  n = save_n;
-  /* And now make the string */
-  d = buffer = xmalloc( buflen );
-  for ( ; n; n--, p++ )
-    {
-      if (*p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) {
-        *d++ = '\\';
-        if( *p == '\n' )
-          *d++ = 'n';
-        else if( *p == '\r' )
-          *d++ = 'r';
-        else if( *p == '\f' )
-          *d++ = 'f';
-        else if( *p == '\v' )
-          *d++ = 'v';
-        else if( *p == '\b' )
-          *d++ = 'b';
-        else if( !*p )
-          *d++ = '0';
-        else {
-          sprintf(d, "x%02x", *p );
-          d += 3;
-        }
-      }
-      else
-        *d++ = *p;
-    }
-  *d = 0;
-  return buffer;
-}
-
-
 /* Given a string containing an UTF-8 encoded text, return the number
    of characters in this string.  It differs from strlen in that it
    only counts complete UTF-8 characters.  SIZE is the maximum length
@@ -1090,7 +1052,8 @@ do_percent_escape (const char *str, const char *extra, int die)
     return NULL;
 
   for (i=j=0; str[i]; i++)
-    if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
+    if (str[i] == ':' || str[i] == '%' || str[i] == '\n'
+        || (extra && strchr (extra, str[i])))
       j++;
   if (die)
     ptr = xmalloc (i + 2 * j + 1);
@@ -1115,6 +1078,13 @@ do_percent_escape (const char *str, const char *extra, int die)
          ptr[i++] = '2';
          ptr[i++] = '5';
        }
+      else if (*str == '\n')
+       {
+         /* The newline is problematic in a line-based format.  */
+         ptr[i++] = '%';
+         ptr[i++] = '0';
+         ptr[i++] = 'a';
+       }
       else if (extra && strchr (extra, *str))
         {
          ptr[i++] = '%';
@@ -1369,6 +1339,42 @@ split_fields (char *string, char **array, int arraysize)
 }
 
 
+/* Split a string into colon delimited fields A pointer to each field
+ * is stored in ARRAY.  Stop splitting at ARRAYSIZE fields.  The
+ * function modifies STRING.  The number of parsed fields is returned.
+ * Note that leading and trailing spaces are not removed from the fields.
+ * Example:
+ *
+ *   char *fields[2];
+ *   if (split_fields (string, fields, DIM (fields)) < 2)
+ *     return  // Not enough args.
+ *   foo (fields[0]);
+ *   foo (fields[1]);
+ */
+int
+split_fields_colon (char *string, char **array, int arraysize)
+{
+  int n = 0;
+  char *p, *pend;
+
+  p = string;
+  do
+    {
+      if (n == arraysize)
+        break;
+      array[n++] = p;
+      pend = strchr (p, ':');
+      if (!pend)
+        break;
+      *pend++ = 0;
+      p = pend;
+    }
+  while (*p);
+
+  return n;
+}
+
+
 \f
 /* Version number parsing.  */
 
@@ -1395,9 +1401,9 @@ parse_version_number (const char *s, int *number)
 
 /* This function breaks up the complete string-representation of the
    version number S, which is of the following struture: <major
-   number>.<minor number>.<micro number><patch level>.  The major,
-   minor and micro number components will be stored in *MAJOR, *MINOR
-   and *MICRO.
+   number>.<minor number>[.<micro number>]<patch level>.  The major,
+   minor, and micro number components will be stored in *MAJOR, *MINOR
+   and *MICRO.  If MICRO is not given 0 is used instead.
 
    On success, the last component, the patch level, will be returned;
    in failure, NULL will be returned.  */
@@ -1424,44 +1430,62 @@ parse_version_string (const char *s, int *major, int *minor, int *micro)
 }
 
 
-/* Check that the version string MY_VERSION is greater or equal than
-   REQ_VERSION.  Returns true if the condition is satisfied or false
-   if not.  This works with 3 part and two part version strings; for a
-   two part version string the micor part is assumed to be 0.  */
+/* Compare the version string MY_VERSION to the version string
+ * REQ_VERSION.  Returns -1, 0, or 1 if MY_VERSION is found,
+ * respectively, to be less than, to match, or be greater than
+ * REQ_VERSION.  This function works for three and two part version
+ * strings; for a two part version string the micro part is assumed to
+ * be 0.  Patch levels are compared as strings.  If a version number
+ * is invalid INT_MIN is returned.  If REQ_VERSION is given as NULL
+ * the function returns 0 if MY_VERSION is parsable version string. */
 int
 compare_version_strings (const char *my_version, const char *req_version)
 {
   int my_major, my_minor, my_micro;
   int rq_major, rq_minor, rq_micro;
-
-  if (!my_version || !req_version)
-    return 0;
-
-  if (!parse_version_string (my_version, &my_major, &my_minor, &my_micro))
-    return 0;
-  if (!parse_version_string(req_version, &rq_major, &rq_minor, &rq_micro))
-    return 0;
-
-  if (my_major > rq_major
-      || (my_major == rq_major && my_minor > rq_minor)
-      || (my_major == rq_major && my_minor == rq_minor
-         && my_micro >= rq_micro))
+  const char *my_patch, *rq_patch;
+  int result;
+
+  if (!my_version)
+    return INT_MIN;
+
+  my_patch = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
+  if (!my_patch)
+    return INT_MIN;
+  if (!req_version)
+    return 0; /* MY_VERSION can be parsed.  */
+  rq_patch = parse_version_string (req_version, &rq_major, &rq_minor,&rq_micro);
+  if (!rq_patch)
+    return INT_MIN;
+
+  if (my_major == rq_major)
     {
-      return 1;
+      if (my_minor == rq_minor)
+        {
+          if (my_micro == rq_micro)
+            result = strcmp (my_patch, rq_patch);
+          else
+            result = my_micro - rq_micro;
+        }
+      else
+        result = my_minor - rq_minor;
     }
-  return 0;
+  else
+    result = my_major - rq_major;
+
+  return !result? 0 : result < 0 ? -1 : 1;
 }
 
 
 \f
 /* Format a string so that it fits within about TARGET_COLS columns.
-   If IN_PLACE is 0, then TEXT is copied to a new buffer, which is
-   returned.  Otherwise, TEXT is modified in place and returned.
  Normally, target_cols will be 72 and max_cols is 80.  */
+ * TEXT_IN is copied to a new buffer, which is returned.  Normally,
+ * target_cols will be 72 and max_cols is 80.  On error NULL is
* returned and ERRNO is set. */
 char *
-format_text (char *text, int in_place, int target_cols, int max_cols)
+format_text (const char *text_in, int target_cols, int max_cols)
 {
-  const int do_debug = 0;
+  /* const int do_debug = 0; */
 
   /* The character under consideration.  */
   char *p;
@@ -1471,9 +1495,11 @@ format_text (char *text, int in_place, int target_cols, int max_cols)
   char *last_space = NULL;
   int last_space_cols = 0;
   int copied_last_space = 0;
+  char *text;
 
-  if (! in_place)
-    text = xstrdup (text);
+  text = xtrystrdup (text_in);
+  if (!text)
+    return NULL;
 
   p = line = text;
   while (1)
@@ -1527,9 +1553,9 @@ format_text (char *text, int in_place, int target_cols, int max_cols)
           cols_with_left_space = last_space_cols;
           cols_with_right_space = cols;
 
-          if (do_debug)
-            log_debug ("Breaking: '%.*s'\n",
-                       (int) ((uintptr_t) p - (uintptr_t) line), line);
+          /* if (do_debug) */
+          /*   log_debug ("Breaking: '%.*s'\n", */
+          /*              (int) ((uintptr_t) p - (uintptr_t) line), line); */
 
           /* The number of columns away from TARGET_COLS.  We prefer
              to underflow than to overflow.  */
@@ -1541,21 +1567,22 @@ format_text (char *text, int in_place, int target_cols, int max_cols)
                max_cols.  */
             right_penalty += 4 * (cols_with_right_space - max_cols);
 
-          if (do_debug)
-            log_debug ("Left space => %d cols (penalty: %d); right space => %d cols (penalty: %d)\n",
-                       cols_with_left_space, left_penalty,
-                       cols_with_right_space, right_penalty);
+          /* if (do_debug) */
+          /*   log_debug ("Left space => %d cols (penalty: %d); " */
+          /*              "right space => %d cols (penalty: %d)\n", */
+          /*              cols_with_left_space, left_penalty, */
+          /*              cols_with_right_space, right_penalty); */
           if (last_space_cols && left_penalty <= right_penalty)
-            /* Prefer the left space.  */
             {
-              if (do_debug)
-                log_debug ("Breaking at left space.\n");
+              /* Prefer the left space.  */
+              /* if (do_debug) */
+              /*   log_debug ("Breaking at left space.\n"); */
               p = last_space;
             }
           else
             {
-              if (do_debug)
-                log_debug ("Breaking at right space.\n");
+              /* if (do_debug) */
+              /*   log_debug ("Breaking at right space.\n"); */
             }
 
           if (! *p)