common: Minor change of hex2str to allow for embedded nul.
[gnupg.git] / common / stringhelp.c
index 36f96b8..7f40c7f 100644 (file)
@@ -1,21 +1,34 @@
 /* stringhelp.c -  standard string helper functions
- * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005,
- *               2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007,
+ *               2008, 2009, 2010  Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 2015  g10 Code GmbH
  *
- * This file is part of JNLIB.
+ * This file is part of JNLIB, which is a subsystem 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.
+ * 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
+ *     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.
  *
  * JNLIB 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 <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <unistd.h>
 #include <sys/types.h>
 #ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
+# endif
 # include <windows.h>
 #endif
 
+#include "util.h"
 #include "libjnlib-config.h"
 #include "utf8conv.h"
+#include "sysutils.h"
 #include "stringhelp.h"
 
-
 #define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
 
 /* Sometimes we want to avoid mixing slashes and backslashes on W32
@@ -48,7 +65,7 @@
 static inline char *
 change_slashes (char *name)
 {
-#ifdef HAVE_DRIVE_LETTERS
+#ifdef HAVE_DOSISH_SYSTEM
   char *p;
 
   if (strchr (name, '\\'))
@@ -57,12 +74,35 @@ change_slashes (char *name)
         if (*p == '/')
           *p = '\\';
     }
-#endif /*HAVE_DRIVE_LETTERS*/
+#endif /*HAVE_DOSISH_SYSTEM*/
   return name;
 }
 
 
 /*
+ * Check whether STRING starts with KEYWORD.  The keyword is
+ * delimited by end of string, a space or a tab.  Returns NULL if not
+ * found or a pointer into STRING to the next non-space character
+ * after the KEYWORD (which may be end of string).
+ */
+char *
+has_leading_keyword (const char *string, const char *keyword)
+{
+  size_t n = strlen (keyword);
+
+  if (!strncmp (string, keyword, n)
+      && (!string[n] || string[n] == ' ' || string[n] == '\t'))
+    {
+      string += n;
+      while (*string == ' ' || *string == '\t')
+        string++;
+      return (char*)string;
+    }
+  return NULL;
+}
+
+
+/*
  * Look for the substring SUB in buffer and return a pointer to that
  * substring in BUFFER or NULL if not found.
  * Comparison is case-insensitive.
@@ -120,7 +160,7 @@ ascii_memistr ( const void *buffer, size_t buflen, const char *sub )
 /* This function is similar to strncpy().  However it won't copy more
    than N - 1 characters and makes sure that a '\0' is appended. With
    N given as 0, nothing will happen.  With DEST given as NULL, memory
-   will be allocated using jnlib_xmalloc (i.e. if it runs out of core
+   will be allocated using xmalloc (i.e. if it runs out of core
    the function terminates).  Returns DES or a pointer to the
    allocated memory.
  */
