Implement tilde expansion in the same was as 1.4.
authorWerner Koch <wk@gnupg.org>
Wed, 26 Aug 2009 08:55:57 +0000 (08:55 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 26 Aug 2009 08:55:57 +0000 (08:55 +0000)
jnlib/ChangeLog
jnlib/stringhelp.c
jnlib/stringhelp.h
jnlib/t-stringhelp.c
jnlib/t-support.c

index 98323b1..2418be9 100644 (file)
@@ -1,3 +1,15 @@
+2009-08-26  Werner Koch  <wk@g10code.com>
+
+       * stringhelp.c [HAVE_PWD_H]: Include pwd.h.
+       (do_make_filename): New.
+       (make_filename, make_filename_try): Implement using the new
+       function.
+       * t-stringhelp.c (test_make_filename_try): New.
+       * t-support.c (gcry_strdup): Fix.
+
+       * stringhelp.h (make_filename, make_filename_try): Add sentinel
+       attribute.
+
 2009-08-25  Werner Koch  <wk@g10code.com>
 
        * stringhelp.c: Include errno.h.
index e8a6908..ebdbfc8 100644 (file)
 #include <stdarg.h>
 #include <ctype.h>
 #include <errno.h>
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
 #ifdef HAVE_W32_SYSTEM
-#include <windows.h>
+# include <windows.h>
 #endif
 
 #include "libjnlib-config.h"
@@ -313,62 +318,159 @@ make_dirname(const char *filepath)
 
 
 \f
-/* Implementation of make_filename and make_filename_try.  We need to
-   use macros here to avoid the use of the sometimes problematic
-   va_copy function which is not available on all systems.  */
-#define MAKE_FILENAME_PART1                        \
-  va_list arg_ptr;                                 \
-  size_t n;                                        \
-  const char *s;                                   \
-  char *name, *home, *p;                           \
-                                                   \
-  va_start (arg_ptr, first_part);                  \
-  n = strlen (first_part) + 1;                     \
-  while ( (s = va_arg (arg_ptr, const char *)) )   \
-    n += strlen(s) + 1;                            \
-  va_end(arg_ptr);                                 \
-                                                   \
-  home = NULL;                                     \
-  if ( *first_part == '~' && first_part[1] == '/'  \
-       && (home = getenv("HOME")) && *home )       \
-    n += strlen (home);                            
+static char *
+do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
+{
+  const char *argv[32];
+  int argc;
+  size_t n; 
+  int skip = 1;
+  char *home_buffer = NULL;
+  char *name, *home, *p;                           
+       
+  n = strlen (first_part) + 1;                     
+  argc = 0;
+  while ( (argv[argc] = va_arg (arg_ptr, const char *)) )   
+    {
+      n += strlen (argv[argc]) + 1;                        
+      if (argc >= DIM (argv)-1)
+        {
+          if (xmode)
+            BUG ();
+          errno = EINVAL;
+          return NULL;
+        }
+      argc++; 
+    }
+  n++;
   
-#define MAKE_FILENAME_PART2                         \
-  p = (home                                         \
-       ? stpcpy (stpcpy (name,home), first_part + 1)\
-       : stpcpy(name, first_part));                 \
-                                                    \
-  va_start (arg_ptr, first_part);                   \
-  while ( (s = va_arg(arg_ptr, const char *)) )     \
-    p = stpcpy (stpcpy (p,"/"), s);                 \
-  va_end(arg_ptr);                                  \
-  return change_slashes (name);
+  home = NULL;                                     
+  if (*first_part == '~')
+    {
+      if (first_part[1] == '/' || !first_part[1])
+        {
+          /* This is the "~/" or "~" case.  */
+          home = getenv("HOME");
+
+#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
+          if (!home)
+            {
+              struct passwd *pwd;
+              
+              pwd = getpwuid (getuid());
+              if (pwd)
+                {
+                  if (xmode)
+                    home_buffer = home = jnlib_xstrdup (pwd->pw_dir);
+                  else
+                    {
+                      home_buffer = home = jnlib_strdup (pwd->pw_dir);
+                      if (!home)
+                        return NULL;
+                    }
+                }
+            }
+#endif /* HAVE_GETPWUID && HAVE_PWD_H */
+
+          if (home && *home)
+            n += strlen (home);                            
+        }
+#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
+      else
+        {
+          /* This is the "~username/" or "~username" case.  */
+          char *user;
+          struct passwd *pwd;
+
+          if (xmode)
+            user = jnlib_xstrdup (first_part+1);
+          else
+            {
+              user = jnlib_strdup (first_part+1);
+              if (!user)
+                return NULL;
+            }
+          p = strchr (user, '/');
+          if (p)
+            *p = 0;
+          skip = 1 + strlen (user);
+
+          /* Fixme: Use getwpnam_r if available.  */
+          pwd = getpwnam (user);
+          jnlib_free (user);
+          if (pwd)
+            {
+              if (xmode)
+                home_buffer = home = jnlib_xstrdup (pwd->pw_dir);
+              else
+                {
+                  home_buffer = home = jnlib_strdup (pwd->pw_dir);
+                  if (!home)
+                    return NULL;
+                }
+            }
+          if (home)
+            n += strlen (home);
+          else
+            skip = 1;
+        }
+#endif /*HAVE_GETPWNAM && HAVE_PWD_H*/
+    }
+
+  if (xmode)
+    name = jnlib_xmalloc (n);
+  else
+    {
+      name = jnlib_malloc (n);
+      if (!name)
+        {
+          jnlib_free (home_buffer);
+          return NULL;
+        }
+    }
+  
+  if (home)
+    p = stpcpy (stpcpy (name, home), first_part + skip);
+  else
+    p = stpcpy (name, first_part);
 
+  jnlib_free (home_buffer);
+
+  for (argc=0; argv[argc]; argc++)
+    p = stpcpy (stpcpy (p, "/"), argv[argc]);
+
+  return change_slashes (name);
+}
 
 /* Construct a filename from the NULL terminated list of parts.  Tilde
-   expansion is done here.  This function terminates the process on
-   memory shortage. */
+   expansion is done for the first argument.  This function terminates
+   the process on memory shortage. */
 char *
 make_filename (const char *first_part, ... )
 {
-  MAKE_FILENAME_PART1
-  name = jnlib_xmalloc (n);
-  MAKE_FILENAME_PART2
+  va_list arg_ptr;
+  char *result;
+
+  va_start (arg_ptr, first_part);
+  result = do_make_filename (1, first_part, arg_ptr);
+  va_end (arg_ptr);
+  return result;
 }
 
 /* Construct a filename from the NULL terminated list of parts.  Tilde
-   expansion is done here.  This function may return NULL on error. */
+   expansion is done for the first argument.  This function may return
+   NULL on error. */
 char *
 make_filename_try (const char *first_part, ... )
 {
-  MAKE_FILENAME_PART1
-  name = jnlib_malloc (n);
-  if (!name)
-    return NULL;
-  MAKE_FILENAME_PART2
+  va_list arg_ptr;
+  char *result;
+
+  va_start (arg_ptr, first_part);
+  result = do_make_filename (0, first_part, arg_ptr);
+  va_end (arg_ptr);
+  return result;
 }
-#undef MAKE_FILENAME_PART1
-#undef MAKE_FILENAME_PART2
 
 
 \f
index 4cd81a1..a560b16 100644 (file)
@@ -37,8 +37,8 @@ size_t length_sans_trailing_ws (const unsigned char *line, size_t len);
 
 char *make_basename(const char *filepath, const char *inputpath);
 char *make_dirname(const char *filepath);
-char *make_filename( const char *first_part, ... );
-char *make_filename_try (const char *first_part, ... );
+char *make_filename( const char *first_part, ... ) GNUPG_GCC_A_SENTINEL(0);
+char *make_filename_try (const char *first_part, ... ) GNUPG_GCC_A_SENTINEL(0);
 int compare_filenames( const char *a, const char *b );
 
 int hextobyte (const char *s);
index 317b020..02041d3 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
 
 #include "stringhelp.h"
 
 #include "t-support.h"
 
 
+static char *home_buffer;
+
+
+const char *
+gethome (void)
+{
+  if (!home_buffer)
+    {
+      char *home = getenv("HOME");
+      
+#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
+      if(home)
+        home_buffer = xstrdup (home);
+      else
+        {
+          struct passwd *pwd;
+          
+          pwd = getpwuid (getuid());
+          if (pwd)
+            home_buffer = xstrdup (pwd->pw_dir);
+        }
+#endif
+    }
+  return home_buffer;
+}
+
 
 static void
 test_percent_escape (void)
@@ -261,6 +292,110 @@ test_xstrconcat (void)
 }
 
 
