From 5134fee5b090d68b19924ccad1994ce37732b91c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 26 Aug 2009 08:55:57 +0000 Subject: [PATCH] Implement tilde expansion in the same was as 1.4. --- jnlib/ChangeLog | 12 +++ jnlib/stringhelp.c | 188 +++++++++++++++++++++++++++++++++---------- jnlib/stringhelp.h | 4 +- jnlib/t-stringhelp.c | 137 +++++++++++++++++++++++++++++++ jnlib/t-support.c | 5 +- 5 files changed, 300 insertions(+), 46 deletions(-) diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index 98323b121..2418be901 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,15 @@ +2009-08-26 Werner Koch + + * 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 * stringhelp.c: Include errno.h. diff --git a/jnlib/stringhelp.c b/jnlib/stringhelp.c index e8a69081f..ebdbfc8e1 100644 --- a/jnlib/stringhelp.c +++ b/jnlib/stringhelp.c @@ -24,8 +24,13 @@ #include #include #include +#ifdef HAVE_PWD_H +# include +#endif +#include +#include #ifdef HAVE_W32_SYSTEM -#include +# include #endif #include "libjnlib-config.h" @@ -313,62 +318,159 @@ make_dirname(const char *filepath) -/* 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 diff --git a/jnlib/stringhelp.h b/jnlib/stringhelp.h index 4cd81a1af..a560b163e 100644 --- a/jnlib/stringhelp.h +++ b/jnlib/stringhelp.h @@ -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); diff --git a/jnlib/t-stringhelp.c b/jnlib/t-stringhelp.c index 317b02049..02041d35e 100644 --- a/jnlib/t-stringhelp.c +++ b/jnlib/t-stringhelp.c @@ -22,12 +22,43 @@ #include #include #include +#ifdef HAVE_PWD_H +# include +#endif +#include +#include #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; } diff --git a/jnlib/t-support.c b/jnlib/t-support.c index 756c54c7f..d8eba3b59 100644 --- a/jnlib/t-support.c +++ b/jnlib/t-support.c @@ -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; }