@@ -132,7 +172,7 @@ mem2str( char *dest , const void *src , size_t n )
 
     if( n ) {
        if( !dest )
-           dest = jnlib_xmalloc( n ) ;
+           dest = xmalloc( n ) ;
        d = dest;
        s = src ;
        for(n--; n && *s; n-- )
@@ -230,7 +270,7 @@ length_sans_trailing_chars (const unsigned char *line, size_t len,
 {
   const unsigned char *p, *mark;
   size_t n;
-  
+
   for( mark=NULL, p=line, n=0; n < len; n++, p++ )
     {
       if (strchr (trimchars, *p ))
@@ -241,8 +281,8 @@ length_sans_trailing_chars (const unsigned char *line, size_t len,
       else
         mark = NULL;
     }
-  
-  if (mark) 
+
+  if (mark)
     return mark - line;
   return len;
 }
@@ -273,15 +313,17 @@ make_basename(const char *filepath, const char *inputpath)
     (void)inputpath; /* Only required for riscos.  */
 
     if ( !(p=strrchr(filepath, '/')) )
-#ifdef HAVE_DRIVE_LETTERS
+#ifdef HAVE_DOSISH_SYSTEM
        if ( !(p=strrchr(filepath, '\\')) )
+#endif
+#ifdef HAVE_DRIVE_LETTERS
            if ( !(p=strrchr(filepath, ':')) )
 #endif
              {
-               return jnlib_xstrdup(filepath);
+               return xstrdup(filepath);
              }
 
-    return jnlib_xstrdup(p+1);
+    return xstrdup(p+1);
 #endif
 }
 
@@ -300,16 +342,18 @@ make_dirname(const char *filepath)
     char *p;
 
     if ( !(p=strrchr(filepath, '/')) )
-#ifdef HAVE_DRIVE_LETTERS
+#ifdef HAVE_DOSISH_SYSTEM
        if ( !(p=strrchr(filepath, '\\')) )
+#endif
+#ifdef HAVE_DRIVE_LETTERS
            if ( !(p=strrchr(filepath, ':')) )
 #endif
              {
-               return jnlib_xstrdup(".");
+               return xstrdup(".");
              }
 
     dirname_length = p-filepath;
-    dirname = jnlib_xmalloc(dirname_length+1);
+    dirname = xmalloc(dirname_length+1);
     strncpy(dirname, filepath, dirname_length);
     dirname[dirname_length] = 0;
 
@@ -342,9 +386,9 @@ get_pwdir (int xmode, const char *name)
   if (pwd)
     {
       if (xmode)
-        result = jnlib_xstrdup (pwd->pw_dir);
+        result = xstrdup (pwd->pw_dir);
       else
-        result = jnlib_strdup (pwd->pw_dir);
+        result = xtrystrdup (pwd->pw_dir);
     }
 #else /*!HAVE_PWD_H*/
   /* No support at all.  */
@@ -354,21 +398,31 @@ get_pwdir (int xmode, const char *name)
   return result;
 }
 
+
+/* xmode 0 := Return NULL on error
+         1 := Terminate on error
+         2 := Make sure that name is absolute; return NULL on error
+         3 := Make sure that name is absolute; terminate on error
+ */
 static char *
 do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
 {
   const char *argv[32];
   int argc;
-  size_t n; 
+  size_t n;
   int skip = 1;
   char *home_buffer = NULL;
-  char *name, *home, *p;                           
-       
-  n = strlen (first_part) + 1;                     
+  char *name, *home, *p;
+  int want_abs;
+
+  want_abs = !!(xmode & 2);
+  xmode &= 1;
+
+  n = strlen (first_part) + 1;
   argc = 0;
-  while ( (argv[argc] = va_arg (arg_ptr, const char *)) )   
+  while ( (argv[argc] = va_arg (arg_ptr, const char *)) )
     {
-      n += strlen (argv[argc]) + 1;                        
+      n += strlen (argv[argc]) + 1;
       if (argc >= DIM (argv)-1)
         {
           if (xmode)
@@ -376,11 +430,11 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
           jnlib_set_errno (EINVAL);
           return NULL;
         }
-      argc++; 
+      argc++;
     }
   n++;
-  
-  home = NULL;                                     
+
+  home = NULL;
   if (*first_part == '~')
     {
       if (first_part[1] == '/' || !first_part[1])
@@ -390,18 +444,18 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
           if (!home)
             home = home_buffer = get_pwdir (xmode, NULL);
           if (home && *home)
-            n += strlen (home);                            
+            n += strlen (home);
         }
       else
         {
           /* This is the "~username/" or "~username" case.  */
           char *user;
-    
+
           if (xmode)
-            user = jnlib_xstrdup (first_part+1);
+            user = xstrdup (first_part+1);
           else
             {
-              user = jnlib_strdup (first_part+1);
+              user = xtrystrdup (first_part+1);
               if (!user)
                 return NULL;
             }
@@ -409,9 +463,9 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
           if (p)
             *p = 0;
           skip = 1 + strlen (user);
-          
+
           home = home_buffer = get_pwdir (xmode, user);
-          jnlib_free (user);
+          xfree (user);
           if (home)
             n += strlen (home);
           else
@@ -420,27 +474,84 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
     }
 
   if (xmode)
-    name = jnlib_xmalloc (n);
+    name = xmalloc (n);
   else
     {
-      name = jnlib_malloc (n);
+      name = xtrymalloc (n);
       if (!name)
         {
-          jnlib_free (home_buffer);
+          xfree (home_buffer);
           return NULL;
         }
     }
-  
+
   if (home)
     p = stpcpy (stpcpy (name, home), first_part + skip);
   else
     p = stpcpy (name, first_part);
 
-  jnlib_free (home_buffer);
-
+  xfree (home_buffer);
   for (argc=0; argv[argc]; argc++)
     p = stpcpy (stpcpy (p, "/"), argv[argc]);
 
+  if (want_abs)
+    {
+#ifdef HAVE_DRIVE_LETTERS
+      p = strchr (name, ':');
+      if (p)
+        p++;
+      else
+        p = name;
+#else
+      p = name;
+#endif
+      if (*p != '/'
+#ifdef HAVE_DRIVE_LETTERS
+          && *p != '\\'
+#endif
+          )
+        {
+          home = gnupg_getcwd ();
+          if (!home)
+            {
+              if (xmode)
+                {
+                  fprintf (stderr, "\nfatal: getcwd failed: %s\n",
+                           strerror (errno));
+                  exit(2);
+                }
+              xfree (name);
+              return NULL;
+            }
+          n = strlen (home) + 1 + strlen (name) + 1;
+          if (xmode)
+            home_buffer = xmalloc (n);
+          else
+            {
+              home_buffer = xtrymalloc (n);
+              if (!home_buffer)
+                {
+                  xfree (name);
+                  return NULL;
+                }
+            }
+          if (p == name)
+            p = home_buffer;
+          else /* Windows case.  */
+            {
+              memcpy (home_buffer, p, p - name + 1);
+              p = home_buffer + (p - name + 1);
+            }
+          strcpy (stpcpy (stpcpy (p, home), "/"), name);
+          xfree (name);
+          name = home_buffer;
+          /* Let's do a simple compression to catch the most common
+             case of using "." for gpg's --homedir option.  */
+          n = strlen (name);
+          if (n > 2 && name[n-2] == '/' && name[n-1] == '.')
+            name[n-2] = 0;
+        }
+    }
   return change_slashes (name);
 }
 
@@ -474,20 +585,50 @@ make_filename_try (const char *first_part, ... )
   return result;
 }
 
