common: Clarify use of vars in buffer copy code.
[gnupg.git] / common / stringhelp.c
index e8b990a..dea2212 100644 (file)
@@ -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.
@@ -538,6 +540,7 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
               home_buffer = xtrymalloc (n);
               if (!home_buffer)
                 {
+                  xfree (home);
                   xfree (name);
                   return NULL;
                 }
@@ -556,6 +559,7 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
           else
             strcpy (stpcpy (stpcpy (p, home), "/"), name);
 
+          xfree (home);
           xfree (name);
           name = home_buffer;
           /* Let's do a simple compression to catch the most common
@@ -658,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
@@ -685,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
@@ -1329,6 +1293,147 @@ strtokenize (const char *string, const char *delim)
 }
 
 
+/* Split a string into space delimited fields and remove leading and
+ * trailing spaces from each field.  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.
+ * 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 (char *string, char **array, int arraysize)
+{
+  int n = 0;
+  char *p, *pend;
+
+  for (p = string; *p == ' '; p++)
+    ;
+  do
+    {
+      if (n == arraysize)
+        break;
+      array[n++] = p;
+      pend = strchr (p, ' ');
+      if (!pend)
+        break;
+      *pend++ = 0;
+      for (p = pend; *p == ' '; p++)
+        ;
+    }
+  while (*p);
+
+  return n;
+}
+
+
+\f
+/* Version number parsing.  */
+
+/* This function parses the first portion of the version number S and
+   stores it in *NUMBER.  On success, this function returns a pointer
+   into S starting with the first character, which is not part of the
+   initial number portion; on failure, NULL is returned.  */
+static const char*
+parse_version_number (const char *s, int *number)
+{
+  int val = 0;
+
+  if (*s == '0' && digitp (s+1))
+    return NULL;  /* Leading zeros are not allowed.  */
+  for (; digitp (s); s++)
+    {
+      val *= 10;
+      val += *s - '0';
+    }
+  *number = val;
+  return val < 0 ? NULL : s;
+}
+
+
+/* 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.  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.  */
+static const char *
+parse_version_string (const char *s, int *major, int *minor, int *micro)
+{
+  s = parse_version_number (s, major);
+  if (!s || *s != '.')
+    return NULL;
+  s++;
+  s = parse_version_number (s, minor);
+  if (!s)
+    return NULL;
+  if (*s == '.')
+    {
+      s++;
+      s = parse_version_number (s, micro);
+      if (!s)
+        return NULL;
+    }
+  else
+    *micro = 0;
+  return s;  /* Patchlevel.  */
+}
+
+
+/* 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;
+  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)
+    {
+      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;
+    }
+  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.