gpgscm: Fix creation of temporary directories.
[gnupg.git] / common / t-stringhelp.c
index 0c921b0..a105ad1 100644 (file)
@@ -1,20 +1,32 @@
 /* t-stringhelp.c - Regression tests for stringhelp.c
  * Copyright (C) 2007 Free Software Foundation, Inc.
+ *               2015  g10 Code GmbH
  *
- * This file is part of JNLIB.
+ * This file is part of GnuPG.
  *
- * JNLIB is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
+ * GnuPG is free software; you can redistribute and/or modify this
+ * part of GnuPG under the terms of either
  *
- * JNLIB is distributed in the hope that it will be useful, but
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * GnuPG is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * 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 <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 #ifdef HAVE_PWD_H
 # include <pwd.h>
 #endif
 #include <unistd.h>
 #include <sys/types.h>
-
-#include "stringhelp.h"
+#include <limits.h>
 
 #include "t-support.h"
+#include "stringhelp.h"
 
 
 static char *home_buffer;
@@ -42,14 +55,14 @@ gethome (void)
   if (!home_buffer)
     {
       char *home = getenv("HOME");
-      
+
       if(home)
         home_buffer = xstrdup (home);
 #if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
       else
         {
           struct passwd *pwd;
-          
+
           pwd = getpwuid (getuid());
           if (pwd)
             home_buffer = xstrdup (pwd->pw_dir);
@@ -60,15 +73,43 @@ gethome (void)
 }
 
 
+static char *
+mygetcwd (void)
+{
+  char *buffer;
+  size_t size = 100;
+
+  for (;;)
+    {
+      buffer = xmalloc (size+1);
+#ifdef HAVE_W32CE_SYSTEM
+      strcpy (buffer, "/");  /* Always "/".  */
+      return buffer;
+#else
+      if (getcwd (buffer, size) == buffer)
+        return buffer;
+      xfree (buffer);
+      if (errno != ERANGE)
+        {
+          fprintf (stderr,"error getting current cwd: %s\n",
+                   strerror (errno));
+          exit (2);
+        }
+      size *= 2;
+#endif
+    }
+}
+
+
 static void
 test_percent_escape (void)
 {
   char *result;
   static struct {
-    const char *extra; 
-    const char *value; 
+    const char *extra;
+    const char *value;
     const char *expected;
-  } tests[] = 
+  } tests[] =
     {
       { NULL, "", "" },
       { NULL, "%", "%25" },
@@ -104,7 +145,7 @@ test_percent_escape (void)
       result = percent_escape (tests[testno].value, tests[testno].extra);
       if (!result)
         fail (testno);
-      if (strcmp (result, tests[testno].expected))
+      else if (strcmp (result, tests[testno].expected))
         fail (testno);
       xfree (result);
     }
@@ -183,7 +224,8 @@ test_strconcat (void)
     fail (0);
   else if (errno != EINVAL)
     fail (0);
-  
+  xfree (out);
+
 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
   out = strconcat (NULL);
   if (!out || *out)
@@ -192,6 +234,8 @@ test_strconcat (void)
   out = strconcat (NULL, NULL);
   if (!out || *out)
     fail (1);
+  xfree (out);
+
   out = strconcat ("", NULL);
   if (!out || *out)
     fail (1);
@@ -243,6 +287,7 @@ test_xstrconcat (void)
                    "1", "2", "3", "4", "5", "6", "7", NULL);
   if (!out)
     fail (0);
+  xfree (out);
 
 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
   out = xstrconcat (NULL);
@@ -252,6 +297,8 @@ test_xstrconcat (void)
   out = xstrconcat (NULL, NULL);
   if (!out)
     fail (1);
+  xfree (out);
+
   out = xstrconcat ("", NULL);
   if (!out || *out)
     fail (1);
@@ -322,10 +369,10 @@ test_make_filename_try (void)
                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                            "1", "2", NULL);