+/* Construct an absolute filename from the NULL terminated list of
+   parts.  Tilde expansion is done for the first argument.  This
+   function terminates the process on memory shortage. */
+char *
+make_absfilename (const char *first_part, ... )
+{
+  va_list arg_ptr;
+  char *result;
+
+  va_start (arg_ptr, first_part);
+  result = do_make_filename (3, first_part, arg_ptr);
+  va_end (arg_ptr);
+  return result;
+}
+
+/* Construct an absolute filename from the NULL terminated list of
+   parts.  Tilde expansion is done for the first argument.  This
+   function may return NULL on error. */
+char *
+make_absfilename_try (const char *first_part, ... )
+{
+  va_list arg_ptr;
+  char *result;
+
+  va_start (arg_ptr, first_part);
+  result = do_make_filename (2, first_part, arg_ptr);
+  va_end (arg_ptr);
+  return result;
+}
+
 
 \f
 /* Compare whether the filenames are identical.  This is a
    special version of strcmp() taking the semantics of filenames in
    account.  Note that this function works only on the supplied names
-   without considereing any context like the current directory.  See
+   without considering any context like the current directory.  See
    also same_file_p(). */
 int
 compare_filenames (const char *a, const char *b)
 {
-#ifdef HAVE_DRIVE_LETTERS
-  for ( ; *a && *b; a++, b++ ) 
+#ifdef HAVE_DOSISH_SYSTEM
+  for ( ; *a && *b; a++, b++ )
     {
-      if (*a != *b 
+      if (*a != *b
           && (toupper (*(const unsigned char*)a)
               != toupper (*(const unsigned char*)b) )
           && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')))
@@ -496,7 +637,7 @@ compare_filenames (const char *a, const char *b)
   if ((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/'))
     return 0;
   else
-    return (toupper (*(const unsigned char*)a) 
+    return (toupper (*(const unsigned char*)a)
             - toupper (*(const unsigned char*)b));
 #else
     return strcmp(a,b);
@@ -532,129 +673,6 @@ hextobyte (const char *s)
 }
 
 
-/* Print a BUFFER to stream FP while replacing all control characters
-   and the characters DELIM and DELIM2 with standard C escape
-   sequences.  Returns the number of characters printed. */
-size_t 
-print_sanitized_buffer2 (FILE *fp, const void *buffer, size_t length,
-                         int delim, int delim2)
-{
-  const unsigned char *p = buffer;
-  size_t count = 0;
-
-  for (; length; length--, p++, count++)
-    {
-      if (*p < 0x20 
-          || *p == 0x7f
-          || *p == delim 
-          || *p == delim2
-          || ((delim || delim2) && *p=='\\'))
-        {
-          putc ('\\', fp);
-          count++;
-          if (*p == '\n')
-            {
-              putc ('n', fp);
-              count++;
-            }
-          else if (*p == '\r')
-            {
-              putc ('r', fp);
-              count++;
-            }
-          else if (*p == '\f')
-            {
-              putc ('f', fp);
-              count++;
-            }
-          else if (*p == '\v')
-            {
-              putc ('v', fp);
-              count++;
-            }
-          else if (*p == '\b')
-            {
-              putc ('b', fp);
-              count++;
-            }
-          else if (!*p)
-            {
-              putc('0', fp);
-              count++;
-            }
-          else
-            {
-              fprintf (fp, "x%02x", *p);
-              count += 3;
-            }
-       }
-      else
-        {
-          putc (*p, fp);
-          count++;
-        }
-    }
-
-  return count;
-}
-
-/* Same as print_sanitized_buffer2 but with just one delimiter. */
-size_t 
-print_sanitized_buffer (FILE *fp, const void *buffer, size_t length,
-                        int delim)
-{
-  return print_sanitized_buffer2 (fp, buffer, length, delim, 0);
-}
-
-
-size_t 
-print_sanitized_utf8_buffer (FILE *fp, const void *buffer,
-                             size_t length, int delim)
-{
-  const char *p = buffer;
-  size_t i;
-
-  /* We can handle plain ascii simpler, so check for it first. */
-  for (i=0; i < length; i++ ) 
-    {
-      if ( (p[i] & 0x80) )
-        break;
-    }
-  if (i < length)
-    {
-       char *buf = utf8_to_native (p, length, delim);
-       /*(utf8 conversion already does the control character quoting)*/
-        i = strlen (buf);
-       fputs (buf, fp);
-       jnlib_free (buf);
-        return i;
-    }
-  else
-    return print_sanitized_buffer (fp, p, length, delim);
-}
-
-
-size_t 
-print_sanitized_string2 (FILE *fp, const char *string, int delim, int delim2)
-{
-  return string? print_sanitized_buffer2 (fp, string, strlen (string),
-                                          delim, delim2):0;
-}
-
-size_t 
-print_sanitized_string (FILE *fp, const char *string, int delim)
-{
-  return string? print_sanitized_buffer (fp, string, strlen (string), delim):0;
-}
-
-size_t 
-print_sanitized_utf8_string (FILE *fp, const char *string, int delim)
-{
-  return string? print_sanitized_utf8_buffer (fp,
-                                              string, strlen (string),
-                                              delim) : 0;
-}
-
 /* 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.  */
@@ -667,7 +685,7 @@ sanitize_buffer (const void *p_arg, size_t n, int delim)
   char *buffer, *d;
 
   /* First count length. */
-  for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ ) 
+  for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ )
     {
       if ( *p < 0x20 || *p == 0x7f || *p == delim  || (delim && *p=='\\'))
         {
@@ -683,7 +701,7 @@ sanitize_buffer (const void *p_arg, size_t n, int delim)
   p = save_p;
   n = save_n;
   /* And now make the string */
-  d = buffer = jnlib_xmalloc( buflen );
+  d = buffer = xmalloc( buflen );
   for ( ; n; n--, p++ )
     {
       if (*p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) {
@@ -739,7 +757,7 @@ const char *
 w32_strerror (int ec)
 {
   static char strerr[256];
-  
+
   if (ec == -1)
     ec = (int)GetLastError ();
 #ifdef HAVE_W32CE_SYSTEM
@@ -751,7 +769,7 @@ w32_strerror (int ec)
                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
                  strerr, DIM (strerr)-1, NULL);
 #endif
-  return strerr;    
+  return strerr;
 }
 #endif /*HAVE_W32_SYSTEM*/
 
@@ -772,7 +790,7 @@ ascii_islower (int c)
     return c >= 'a' && c <= 'z';
 }
 
-int 
+int
 ascii_toupper (int c)
 {
     if (c >= 'a' && c <= 'z')
@@ -780,7 +798,7 @@ ascii_toupper (int c)
     return c;
 }
 
-int 
+int
 ascii_tolower (int c)
 {
     if (c >= 'A' && c <= 'Z')
@@ -788,6 +806,18 @@ ascii_tolower (int c)
     return c;
 }
 
+/* Lowercase all ASCII characters in S.  */
+char *
+ascii_strlwr (char *s)
+{
+  char *p = s;
+
+  for (p=s; *p; p++ )
+    if (isascii (*p) && *p >= 'A' && *p <= 'Z')
+      *p |= 0x20;
+
+  return s;
+}
 
 int
 ascii_strcasecmp( const char *a, const char *b )
@@ -802,7 +832,7 @@ ascii_strcasecmp( const char *a, const char *b )
     return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
 }
 
-int 
+int
 ascii_strncasecmp (const char *a, const char *b, size_t n)
 {
   const unsigned char *p1 = (const unsigned char *)a;
@@ -824,7 +854,7 @@ ascii_strncasecmp (const char *a, const char *b, size_t n)
       ++p2;
     }
   while (c1 == c2);
-  
+
   return c1 - c2;
 }
 
@@ -870,7 +900,7 @@ ascii_memcasemem (const void *haystack, size_t nhaystack,
     {
       const char *a = haystack;
       const char *b = a + nhaystack - nneedle;
-      
+
       for (; a <= b; a++)
         {
           if ( !ascii_memcasecmp (a, needle, nneedle) )
@@ -896,6 +926,26 @@ stpcpy(char *a,const char *b)
 }
 #endif
 
+#ifndef HAVE_STRPBRK
+/* Find the first occurrence in S of any character in ACCEPT.
+   Code taken from glibc-2.6/string/strpbrk.c (LGPLv2.1+) and modified. */
+char *
+strpbrk (const char *s, const char *accept)
+{
+  while (*s != '\0')
+    {
+      const char *a = accept;
+      while (*a != '\0')
+       if (*a++ == *s)
+         return (char *) s;
+      ++s;
+    }
+
+  return NULL;
+}
+#endif /*!HAVE_STRPBRK*/
+
+
 #ifndef HAVE_STRSEP
 /* Code taken from glibc-2.2.1/sysdeps/generic/strsep.c. */
 char *
@@ -908,8 +958,8 @@ strsep (char **stringp, const char *delim)
     return NULL;
 
   /* A frequent case is when the delimiter string contains only one
-     character.  Here we don't need to call the expensive `strpbrk'
-     function and instead work using `strchr'.  */
+     character.  Here we don't need to call the expensive 'strpbrk'
+     function and instead work using 'strchr'.  */
   if (delim[0] == '\0' || delim[1] == '\0')
     {
       char ch = delim[0];
@@ -1014,10 +1064,10 @@ do_percent_escape (const char *str, const char *extra, int die)
     if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
       j++;
   if (die)
-    ptr = jnlib_xmalloc (i + 2 * j + 1);
+    ptr = xmalloc (i + 2 * j + 1);
   else
     {
-      ptr = jnlib_malloc (i + 2 * j + 1);
+      ptr = xtrymalloc (i + 2 * j + 1);
       if (!ptr)
         return NULL;
     }
@@ -1092,7 +1142,7 @@ do_strconcat (const char *s1, va_list arg_ptr)
       argc++;
     }
   needed++;
-  buffer = jnlib_malloc (needed);
+  buffer = xtrymalloc (needed);
   if (buffer)
     {
       for (p = buffer, argc=0; argv[argc]; argc++)
@@ -1112,7 +1162,7 @@ strconcat (const char *s1, ...)
   char *result;
 
   if (!s1)
-    result = jnlib_strdup ("");
+    result = xtrystrdup ("");
   else
     {
       va_start (arg_ptr, s1);
@@ -1131,7 +1181,7 @@ xstrconcat (const char *s1, ...)
   char *result;
 
   if (!s1)
-    result = jnlib_xstrdup ("");
+    result = xstrdup ("");
   else
     {
       va_start (arg_ptr, s1);
@@ -1149,3 +1199,38 @@ xstrconcat (const char *s1, ...)
   return result;
 }
 
+/* Split a string into fields at DELIM.  REPLACEMENT is the character
+   to replace the delimiter with (normally: '\0' so that each field is
+   NUL terminated).  The caller is responsible for freeing the result.
+   Note: this function modifies STRING!  If you need the original
+   value, then you should pass a copy to this function.
+
+   If malloc fails, this function returns NULL.  */
+char **
+strsplit (char *string, char delim, char replacement, int *count)
+{
+  int fields = 1;
+  char *t;
+  char **result;
+
+  /* First, count the number of fields.  */
+  for (t = strchr (string, delim); t; t = strchr (t + 1, delim))
+    fields ++;
+
+  result = xtrycalloc (sizeof (*result), (fields + 1));
+  if (! result)
+    return NULL;
+
+  result[0] = string;
+  fields = 1;
+  for (t = strchr (string, delim); t; t = strchr (t + 1, delim))
+    {
+      result[fields ++] = t + 1;
+      *t = replacement;
+    }
+
+  if (count)
+    *count = fields;
+
+  return result;
+}