common: Add functions make_absfilename and make_absfilename_try.
authorWerner Koch <wk@gnupg.org>
Tue, 15 Apr 2014 14:40:48 +0000 (16:40 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 22 Apr 2014 13:58:33 +0000 (15:58 +0200)
* common/stringhelp.c (do_make_filename): Add modes 2 and 3.
(make_absfilename): New.
(make_absfilename_try): New.

common/stringhelp.c
common/stringhelp.h
common/t-stringhelp.c

index 7cbf82c..4d7c3a6 100644 (file)
@@ -1,6 +1,7 @@
 /* stringhelp.c -  standard string helper functions
  * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007,
  *               2008, 2009, 2010  Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of JNLIB, which is a subsystem of GnuPG.
  *
@@ -49,9 +50,9 @@
 
 #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
@@ -395,6 +396,12 @@ 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)
 {
@@ -404,6 +411,10 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
   int skip = 1;
   char *home_buffer = NULL;
   char *name, *home, *p;
+  int want_abs;
+
+  want_abs = !!(xmode & 2);
+  xmode &= 1;
 
   n = strlen (first_part) + 1;
   argc = 0;
@@ -478,10 +489,65 @@ do_make_filename (int xmode, const char *first_part, va_list arg_ptr)
     p = stpcpy (name, first_part);
 
   jnlib_free (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 = 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);
+                }
+              jnlib_free (name);
+              return NULL;
+            }
+          n = strlen (home) + 1 + strlen (name) + 1;
+          if (xmode)
+            home_buffer = jnlib_xmalloc (n);
+          else
+            {
+              home_buffer = jnlib_malloc (n);
+              if (!home_buffer)
+                {
+                  jnlib_free (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);
+          jnlib_free (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);
 }
 
@@ -515,6 +581,36 @@ 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
index 21bb20d..1ad380e 100644 (file)
@@ -53,6 +53,9 @@ char *make_basename(const char *filepath, const char *inputpath);
 char *make_dirname(const char *filepath);
 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);
+char *make_absfilename (const char *first_part, ...) GNUPG_GCC_A_SENTINEL(0);
+char *make_absfilename_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 990a800..dcd5a45 100644 (file)
@@ -71,6 +71,34 @@ 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)
 {
@@ -407,6 +435,50 @@ 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);
+  if (strlen (out) < cwdlen + 7)
+    fail (0);
+  if (strncmp (out, cwd, cwdlen))
+    fail (0);
+  if (strcmp (out+cwdlen, "/foo/bar"))
+    fail (0);
+  xfree (out);
+
+  out = make_absfilename_try ("./foo", NULL);
+  if (!out)
+    fail (1);
+  if (strlen (out) < cwdlen + 5)
+    fail (1);
+  if (strncmp (out, cwd, cwdlen))
+    fail (1);
+  if (strcmp (out+cwdlen, "/./foo"))
+    fail (1);
+  xfree (out);
+
+  out = make_absfilename_try (".", NULL);
+  if (!out)
+    fail (2);
+  if (strlen (out) < cwdlen)
+    fail (2);
+  if (strncmp (out, cwd, cwdlen))
+    fail (2);
+  if (strcmp (out+cwdlen, ""))
+    fail (2);
+  xfree (out);
+
+  xfree (cwd);
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -418,6 +490,7 @@ main (int argc, char **argv)
   test_strconcat ();
   test_xstrconcat ();
   test_make_filename_try ();
+  test_make_absfilename_try ();
 
   xfree (home_buffer);
   return 0;