common: Add function strtokenize.
authorWerner Koch <wk@gnupg.org>
Mon, 22 Jun 2015 17:28:33 +0000 (19:28 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 22 Jun 2015 17:28:33 +0000 (19:28 +0200)
* common/stringhelp.c: Include assert.h.
(strtokenize): New.
* common/t-stringhelp.c (test_strtokenize): New.

Signed-off-by: Werner Koch <wk@gnupg.org>
common/stringhelp.c
common/stringhelp.h
common/t-stringhelp.c

index ca8ae0b..6714eb8 100644 (file)
@@ -48,6 +48,7 @@
 # endif
 # include <windows.h>
 #endif
+#include <assert.h>
 
 #include "util.h"
 #include "common-defs.h"
@@ -1234,3 +1235,69 @@ strsplit (char *string, char delim, char replacement, int *count)
 
   return result;
 }
+
+
+/* Tokenize STRING using the set of delimiters in DELIM.  Leading
+ * spaces and tabs are removed from all tokens.  The caller must xfree
+ * the result.
+ *
+ * Returns: A malloced and NULL delimited array with the tokens.  On
+ *          memory error NULL is returned and ERRNO is set.
+ */
+char **
+strtokenize (const char *string, const char *delim)
+{
+  const char *s;
+  size_t fields;
+  size_t bytes, n;
+  char *buffer;
+  char *p, *px, *pend;
+  char **result;
+
+  /* Count the number of fields.  */
+  for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
+    fields++;
+  fields++; /* Add one for the terminating NULL.  */
+
+  /* Allocate an array for all fields, a terminating NULL, and space
+     for a copy of the string.  */
+  bytes = fields * sizeof *result;
+  if (bytes / sizeof *result != fields)
+    {
+      gpg_err_set_errno (ENOMEM);
+      return NULL;
+    }
+  n = strlen (string) + 1;
+  bytes += n;
+  if (bytes < n)
+    {
+      gpg_err_set_errno (ENOMEM);
+      return NULL;
+    }
+  result = xtrymalloc (bytes);
+  if (!result)
+    return NULL;
+  buffer = (char*)(result + fields);
+
+  /* Copy and parse the string.  */
+  strcpy (buffer, string);
+  for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
+    {
+      *pend = 0;
+      while (spacep (p))
+        p++;
+      for (px = pend - 1; px >= p && spacep (px); px--)
+        *px = 0;
+      result[n++] = p;
+    }
+  while (spacep (p))
+    p++;
+  for (px = p + strlen (p) - 1; px >= p && spacep (px); px--)
+    *px = 0;
+  result[n++] = p;
+  result[n] = NULL;
+
+  assert ((char*)(result + n + 1) == buffer);
+
+  return result;
+}
index b9b51fd..ab16d16 100644 (file)
@@ -145,6 +145,10 @@ char *xstrconcat (const char *s1, ...) GNUPG_GCC_A_SENTINEL(0);
 
 char **strsplit (char *string, char delim, char replacement, int *count);
 
+/* Tokenize STRING using the set of delimiters in DELIM.  */
+char **strtokenize (const char *string, const char *delim);
+
+
 /*-- mapstrings.c --*/
 const char *map_static_macro_string (const char *string);
 
index 6c47237..13f3afa 100644 (file)
@@ -538,6 +538,146 @@ test_strsplit (void)
     }
 }
 
+
+
+static void
+test_strtokenize (void)
+{
+  struct {
+    const char *s;
+    const char *delim;
+    const char *fields_expected[10];
+  } tv[] = {
+    {
+      "", ":",
+      { "", NULL }
+    },
+    {
+      "a", ":",
+      { "a", NULL }
+    },
+    {
+      ":", ":",
+      { "", "", NULL }
+    },
+    {
+      "::", ":",
+      { "", "", "", NULL }
+    },
+    {
+      "a:b:c", ":",
+      { "a", "b", "c", NULL }
+    },
+    {
+      "a:b:", ":",
+      { "a", "b", "", NULL }
+    },
+    {
+      "a:b", ":",
+      { "a", "b", NULL }
+    },
+    {
+      "aa:b:cd", ":",
+      { "aa", "b", "cd", NULL }
+    },
+    {
+      "aa::b:cd", ":",
+      { "aa", "", "b", "cd", NULL }
+    },
+    {
+      "::b:cd", ":",
+      { "", "", "b", "cd", NULL }
+    },
+    {
+      "aa:   : b:cd ", ":",
+      { "aa", "", "b", "cd", NULL }
+    },
+    {
+      "  aa:   : b:  cd ", ":",
+      { "aa", "", "b", "cd", NULL }
+    },
+    {
+      "  ", ":",
+      { "", NULL }
+    },
+    {
+      "  :", ":",
+      { "", "", NULL }
+    },
+    {
+      "  : ", ":",
+      { "", "", NULL }
+    },
+    {
+      ": ", ":",
+      { "", "", NULL }
+    },
+    {
+      ": x ", ":",
+      { "", "x", NULL }
+    },
+    {
+      "a:bc:cde:fghi:jklmn::foo:", ":",
+      { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
+    },
+    {
+      ",a,bc,,def,", ",",
+      { "", "a", "bc", "", "def", "", NULL }
+    },
+    {
+      " a ", " ",
+      { "", "a", "", NULL }
+    },
+    {
+      " ", " ",
+      { "", "", NULL }
+    },
+    {
+      "", " ",
+      { "", NULL }
+    }
+  };
+
+  int tidx;
+
+  for (tidx = 0; tidx < DIM(tv); tidx++)
+    {
+      char **fields;
+      int field_count;
+      int field_count_expected;
+      int i;
+
+      for (field_count_expected = 0;
+           tv[tidx].fields_expected[field_count_expected];
+           field_count_expected ++)
+        ;
+
+      fields = strtokenize (tv[tidx].s, tv[tidx].delim);
+      if (!fields)
+        fail (tidx * 1000);
+      else
+        {
+          for (field_count = 0; fields[field_count]; field_count++)
+            ;
+          if (field_count != field_count_expected)
+            fail (tidx * 1000);
+          else
+            {
+              for (i = 0; i < field_count_expected; i++)
+                if (strcmp (tv[tidx].fields_expected[i], fields[i]))
+                  {
+                    printf ("For field %d, expected '%s', but got '%s'\n",
+                            i, tv[tidx].fields_expected[i], fields[i]);
+                    fail (tidx * 1000 + i + 1);
+                  }
+            }
+          }
+
+      xfree (fields);
+    }
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -551,6 +691,7 @@ main (int argc, char **argv)
   test_make_filename_try ();
   test_make_absfilename_try ();
   test_strsplit ();
+  test_strtokenize ();
 
   xfree (home_buffer);
   return 0;