diff --git a/common/stringhelp.c b/common/stringhelp.c index 7cbf82ccc..4d7c3a63b 100644 --- a/common/stringhelp.c +++ b/common/stringhelp.c @@ -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; +} + /* Compare whether the filenames are identical. This is a diff --git a/common/stringhelp.h b/common/stringhelp.h index 21bb20d5a..1ad380e79 100644 --- a/common/stringhelp.h +++ b/common/stringhelp.h @@ -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); diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c index 990a8004a..dcd5a453f 100644 --- a/common/t-stringhelp.c +++ b/common/t-stringhelp.c @@ -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;