From 6fa8dacb97587707156507aa35141e414fc284bb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 25 Feb 2010 15:58:55 -0500 Subject: Add a tor_asprintf() function, and use it in a couple of places. asprintf() is a GNU extension that some BSDs have picked up: it does a printf into a newly allocated chunk of RAM. Our tor_asprintf() differs from standard asprintf() in that: - Like our other malloc functions, it asserts on OOM. - It works on windows. - It always sets its return-field. --- configure.in | 2 +- src/common/compat.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/common/compat.h | 4 +++ src/or/circuitbuild.c | 14 ++++----- src/test/test_util.c | 46 +++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 8 deletions(-) diff --git a/configure.in b/configure.in index e045c08b3..e94850311 100644 --- a/configure.in +++ b/configure.in @@ -189,7 +189,7 @@ dnl ------------------------------------------------------------------- dnl Check for functions before libevent, since libevent-1.2 apparently dnl exports strlcpy without defining it in a header. -AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl) +AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl vasprintf) using_custom_malloc=no if test x$enable_openbsd_malloc = xyes ; then diff --git a/src/common/compat.c b/src/common/compat.c index 406d74eb2..49e66c6a6 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -307,6 +307,92 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args) return r; } +/** + * Portable asprintf implementation. Does a printf() into a newly malloc'd + * string. Sets *strp to this string, and returns its length (not + * including the terminating NUL character). + * + * You can treat this function as if its implementation were something like +
+     char buf[_INFINITY_];
+     tor_snprintf(buf, sizeof(buf), fmt, args);
+     *strp = tor_strdup(buf);
+     return strlen(*strp):
+   
+ * Where _INFINITY_ is an imaginary constant so big that any string can fit + * into it. + */ +int +tor_asprintf(char **strp, const char *fmt, ...) +{ + int r; + va_list args; + va_start(args, fmt); + r = tor_vasprintf(strp, fmt, args); + va_end(args); + if (!*strp || r < 0) { + log_err(LD_BUG, "Internal error in asprintf"); + tor_assert(0); + } + return r; +} + +/** + * Portable vasprintf implementation. Does a printf() into a newly malloc'd + * string. Differs from regular vasprintf in the same ways that + * tor_asprintf() differs from regular asprintf. + */ +int +tor_vasprintf(char **strp, const char *fmt, va_list args) +{ +#ifdef HAVE_VASPRINTF + /* If the platform gives us one, use it. */ + int r = vasprintf(strp, fmt, args); + if (r < 0) + *strp = NULL; + return r; +#elif defined(MS_WINDOWS) + /* On Windows, _vsnprintf won't tell us the length of the string if it + * overflows, so we need to use _vcsprintf to tell how much to allocate */ + int len, r; + char *res; + len = _vcsprintf(fmt, args); + if (len < 0) { + strp = NULL; + return -1; + } + *strp = tor_malloc(len + 1); + r = _vsnprintf(*strp, len+1, fmt, args); + if (r != len) { + tor_free(*strp); + return -1; + } + return len; +#else + /* Everywhere else, we have a decent vsnprintf that tells us how many + * characters we need. We give it a try on a short buffer first, since + * it might be nice to avoid the second vsnprintf call. + */ + char buf[128]; + int len, r; + va_list tmp_args; + va_copy(tmp_args, args); + len = vsnprintf(buf, sizeof(buf), fmt, tmp_args); + va_end(tmp_args); + if (len < (int)sizeof(buf)) { + *strp = tor_strdup(buf); + return len; + } + *strp = tor_malloc(len+1); + r = vsnprintf(*strp, len+1, fmt, args); + if (r != len) { + tor_free(*strp); + return -1; + } + return len; +#endif +} + /** Given hlen bytes at haystack and nlen bytes at * needle, return a pointer to the first occurrence of the needle * within the haystack, or NULL if there is no such occurrence. diff --git a/src/common/compat.h b/src/common/compat.h index 554ae8919..f3e30452b 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -235,6 +235,10 @@ int tor_snprintf(char *str, size_t size, const char *format, ...) int tor_vsnprintf(char *str, size_t size, const char *format, va_list args) ATTR_NONNULL((1,3)); +int tor_asprintf(char **strp, const char *fmt, ...) + CHECK_PRINTF(2,3); +int tor_vasprintf(char **strp, const char *fmt, va_list args); + const void *tor_memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) ATTR_PURE ATTR_NONNULL((1,3)); static const void *tor_memstr(const void *haystack, size_t hlen, diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 08ee58416..cb9ba9e9b 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1086,21 +1086,21 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) crypt_path_t *hop; smartlist_t *elements; const char *states[] = {"closed", "waiting for keys", "open"}; - char buf[128]; char *s; elements = smartlist_create(); if (verbose) { const char *nickname = build_state_get_exit_nickname(circ->build_state); - tor_snprintf(buf, sizeof(buf), "%s%s circ (length %d%s%s):", + char *cp; + tor_asprintf(&cp, "%s%s circ (length %d%s%s):", circ->build_state->is_internal ? "internal" : "exit", circ->build_state->need_uptime ? " (high-uptime)" : "", circ->build_state->desired_path_len, circ->_base.state == CIRCUIT_STATE_OPEN ? "" : ", exit ", circ->_base.state == CIRCUIT_STATE_OPEN ? "" : (nickname?nickname:"*unnamed*")); - smartlist_add(elements, tor_strdup(buf)); + smartlist_add(elements, cp); } hop = circ->cpath; @@ -3067,21 +3067,21 @@ static void log_entry_guards(int severity) { smartlist_t *elements = smartlist_create(); - char buf[1024]; char *s; SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { const char *msg = NULL; + char *cp; if (entry_is_live(e, 0, 1, 0, &msg)) - tor_snprintf(buf, sizeof(buf), "%s (up %s)", + tor_asprintf(&cp, "%s (up %s)", e->nickname, e->made_contact ? "made-contact" : "never-contacted"); else - tor_snprintf(buf, sizeof(buf), "%s (%s, %s)", + tor_asprintf(&cp, "%s (%s, %s)", e->nickname, msg, e->made_contact ? "made-contact" : "never-contacted"); - smartlist_add(elements, tor_strdup(buf)); + smartlist_add(elements, cp); }); s = smartlist_join_strings(elements, ",", 0, NULL); diff --git a/src/test/test_util.c b/src/test/test_util.c index ad8d82b4c..0e40ae44a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1050,6 +1050,51 @@ test_util_find_str_at_start_of_line(void *ptr) ; } +static void +test_util_asprintf(void *ptr) +{ +#define LOREMIPSUM \ + "Lorem ipsum dolor sit amet, consectetur adipisicing elit" + char *cp=NULL, *cp2=NULL; + int r; + (void)ptr; + + /* empty string. */ + r = tor_asprintf(&cp, "%s", ""); + tt_assert(cp); + tt_int_op(r, ==, strlen(cp)); + tt_str_op(cp, ==, ""); + + /* Short string with some printing in it. */ + r = tor_asprintf(&cp2, "First=%d, Second=%d", 101, 202); + tt_assert(cp2); + tt_int_op(r, ==, strlen(cp2)); + tt_str_op(cp2, ==, "First=101, Second=202"); + tt_assert(cp != cp2); + tor_free(cp); + tor_free(cp2); + + /* Glass-box test: a string exactly 128 characters long. */ + r = tor_asprintf(&cp, "Lorem1: %sLorem2: %s", LOREMIPSUM, LOREMIPSUM); + tt_assert(cp); + tt_int_op(r, ==, 128); + tt_assert(cp[128] == '\0'); + tt_str_op(cp, ==, + "Lorem1: "LOREMIPSUM"Lorem2: "LOREMIPSUM); + tor_free(cp); + + /* String longer than 128 characters */ + r = tor_asprintf(&cp, "1: %s 2: %s 3: %s", + LOREMIPSUM, LOREMIPSUM, LOREMIPSUM); + tt_assert(cp); + tt_int_op(r, ==, strlen(cp)); + tt_str_op(cp, ==, "1: "LOREMIPSUM" 2: "LOREMIPSUM" 3: "LOREMIPSUM); + + done: + tor_free(cp); + tor_free(cp2); +} + #define UTIL_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name } @@ -1071,6 +1116,7 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(sscanf), UTIL_LEGACY(strtok), UTIL_TEST(find_str_at_start_of_line, 0), + UTIL_TEST(asprintf, 0), END_OF_TESTCASES }; -- cgit v1.2.3