From: Rusty Russell Date: Mon, 3 Dec 2012 08:59:39 +0000 (+1030) Subject: tal/str: move tal string functions here from tal. X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=5582b011948769779b0d839d35873c2bc557f9cb tal/str: move tal string functions here from tal. They don't need anything internal to tal, they're just helpers. Signed-off-by: Rusty Russell --- diff --git a/ccan/tal/_info b/ccan/tal/_info index 3f7c22c7..853c6985 100644 --- a/ccan/tal/_info +++ b/ccan/tal/_info @@ -12,27 +12,29 @@ * This allows you to build complex objects based on their lifetimes, eg: * * struct foo *X = tal(NULL, struct foo); - * X->name = tal_strdup(X, "foo"); + * X->val = tal(X, int); * - * and the pointer X->name would be a "child" of the tal context "X"; - * tal_free(X->name) would free X->name as expected, by tal_free(X) would - * free X and X->name. + * and the pointer X->val would be a "child" of the tal context "X"; + * tal_free(X->val) would free X->val as expected, by tal_free(X) would + * free X and X->val. * * With an overhead of approximately 4 pointers per object * (vs. talloc's 12 pointers), it uses dynamic allocation for * destructors and child lists, so those operations can fail. It does * not support talloc's references or failing destructors. * + * See Also: + * ccan/tal/str (useful string helpers) + * * Example: * #include - * #include * #include * #include * * // A structure containing a popened command. * struct command { * FILE *f; - * const char *command; + * char *command; * }; * * // When struct command is freed, we also want to pclose pipe. @@ -43,21 +45,22 @@ * * // This function opens a writable pipe to the given command. * static struct command *open_output_cmd(const tal_t *ctx, - * const char *fmt, ...) + * const char *a0, const char *a1) * { - * va_list ap; * struct command *cmd = tal(ctx, struct command); * * if (!cmd) * return NULL; * - * va_start(ap, fmt); - * cmd->command = tal_vasprintf(cmd, fmt, ap); - * va_end(ap); + * // Note that tal/str has helpers to make this much easier! + * cmd->command = tal_arrz(cmd, char, strlen(a0) + strlen(a1) + 2); * if (!cmd->command) { * tal_free(cmd); * return NULL; * } + * strcat(cmd->command, a0); + * strcat(cmd->command, " "); + * strcat(cmd->command, a1); * * cmd->f = popen(cmd->command, "w"); * if (!cmd->f) { @@ -75,7 +78,7 @@ * if (argc != 2) * errx(1, "Usage: %s \n", argv[0]); * - * cmd = open_output_cmd(NULL, "%s hello", argv[1]); + * cmd = open_output_cmd(NULL, argv[1], "hello"); * if (!cmd) * err(1, "Running '%s hello'", argv[1]); * fprintf(cmd->f, "This is a test\n"); diff --git a/ccan/tal/str/str.c b/ccan/tal/str/str.c index cafb04bb..b7df54c4 100644 --- a/ccan/tal/str/str.c +++ b/ccan/tal/str/str.c @@ -9,10 +9,78 @@ #include #include #include +#include #include #include #include +char *tal_strdup(const tal_t *ctx, const char *p) +{ + /* We have to let through NULL for take(). */ + return tal_dup_(ctx, p, 1, p ? strlen(p) + 1: 1, 0, false, + TAL_LABEL(char, "[]")); +} + +char *tal_strndup(const tal_t *ctx, const char *p, size_t n) +{ + size_t len; + char *ret; + + /* We have to let through NULL for take(). */ + if (likely(p)) { + len = strlen(p); + if (len > n) + len = n; + } else + len = n; + + ret = tal_dup_(ctx, p, 1, len, 1, false, TAL_LABEL(char, "[]")); + if (ret) + ret[len] = '\0'; + return ret; +} + +char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = tal_vasprintf(ctx, fmt, ap); + va_end(ap); + + return ret; +} + +char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) +{ + size_t max; + char *buf; + int ret; + + if (!fmt && taken(fmt)) + return NULL; + + /* A decent guess to start. */ + max = strlen(fmt) * 2; + buf = tal_arr(ctx, char, max); + while (buf) { + va_list ap2; + + va_copy(ap2, ap); + ret = vsnprintf(buf, max, fmt, ap2); + va_end(ap2); + + if (ret < max) + break; + if (!tal_resize(&buf, max *= 2)) + buf = tal_free(buf); + } + if (taken(fmt)) + tal_free(fmt); + return buf; +} + char **tal_strsplit(const tal_t *ctx, const char *string, const char *delims, enum strsplit flags) { diff --git a/ccan/tal/str/str.h b/ccan/tal/str/str.h index f30911e2..ab776e92 100644 --- a/ccan/tal/str/str.h +++ b/ccan/tal/str/str.h @@ -6,6 +6,39 @@ #include #include +/** + * tal_strdup - duplicate a string + * @ctx: NULL, or tal allocated object to be parent. + * @p: the string to copy (can be take()). + */ +char *tal_strdup(const tal_t *ctx, const char *p); + +/** + * tal_strndup - duplicate a limited amount of a string. + * @ctx: NULL, or tal allocated object to be parent. + * @p: the string to copy (can be take()). + * @n: the maximum length to copy. + * + * Always gives a nul-terminated string, with strlen() <= @n. + */ +char *tal_strndup(const tal_t *ctx, const char *p, size_t n); + +/** + * tal_asprintf - allocate a formatted string + * @ctx: NULL, or tal allocated object to be parent. + * @fmt: the printf-style format (can be take()). + */ +char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) PRINTF_FMT(2,3); + +/** + * tal_vasprintf - allocate a formatted string (va_list version) + * @ctx: NULL, or tal allocated object to be parent. + * @fmt: the printf-style format (can be take()). + * @va: the va_list containing the format args. + */ +char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) + PRINTF_FMT(2,0); + enum strsplit { STR_EMPTY_OK, STR_NO_EMPTY diff --git a/ccan/tal/str/test/run-string.c b/ccan/tal/str/test/run-string.c new file mode 100644 index 00000000..4f8899de --- /dev/null +++ b/ccan/tal/str/test/run-string.c @@ -0,0 +1,41 @@ +#include +#include +#include + +int main(void) +{ + char *parent, *c; + + plan_tests(13); + + parent = tal(NULL, char); + ok1(parent); + + c = tal_strdup(parent, "hello"); + ok1(strcmp(c, "hello") == 0); + ok1(tal_parent(c) == parent); + + c = tal_strndup(parent, "hello", 3); + ok1(strcmp(c, "hel") == 0); + ok1(tal_parent(c) == parent); + + c = tal_typechk_(parent, char *); + c = tal_dup(parent, char, "hello", 6, 0); + ok1(strcmp(c, "hello") == 0); + ok1(strcmp(tal_name(c), "char[]") == 0); + ok1(tal_parent(c) == parent); + + /* Now with an extra byte. */ + c = tal_dup(parent, char, "hello", 6, 1); + ok1(strcmp(c, "hello") == 0); + ok1(strcmp(tal_name(c), "char[]") == 0); + ok1(tal_parent(c) == parent); + strcat(c, "x"); + + c = tal_asprintf(parent, "hello %s", "there"); + ok1(strcmp(c, "hello there") == 0); + ok1(tal_parent(c) == parent); + tal_free(parent); + + return exit_status(); +} diff --git a/ccan/tal/str/test/run-take.c b/ccan/tal/str/test/run-take.c new file mode 100644 index 00000000..0cbab9e0 --- /dev/null +++ b/ccan/tal/str/test/run-take.c @@ -0,0 +1,47 @@ +#include +#include +#include + +int main(void) +{ + char *parent, *c; + + plan_tests(14); + + parent = tal(NULL, char); + ok1(parent); + + c = tal_strdup(parent, "hello"); + + c = tal_strdup(parent, take(c)); + ok1(strcmp(c, "hello") == 0); + ok1(tal_parent(c) == parent); + + c = tal_strndup(parent, take(c), 5); + ok1(strcmp(c, "hello") == 0); + ok1(tal_parent(c) == parent); + + c = tal_strndup(parent, take(c), 3); + ok1(strcmp(c, "hel") == 0); + ok1(tal_parent(c) == parent); + tal_free(c); + + c = tal_strdup(parent, "hello %s"); + c = tal_asprintf(parent, take(c), "there"); + ok1(strcmp(c, "hello there") == 0); + ok1(tal_parent(c) == parent); + /* No leftover allocations. */ + tal_free(c); + ok1(tal_first(parent) == NULL); + + tal_free(parent); + ok1(!taken_any()); + + /* NULL pass-through. */ + c = NULL; + ok1(tal_strdup(NULL, take(c)) == NULL); + ok1(tal_strndup(NULL, take(c), 5) == NULL); + ok1(tal_asprintf(NULL, take(c), 0) == NULL); + + return exit_status(); +} diff --git a/ccan/tal/tal.c b/ccan/tal/tal.c index c78185be..4f7d5678 100644 --- a/ccan/tal/tal.c +++ b/ccan/tal/tal.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -713,32 +712,6 @@ bool tal_resize_(tal_t **ctxp, size_t size, size_t count) return true; } -char *tal_strdup(const tal_t *ctx, const char *p) -{ - /* We have to let through NULL for take(). */ - return tal_dup_(ctx, p, 1, p ? strlen(p) + 1: 1, 0, false, - TAL_LABEL(char, "[]")); -} - -char *tal_strndup(const tal_t *ctx, const char *p, size_t n) -{ - size_t len; - char *ret; - - /* We have to let through NULL for take(). */ - if (likely(p)) { - len = strlen(p); - if (len > n) - len = n; - } else - len = n; - - ret = tal_dup_(ctx, p, 1, len, 1, false, TAL_LABEL(char, "[]")); - if (ret) - ret[len] = '\0'; - return ret; -} - void *tal_dup_(const tal_t *ctx, const void *p, size_t size, size_t n, size_t extra, bool add_count, const char *label) @@ -776,47 +749,6 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size, return ret; } -char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) -{ - va_list ap; - char *ret; - - va_start(ap, fmt); - ret = tal_vasprintf(ctx, fmt, ap); - va_end(ap); - - return ret; -} - -char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) -{ - size_t max; - char *buf; - int ret; - - if (!fmt && taken(fmt)) - return NULL; - - /* A decent guess to start. */ - max = strlen(fmt) * 2; - buf = tal_arr(ctx, char, max); - while (buf) { - va_list ap2; - - va_copy(ap2, ap); - ret = vsnprintf(buf, max, fmt, ap2); - va_end(ap2); - - if (ret < max) - break; - if (!tal_resize(&buf, max *= 2)) - buf = tal_free(buf); - } - if (taken(fmt)) - tal_free(fmt); - return buf; -} - void tal_set_backend(void *(*alloc_fn)(size_t size), void *(*resize_fn)(void *, size_t size), void (*free_fn)(void *), diff --git a/ccan/tal/tal.h b/ccan/tal/tal.h index efe21bc5..091eb034 100644 --- a/ccan/tal/tal.h +++ b/ccan/tal/tal.h @@ -286,39 +286,6 @@ tal_t *tal_parent(const tal_t *ctx); sizeof(type), (n), (extra), \ true, TAL_LABEL(type, "[]"))) -/** - * tal_strdup - duplicate a string - * @ctx: NULL, or tal allocated object to be parent. - * @p: the string to copy (can be take()). - */ -char *tal_strdup(const tal_t *ctx, const char *p); - -/** - * tal_strndup - duplicate a limited amount of a string. - * @ctx: NULL, or tal allocated object to be parent. - * @p: the string to copy (can be take()). - * @n: the maximum length to copy. - * - * Always gives a nul-terminated string, with strlen() <= @n. - */ -char *tal_strndup(const tal_t *ctx, const char *p, size_t n); - -/** - * tal_asprintf - allocate a formatted string - * @ctx: NULL, or tal allocated object to be parent. - * @fmt: the printf-style format (can be take()). - */ -char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) PRINTF_FMT(2,3); - -/** - * tal_vasprintf - allocate a formatted string (va_list version) - * @ctx: NULL, or tal allocated object to be parent. - * @fmt: the printf-style format (can be take()). - * @va: the va_list containing the format args. - */ -char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) - PRINTF_FMT(2,0); - /** * tal_set_backend - set the allocation or error functions to use diff --git a/ccan/tal/test/run-string.c b/ccan/tal/test/run-string.c deleted file mode 100644 index 0155ecbe..00000000 --- a/ccan/tal/test/run-string.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -int main(void) -{ - char *parent, *c; - - plan_tests(13); - - parent = tal(NULL, char); - ok1(parent); - - c = tal_strdup(parent, "hello"); - ok1(strcmp(c, "hello") == 0); - ok1(tal_parent(c) == parent); - - c = tal_strndup(parent, "hello", 3); - ok1(strcmp(c, "hel") == 0); - ok1(tal_parent(c) == parent); - - c = tal_typechk_(parent, char *); - c = tal_dup(parent, char, "hello", 6, 0); - ok1(strcmp(c, "hello") == 0); - ok1(strcmp(tal_name(c), "char[]") == 0); - ok1(tal_parent(c) == parent); - - /* Now with an extra byte. */ - c = tal_dup(parent, char, "hello", 6, 1); - ok1(strcmp(c, "hello") == 0); - ok1(strcmp(tal_name(c), "char[]") == 0); - ok1(tal_parent(c) == parent); - strcat(c, "x"); - - c = tal_asprintf(parent, "hello %s", "there"); - ok1(strcmp(c, "hello there") == 0); - ok1(tal_parent(c) == parent); - tal_free(parent); - - return exit_status(); -} diff --git a/ccan/tal/test/run-take.c b/ccan/tal/test/run-take.c index 9f0a22c6..72d2e8e3 100644 --- a/ccan/tal/test/run-take.c +++ b/ccan/tal/test/run-take.c @@ -6,7 +6,7 @@ int main(void) { char *parent, *c; - plan_tests(32); + plan_tests(21); /* We can take NULL. */ ok1(take(NULL) == NULL); @@ -24,20 +24,8 @@ int main(void) ok1(!is_taken(parent)); ok1(!taken(parent)); - c = tal_strdup(parent, "hello"); - - c = tal_strdup(parent, take(c)); - ok1(strcmp(c, "hello") == 0); - ok1(tal_parent(c) == parent); - - c = tal_strndup(parent, take(c), 5); - ok1(strcmp(c, "hello") == 0); - ok1(tal_parent(c) == parent); - - c = tal_strndup(parent, take(c), 3); - ok1(strcmp(c, "hel") == 0); - ok1(tal_parent(c) == parent); - + c = tal(parent, char); + *c = 'h'; c = tal_dup(parent, char, take(c), 1, 0); ok1(c[0] == 'h'); ok1(tal_parent(c) == parent); @@ -56,23 +44,13 @@ int main(void) tal_free(c); ok1(tal_first(parent) == NULL); - c = tal_strdup(parent, "hello %s"); - c = tal_asprintf(parent, take(c), "there"); - ok1(strcmp(c, "hello there") == 0); - ok1(tal_parent(c) == parent); - /* No leftover allocations. */ - tal_free(c); - ok1(tal_first(parent) == NULL); - tal_free(parent); ok1(!taken_any()); /* NULL pass-through. */ c = NULL; - ok1(tal_strdup(NULL, take(c)) == NULL); - ok1(tal_strndup(NULL, take(c), 5) == NULL); ok1(tal_dup(NULL, char, take(c), 5, 5) == NULL); - ok1(tal_asprintf(NULL, take(c), 0) == NULL); + ok1(!taken_any()); return exit_status(); } diff --git a/ccan/tal/test/run-test-backend.c b/ccan/tal/test/run-test-backend.c index 4663a566..66144cb3 100644 --- a/ccan/tal/test/run-test-backend.c +++ b/ccan/tal/test/run-test-backend.c @@ -53,7 +53,8 @@ int main(void) tal_add_destructor(p, destroy_p); tal_set_name(p, "test"); - name = tal_asprintf(NULL, "test2"); + name = tal_arr(NULL, char, 6); + strcpy(name, "test2"); tal_set_name(p, name); /* makes us free old name */ tal_set_name(p, name);