-  if (!out || strcmp (out, 
-                      "1/2/3/4/5/6/7/8/9/10/" 
-                      "1/2/3/4/5/6/7/8/9/10/" 
-                      "1/2/3/4/5/6/7/8/9/10/" 
+  if (!out || strcmp (out,
+                      "1/2/3/4/5/6/7/8/9/10/"
+                      "1/2/3/4/5/6/7/8/9/10/"
+                      "1/2/3/4/5/6/7/8/9/10/"
                       "1/2"))
     fail (0);
   xfree (out);
@@ -359,13 +406,13 @@ test_make_filename_try (void)
   out = make_filename_try ("~/foo", "bar", NULL);
   if (!out)
     fail (2);
-  if (home)
+  else if (home)
     {
       if (strlen (out) < homelen + 7)
         fail (2);
-      if (strncmp (out, home, homelen))
+      else if (strncmp (out, home, homelen))
         fail (2);
-      if (strcmp (out+homelen, "/foo/bar"))
+      else if (strcmp (out+homelen, "/foo/bar"))
         fail (2);
     }
   else
@@ -378,13 +425,13 @@ test_make_filename_try (void)
   out = make_filename_try ("~", "bar", NULL);
   if (!out)
     fail (2);
-  if (home)
+  else if (home)
     {
       if (strlen (out) < homelen + 3)
         fail (2);
-      if (strncmp (out, home, homelen))
+      else if (strncmp (out, home, homelen))
         fail (2);
-      if (strcmp (out+homelen, "/bar"))
+      else if (strcmp (out+homelen, "/bar"))
         fail (2);
     }
   else
@@ -396,6 +443,539 @@ test_make_filename_try (void)
 }
 
 
+static void
+test_make_absfilename_try (void)
+{
+  char *out;
+  char *cwd = mygetcwd ();
+  size_t cwdlen = strlen (cwd);
+
+  out = make_absfilename_try ("foo", "bar", NULL);
+  if (!out)
+    fail (0);
+  else if (strlen (out) < cwdlen + 7)
+    fail (0);
+  else if (strncmp (out, cwd, cwdlen))
+    fail (0);
+  else if (strcmp (out+cwdlen, "/foo/bar"))
+    fail (0);
+  xfree (out);
+
+  out = make_absfilename_try ("./foo", NULL);
+  if (!out)
+    fail (1);
+  else if (strlen (out) < cwdlen + 5)
+    fail (1);
+  else if (strncmp (out, cwd, cwdlen))
+    fail (1);
+  else if (strcmp (out+cwdlen, "/./foo"))
+    fail (1);
+  xfree (out);
+
+  out = make_absfilename_try (".", NULL);
+  if (!out)
+    fail (2);
+  else if (strlen (out) < cwdlen)
+    fail (2);
+  else if (strncmp (out, cwd, cwdlen))
+    fail (2);
+  else if (strcmp (out+cwdlen, ""))
+    fail (2);
+  xfree (out);
+
+  xfree (cwd);
+}
+
+static void
+test_strsplit (void)
+{
+  struct {
+    const char *s;
+    char delim;
+    char replacement;
+    const char *fields_expected[10];
+  } tv[] = {
+    {
+      "a:bc:cde:fghi:jklmn::foo:", ':', '\0',
+      { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
+    },
+    {
+      ",a,bc,,def,", ',', '!',
+      { "!a!bc!!def!", "a!bc!!def!", "bc!!def!", "!def!", "def!", "", NULL }
+    },
+    {
+      "", ':', ',',
+      { "", NULL }
+    }
+  };
+
+  int tidx;
+
+  for (tidx = 0; tidx < DIM(tv); tidx++)
+    {
+      char *s2;
+      int field_count;
+      char **fields;
+      int field_count_expected;
+      int i;
+
+      /* Count the fields.  */
+      for (field_count_expected = 0;
+           tv[tidx].fields_expected[field_count_expected];
+           field_count_expected ++)
+        ;
+
+      /* We need to copy s since strsplit modifies it in place.  */
+      s2 = xstrdup (tv[tidx].s);
+      fields = strsplit (s2, tv[tidx].delim, tv[tidx].replacement,
+                         &field_count);
+
+      if (field_count != field_count_expected)
+        fail (tidx * 1000);
+
+      for (i = 0; i < field_count_expected; i ++)
+        if (strcmp (tv[tidx].fields_expected[i], fields[i]) != 0)
+          {
+            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);
+      xfree (s2);
+    }
+}
+
+
+
+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);
+    }
+}
+
+
+static void
+test_split_fields (void)
+{
+  struct {
+    const char *s;
+    int nfields;
+    const char *fields_expected[10];
+  } tv[] = {
+    {
+      "a bc cde fghi jklmn   foo ", 6,
+      { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL }
+    },
+    {
+      " a bc  def ", 2,
+      { "a", "bc", "def", NULL }
+    },
+    {
+      " a bc  def ", 3,
+      { "a", "bc", "def", NULL }
+    },
+    {
+      " a bc  def ", 4,
+      { "a", "bc", "def", NULL }
+    },
+    {
+      "", 0,
+      { NULL }
+    }
+  };
+
+  int tidx;
+  char *fields[10];
+  int field_count_expected, nfields, field_count, i;
+  char *s2;
+
+  for (tidx = 0; tidx < DIM(tv); tidx++)
+    {
+      nfields = tv[tidx].nfields;
+      assert (nfields <= DIM (fields));
+
+      /* Count the fields.  */
+      for (field_count_expected = 0;
+           tv[tidx].fields_expected[field_count_expected];
+           field_count_expected ++)
+        ;
+      if (field_count_expected > nfields)
+        field_count_expected = nfields;
+
+      /* We need to copy s since split_fields modifies in place.  */
+      s2 = xstrdup (tv[tidx].s);
+      field_count = split_fields (s2, fields, nfields);
+
+      if (field_count != field_count_expected)
+        {
+          printf ("%s: tidx %d: expected %d, got %d\n",
+                  __func__, tidx, field_count_expected, field_count);
+          fail (tidx * 1000);
+        }
+      else
+        {
+          for (i = 0; i < field_count_expected; i ++)
+            if (strcmp (tv[tidx].fields_expected[i], fields[i]))
+              {
+                printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n",
+                        __func__,
+                        tidx, i, tv[tidx].fields_expected[i], fields[i]);
+                fail (tidx * 1000 + i + 1);
+              }
+        }
+
+      xfree (s2);
+    }
+}
+
+
+static char *
+stresc (char *s)
+{
+  char *p;
+  int l = strlen (s) + 1;
+
+  for (p = s; *p; p ++)
+    if (*p == '\n')
+      l ++;
+
+  p = xmalloc (l);
+  for (l = 0; *s; s ++, l ++)
+    {
+      if (*s == ' ')
+        p[l] = '_';
+      else if (*p == '\n')
+        {
+          p[l ++] = '\\';
+          p[l ++] = 'n';
+          p[l] = '\n';
+        }
+      else
+        p[l] = *s;
+    }
+  p[l] = *s;
+
+  return p;
+}
+
+
+static void
+test_format_text (void)
+{
+  struct test
+  {
+    int target_cols, max_cols;
+    char *input;
+    char *expected;
+  };
+
+  struct test tests[] = {
+    {
+      10, 12,
+      "",
+      "",
+    },
+    {
+      10, 12,
+      " ",
+      "",
+    },
+    {
+      10, 12,
+      "  ",
+      "",
+    },
+    {
+      10, 12,
+      " \n ",
+      " \n",
+    },
+    {
+      10, 12,
+      " \n  \n ",
+      " \n  \n",
+    },
+    {
+      10, 12,
+      "0123456789 0123456789 0",
+      "0123456789\n0123456789\n0",
+    },
+    {
+      10, 12,
+      "   0123456789   0123456789   0  ",
+      "   0123456789\n0123456789\n0",
+    },
+    {
+      10, 12,
+      "01 34 67 90 23 56  89 12 45 67 89 1",
+      "01 34 67\n90 23 56\n89 12 45\n67 89 1"
+    },
+    {
+      10, 12,
+      "01 34 67 90 23 56  89 12 45 67 89 1",
+      "01 34 67\n90 23 56\n89 12 45\n67 89 1"
+    },
+    {
+      72, 80,
+      "Warning: if you think you've seen more than 10 messages "
+      "signed by this key, then this key might be a forgery!  "
+      "Carefully examine the email address for small variations "
+      "(e.g., additional white space).  If the key is suspect, "
+      "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n",
+      "Warning: if you think you've seen more than 10 messages signed by this\n"
+      "key, then this key might be a forgery!  Carefully examine the email\n"
+      "address for small variations (e.g., additional white space).  If the key\n"
+      "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n"
+      "being bad.\n"
+
+    },
+    {
+      72, 80,
+      "Normally, there is only a single key associated with an email "
+      "address.  However, people sometimes generate a new key if "
+      "their key is too old or they think it might be compromised.  "
+      "Alternatively, a new key may indicate a man-in-the-middle "
+      "attack!  Before accepting this key, you should talk to or "
+      "call the person to make sure this new key is legitimate.",
+      "Normally, there is only a single key associated with an email "
+      "address.\nHowever, people sometimes generate a new key if "
+      "their key is too old or\nthey think it might be compromised.  "
+      "Alternatively, a new key may indicate\na man-in-the-middle "
+      "attack!  Before accepting this key, you should talk\nto or "
+      "call the person to make sure this new key is legitimate.",
+    }
+  };
+
+  int i;
+  int failed = 0;
+
+  for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++)
+    {
+      struct test *test = &tests[i];
+      char *result =
+        format_text (test->input, 0, test->target_cols, test->max_cols);
+      if (strcmp (result, test->expected) != 0)
+        {
+          printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n",
+                  __func__, i + 1, stresc (test->expected), stresc (result));
+          failed ++;
+        }
+      xfree (result);
+    }
+
+  if (failed)
+    fail(0);
+}
+
+
+static void
+test_compare_version_strings (void)
+{
+  struct { const char *a; const char *b; int okay; } tests[] = {
+    { "1.0.0",   "1.0.0", 0 },
+    { "1.0.0-",  "1.0.0", 1 },
+    { "1.0.0-1", "1.0.0", 1 },
+    { "1.0.0.1", "1.0.0", 1 },
+    { "1.0.0",   "1.0.1", -1 },
+    { "1.0.0-",  "1.0.1", -1 },
+    { "1.0.0-1", "1.0.1", -1 },
+    { "1.0.0.1", "1.0.1", -1 },
+    { "1.0.0",   "1.1.0", -1 },
+    { "1.0.0-",  "1.1.0", -1 },
+    { "1.0.0-1", "1.1.0", -1 },
+    { "1.0.0.1", "1.1.0", -1 },
+
+    { "1.0.0",   "1.0.0-", -1 },
+    { "1.0.0",   "1.0.0-1", -1 },
+    { "1.0.0",   "1.0.0.1", -1 },
+    { "1.1.0",   "1.0.0", 1 },
+    { "1.1.1",   "1.1.0", 1 },
+    { "1.1.2",   "1.1.2", 0 },
+    { "1.1.2",   "1.0.2", 1 },
+    { "1.1.2",   "0.0.2", 1 },
+    { "1.1.2",   "1.1.3", -1 },
+
+    { "0.99.1",  "0.9.9", 1 },
+    { "0.9.1",   "0.91.0", -1 },
+
+    { "1.5.3",   "1.5",  1 },
+    { "1.5.0",   "1.5",  0 },
+    { "1.4.99",  "1.5",  -1 },
+    { "1.5",     "1.4.99",  1 },
+    { "1.5",     "1.5.0",  0 },
+    { "1.5",     "1.5.1",  -1 },
+
+    { "1.5.3-x17",   "1.5-23",  1 },
+
+    { "1.5.3a",   "1.5.3",  1 },
+    { "1.5.3a",   "1.5.3b",  -1 },
+
+    { "3.1.4-ab", "3.1.4-ab", 0 },
+    { "3.1.4-ab", "3.1.4-ac", -1 },
+    { "3.1.4-ac", "3.1.4-ab", 1 },
+    { "3.1.4-ab", "3.1.4-abb", -1 },
+    { "3.1.4-abb", "3.1.4-ab", 1 },
+
+    { "",       "",   INT_MIN },
+    { NULL,     "",   INT_MIN },
+    { "1.2.3",  "",   INT_MIN },
+    { "1.2.3",  "2",  INT_MIN },
+
+    /* Test cases for validity of A.  */
+    { "",      NULL, INT_MIN },
+    { "1",     NULL, INT_MIN },
+    { "1.",    NULL, 0       },
+    { "1.0",   NULL, 0       },
+    { "1.0.",  NULL, 0       },
+    { "a1.2",  NULL, INT_MIN },
+    { NULL,    NULL, INT_MIN }
+  };
+  int idx;
+  int res;
+
+  for (idx=0; idx < DIM(tests); idx++)
+    {
+      res = compare_version_strings (tests[idx].a, tests[idx].b);
+      /* printf ("test %d: '%s'  '%s'  %d  ->  %d\n", */
+      /*         idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */
+      if (res != tests[idx].okay)
+        fail (idx);
+    }
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -407,8 +987,13 @@ main (int argc, char **argv)
   test_strconcat ();
   test_xstrconcat ();
   test_make_filename_try ();
+  test_make_absfilename_try ();
+  test_strsplit ();
+  test_strtokenize ();
+  test_split_fields ();
+  test_compare_version_strings ();
+  test_format_text ();
 
   xfree (home_buffer);
-  return 0;
+  return !!errcount;
 }
-