diff --git a/src/buffer.c b/src/buffer.c index c40d0c7f..1a6fcd25 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -10,8 +10,10 @@ #include #endif +#include #include #include +#include #include "buffer.h" #include "refobject.h" @@ -195,6 +197,73 @@ int buffer_push_string(buffer_t *buffer, const char *string) return buffer_push_data(buffer, string, strlen(string)); } + +int buffer_push_printf(buffer_t *buffer, const char *format, ...) +{ + int ret; + va_list ap; + + if (!buffer || !format) + return -1; + + if (!*format) + return 0; + + va_start(ap, format); + ret = buffer_push_vprintf(buffer, format, ap); + va_end(ap); + + return ret; +} + +int buffer_push_vprintf(buffer_t *buffer, const char *format, va_list ap) +{ + void *buf; + int ret; + size_t length = 1024; + + if (!buffer || !format) + return -1; + + if (!*format) + return 0; + + ret = buffer_zerocopy_push_request(buffer, &buf, length); + if (ret != 0) + return ret; + + ret = vsnprintf(buf, length, format, ap); + if (ret >= 0 && (size_t)ret < length) { + return buffer_zerocopy_push_complete(buffer, ret); + } else if (ret < 0) { + /* This vsnprintf() likely does not follow POSIX. + * We don't know what length we need to asume. So asume a big one and hope for the best. */ + length = 8192; + } else { + /* Reallocate the buffer to the size reported plus one for '\0'-termination */ + length = ret + 1; + } + + /* We have not written any data yet. */ + ret = buffer_zerocopy_push_complete(buffer, 0); + if (ret != 0) + return ret; + + /* Now let's try again. */ + ret = buffer_zerocopy_push_request(buffer, &buf, length); + if (ret != 0) + return ret; + + ret = vsnprintf(buf, length, format, ap); + if (ret < 0 || (size_t)ret >= length) { + /* This still didn't work. Giving up. */ + buffer_zerocopy_push_complete(buffer, 0); + return -1; + } + + return buffer_zerocopy_push_complete(buffer, ret); +} + int buffer_zerocopy_push_request(buffer_t *buffer, void **data, size_t request) { if (!buffer || !data) diff --git a/src/buffer.h b/src/buffer.h index e2f5427e..8e04e2d2 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -18,6 +18,8 @@ #include #endif +#include + #include "icecasttypes.h" #include "compat.h" #include "refobject.h" @@ -127,6 +129,30 @@ int buffer_push_data(buffer_t *buffer, const void *data, size_t length); */ int buffer_push_string(buffer_t *buffer, const char *string); +/* This pushes a formated string to the end of the buffer. + * Parameters: + * buffer + * The buffer to operate on. + * format + * The format string as for printf() family functions. + * ... + * The parameters according to the format string. + */ +int buffer_push_printf(buffer_t *buffer, const char *format, ...); + +/* This pushes a formated string to the end of the buffer using a va_list. + * Parameters: + * buffer + * The buffer to operate on. + * format + * The format string as for printf() family functions. + * ap + * The parameters according to the format string as va_list. + * See also: + * vprintf(3). + */ +int buffer_push_vprintf(buffer_t *buffer, const char *format, va_list ap); + /* This requests for a memory buffer that can be pushed to without the need for copy. * Parameters: * buffer diff --git a/src/tests/ctest_buffer.c b/src/tests/ctest_buffer.c index 5b2998d5..4cda721e 100644 --- a/src/tests/ctest_buffer.c +++ b/src/tests/ctest_buffer.c @@ -281,6 +281,28 @@ static void test_length(void) ctest_test("un-referenced", refobject_unref(a) == 0); } +static void test_printf(void) +{ + buffer_t *a; + const char *str = "Hello World!"; + const int num = -127; + const char *match_a = ":Hello World!:"; + const char *match_b = ":Hello World!:<-127 >"; + const char *match_c = ":Hello World!:<-127 >? +127?"; + + a = buffer_new_simple(); + ctest_test("buffer created", a != NULL); + + ctest_test("Set length to match pattern a", buffer_push_printf(a, ":%s:", str) == 0); + test__compare_to_string(a, "string matches pattern a", match_a); + ctest_test("Set length to match pattern a", buffer_push_printf(a, "<%-5i>", num) == 0); + test__compare_to_string(a, "string matches pattern b", match_b); + ctest_test("Set length to match pattern a", buffer_push_printf(a, "?%+5i?", -num) == 0); + test__compare_to_string(a, "string matches pattern c", match_c); + + ctest_test("un-referenced", refobject_unref(a) == 0); +} + int main (void) { ctest_init(); @@ -299,6 +321,8 @@ int main (void) test_shift(); test_length(); + test_printf(); + ctest_fin(); return 0;