Signed-off-by: Pavel Hrdina <phrdina@xxxxxxxxxx> --- cfg.mk | 2 +- src/libvirt_private.syms | 2 ++ src/util/virstring.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 27 +++++++++++++++++++ tests/virstringtest.c | 49 +++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/cfg.mk b/cfg.mk index aaba61f1dc..22c655eac6 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1147,7 +1147,7 @@ exclude_file_name_regexp--sc_prohibit_fork_wrappers = \ exclude_file_name_regexp--sc_prohibit_gethostname = ^src/util/virutil\.c$$ exclude_file_name_regexp--sc_prohibit_internal_functions = \ - ^src/(util/(viralloc|virutil|virfile)\.[hc]|esx/esx_vi\.c)$$ + ^src/(util/(viralloc|virutil|virfile|virstring)\.[hc]|esx/esx_vi\.c)$$ exclude_file_name_regexp--sc_prohibit_newline_at_end_of_diagnostic = \ ^src/rpc/gendispatch\.pl$$ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 07a35333b1..e9c4d73779 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2502,6 +2502,8 @@ virAsprintfInternal; virSkipSpaces; virSkipSpacesAndBackslash; virSkipSpacesBackwards; +virStrcat; +virStrcatInplace; virStrcpy; virStrdup; virStringBufferIsPrintable; diff --git a/src/util/virstring.c b/src/util/virstring.c index 69abc267bf..bc15ce7e9e 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -837,6 +837,76 @@ virStrndup(char **dest, } +/** + * virStrcat + * @dest: where to store concatenated string + * @src: the source string to append to @dest + * @inPlace: false if we should expand the allocated memory before moving, + * true if we should assume someone else has already done that. + * @report: whether to report OOM error, if there is one + * @domcode: error domain code + * @filename: caller's filename + * @funcname: caller's funcname + * @linenr: caller's line number + * + * Wrapper over strcat, which reports OOM error if told so, + * in which case callers wants to pass @domcode, @filename, + * @funcname and @linenr which should represent location in + * caller's body where virStrcat is called from. Consider + * using VIR_STRCAT which sets these automatically. + * + * Returns: 0 for NULL src, 1 on successful concatenate, -1 otherwise. + */ +int +virStrcat(char **dest, + const char *src, + bool report, + int domcode, + const char *filename, + const char *funcname, + size_t linenr) +{ + size_t dest_len = 0; + size_t src_len = 0; + + if (!src) + return 0; + + if (*dest) + dest_len = strlen(*dest); + src_len = strlen(src); + + if (virReallocN(dest, sizeof(*dest), dest_len + src_len + 1, + report, domcode, filename, funcname, linenr) < 0) + return -1; + + strcat(*dest, src); + + return 1; +} + + +/** + * virStrcat + * @dest: where to store concatenated string + * @src: the source string to append to @dest + * + * Wrapper over strcat, which properly handles if @src is NULL. + * + * Returns: 0 for NULL src, 1 on successful concatenate. + */ +int +virStrcatInplace(char *dest, const char *src) +{ + if (!src) + return 0; + + strcat(dest, src); + + return 1; +} + + size_t virStringListLength(const char * const *strings) { size_t i = 0; diff --git a/src/util/virstring.h b/src/util/virstring.h index a5550e30d2..79d2f23c80 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -131,6 +131,13 @@ int virStrdup(char **dest, const char *src, bool report, int domcode, int virStrndup(char **dest, const char *src, ssize_t n, bool report, int domcode, const char *filename, const char *funcname, size_t linenr) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1); + +int virStrcat(char **dest, const char *src, bool report, int domcode, + const char *filename, const char *funcname, size_t linenr) + ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1); +int virStrcatInplace(char *dest, const char *src) + ATTRIBUTE_NONNULL(1); + int virAsprintfInternal(bool report, int domcode, const char *filename, const char *funcname, size_t linenr, char **strp, const char *fmt, ...) @@ -209,6 +216,26 @@ int virVasprintfInternal(bool report, int domcode, const char *filename, # define VIR_STRNDUP_QUIET(dst, src, n) virStrndup(&(dst), src, n, false, \ 0, NULL, NULL, 0) +/** + * VIR_STRCAT: + * @dst: variable to hold result (char*, not char**) + * @src: string to concatenate to @dst + * + * Concatenate @src string into @dst string, @dst may be NULL. + * + * This macro is safe to use on arguments with side effects. + * + * VIR_STRCAT_INPLACE expect the @dst to be already allocated to hold + * the result. + * + * Returns -1 on failure, 0 if @src was NULL, 1 if @src was concatenated. + */ +# define VIR_STRCAT(dst, src) virStrcat(&(dst), src, true, VIR_FROM_THIS, \ + __FILE__, __FUNCTION__, __LINE__) +# define VIR_STRCAT_QUIET(dst, src) virStrcat(&(dst), src, false, 0, \ + NULL, NULL, 0) +# define VIR_STRCAT_INPLACE(dst, src) virStrcatInplace(dst, src) + size_t virStringListLength(const char * const *strings); /** diff --git a/tests/virstringtest.c b/tests/virstringtest.c index db1731f96a..d2d6138326 100644 --- a/tests/virstringtest.c +++ b/tests/virstringtest.c @@ -693,6 +693,37 @@ static int testStripControlChars(const void *args) return ret; } + +struct testStrcatData { + const char *dest; + const char *src; + const char *res; +}; + +static int +testStrcat(const void *args) +{ + const struct testStrcatData *data = args; + char *str = NULL; + int ret = -1; + + if (VIR_STRDUP(str, data->dest) < 0) + goto cleanup; + + if (VIR_STRCAT(str, data->src) < 0) + goto cleanup; + + if (!STREQ_NULLABLE(str, data->res)) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(str); + return ret; +} + + static int mymain(void) { @@ -958,6 +989,24 @@ mymain(void) TEST_STRIP_CONTROL_CHARS("\x01H\x02" "E\x03L\x04L\x05O", "HELLO"); TEST_STRIP_CONTROL_CHARS("\x01\x02\x03\x04HELL\x05O", "HELLO"); TEST_STRIP_CONTROL_CHARS("\nhello \x01\x07hello\t", "\nhello hello\t"); + +#define TEST_STRCAT(dests, srcs, ress) \ + do { \ + struct testStrcatData strcatData = { \ + .dest = dests, \ + .src = srcs, \ + .res = ress, \ + }; \ + if (virTestRun("Concatenate '" #dests "' with '" #srcs "'", \ + testStrcat, &strcatData) < 0) \ + ret = -1; \ + } while (0) + + TEST_STRCAT(NULL, NULL, NULL); + TEST_STRCAT(NULL, "world", "world"); + TEST_STRCAT("hello", NULL, "hello"); + TEST_STRCAT("hello", "world", "helloworld"); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.11.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list