+static void
+test_make_filename_try (void)
+{
+  char *out;
+  const char *home = gethome ();
+  size_t homelen = home? strlen (home):0;
+
+  out = make_filename_try ("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", "3", NULL);
+  if (out)
+    fail (0);
+  else if (errno != EINVAL)
+    fail (0);
+  xfree (out);
+  out = make_filename_try ("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", "3", "4", NULL);
+  if (out)
+    fail (0);
+  else if (errno != EINVAL)
+    fail (0);
+  xfree (out);
+
+  out = make_filename_try ("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", 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/" 
+                      "1/2"))
+    fail (0);
+  xfree (out);
+
+  out = make_filename_try ("foo", "~/bar", "baz/cde", NULL);
+  if (!out || strcmp (out, "foo/~/bar/baz/cde"))
+    fail (1);
+  xfree (out);
+
+  out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL);
+  if (!out || strcmp (out, "foo/~/bar/baz/cde/"))
+    fail (1);
+  xfree (out);
+
+  out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL);
+  if (!out || strcmp (out, "/foo/~/bar/baz/cde/"))
+    fail (1);
+  xfree (out);
+
+  out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL);
+  if (!out || strcmp (out, "//foo/~/bar/baz/cde/"))
+    fail (1);
+  xfree (out);
+
+  out = make_filename_try ("", "~/bar", "baz/cde", NULL);
+  if (!out || strcmp (out, "/~/bar/baz/cde"))
+    fail (1);
+  xfree (out);
+
+
+  out = make_filename_try ("~/foo", "bar", NULL);
+  if (!out)
+    fail (2);
+  if (home)
+    {
+      if (strlen (out) < homelen + 7)
+        fail (2);
+      if (strncmp (out, home, homelen))
+        fail (2);
+      if (strcmp (out+homelen, "/foo/bar"))
+        fail (2);
+    }
+  else
+    {
+      if (strcmp (out, "~/foo/bar"))
+        fail (2);
+    }
+  xfree (out);
+
+  out = make_filename_try ("~", "bar", NULL);
+  if (!out)
+    fail (2);
+  if (home)
+    {
+      if (strlen (out) < homelen + 3)
+        fail (2);
+      if (strncmp (out, home, homelen))
+        fail (2);
+      if (strcmp (out+homelen, "/bar"))
+        fail (2);
+    }
+  else
+    {
+      if (strcmp (out, "~/bar"))
+        fail (2);
+    }
+  xfree (out);
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -271,7 +406,9 @@ main (int argc, char **argv)
   test_compare_filenames ();
   test_strconcat ();
   test_xstrconcat ();
+  test_make_filename_try ();
 
+  xfree (home_buffer);
   return 0;
 }
 
index 756c54c..d8eba3b 100644 (file)
@@ -56,7 +56,10 @@ gcry_xmalloc (size_t n)
 char *
 gcry_strdup (const char *string)
 {
-  return malloc (strlen (string)+1);
+  char *p = malloc (strlen (string)+1);
+  if (p)
+    strcpy (p, string);
+  return p;
 }