diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index e70e70525..98323b121 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,12 @@ +2009-08-25 Werner Koch + + * stringhelp.c: Include errno.h. + (do_strconcat): New. + (strconcat, xstrconcat): New. + * types.h (GNUPG_GCC_A_SENTINEL): New. + * t-stringhelp.c (test_strconcat, test_xstrconcat): New. + (main): Run them. + 2009-07-07 Werner Koch * stringhelp.c (make_filename_try): Use jnlib_malloc. diff --git a/jnlib/stringhelp.c b/jnlib/stringhelp.c index 3ff743a08..e8a69081f 100644 --- a/jnlib/stringhelp.c +++ b/jnlib/stringhelp.c @@ -1,6 +1,6 @@ /* stringhelp.c - standard string helper functions * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, - * 2006, 2007, 2008 Free Software Foundation, Inc. + * 2006, 2007, 2008, 2009 Free Software Foundation, Inc. * * This file is part of JNLIB. * @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef HAVE_W32_SYSTEM #include #endif @@ -956,3 +957,85 @@ try_percent_escape (const char *str, const char *extra) { return do_percent_escape (str, extra, 0); } + + + +static char * +do_strconcat (const char *s1, va_list arg_ptr) +{ + const char *argv[48]; + size_t argc; + size_t needed; + char *buffer, *p; + + argc = 0; + argv[argc++] = s1; + needed = strlen (s1); + while (((argv[argc] = va_arg (arg_ptr, const char *)))) + { + needed += strlen (argv[argc]); + if (argc >= DIM (argv)-1) + { + errno = EINVAL; + return NULL; + } + argc++; + } + needed++; + buffer = jnlib_malloc (needed); + if (buffer) + { + for (p = buffer, argc=0; argv[argc]; argc++) + p = stpcpy (p, argv[argc]); + } + return buffer; +} + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char * +strconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = jnlib_strdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + return result; +} + +/* Same as strconcat but terminate the process with an error message + if something goes wrong. */ +char * +xstrconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = jnlib_xstrdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (s1, arg_ptr); + va_end (arg_ptr); + } + if (!result) + { + if (errno == EINVAL) + fputs ("\nfatal: too many args for xstrconcat\n", stderr); + else + fputs ("\nfatal: out of memory\n", stderr); + exit (2); + } + return result; +} + diff --git a/jnlib/stringhelp.h b/jnlib/stringhelp.h index 32150f7de..4cd81a1af 100644 --- a/jnlib/stringhelp.h +++ b/jnlib/stringhelp.h @@ -1,6 +1,6 @@ /* stringhelp.h * Copyright (C) 1998, 1999, 2000, 2001, 2003, - * 2006, 2007 Free Software Foundation, Inc. + * 2006, 2007, 2009 Free Software Foundation, Inc. * * This file is part of JNLIB. * @@ -124,4 +124,13 @@ char *percent_escape (const char *str, const char *extra); char *try_percent_escape (const char *str, const char *extra); +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char *strconcat (const char *s1, ...) GNUPG_GCC_A_SENTINEL(0); +/* Ditto, but die on error. */ +char *xstrconcat (const char *s1, ...) GNUPG_GCC_A_SENTINEL(0); + + + #endif /*LIBJNLIB_STRINGHELP_H*/ diff --git a/jnlib/t-stringhelp.c b/jnlib/t-stringhelp.c index f6590230e..317b02049 100644 --- a/jnlib/t-stringhelp.c +++ b/jnlib/t-stringhelp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "stringhelp.h" @@ -118,6 +119,148 @@ test_compare_filenames (void) } +static void +test_strconcat (void) +{ + char *out; + + out = strconcat ("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", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + else + xfree (out); + out = strconcat ("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", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + + out = strconcat ("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", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = strconcat (NULL); + if (!out || *out) + fail (1); +#endif + out = strconcat (NULL, NULL); + if (!out || *out) + fail (1); + out = strconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = strconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = strconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = strconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = strconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + +static void +test_xstrconcat (void) +{ + char *out; + + out = xstrconcat ("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", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = xstrconcat (NULL); + if (!out) + fail (1); +#endif + out = xstrconcat (NULL, NULL); + if (!out) + fail (1); + out = xstrconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = xstrconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = xstrconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = xstrconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = xstrconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = xstrconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + + int main (int argc, char **argv) { @@ -126,6 +269,8 @@ main (int argc, char **argv) test_percent_escape (); test_compare_filenames (); + test_strconcat (); + test_xstrconcat (); return 0; } diff --git a/jnlib/types.h b/jnlib/types.h index 3f64ba58d..62fa0470d 100644 --- a/jnlib/types.h +++ b/jnlib/types.h @@ -99,4 +99,16 @@ # endif #endif + +/* Some GCC attributes. Note that we use also define some in + mischelp.h, but this header and types.h are not always included. + Should eventually be put into one file (e.g. nlib-common.h). */ +#if __GNUC__ >= 4 +# define GNUPG_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a))) +#else +# define GNUPG_GCC_A_SENTINEL(a) +#endif + + + #endif /*LIBJNLIB_TYPES_H*/