From: Florian Forster Date: Thu, 15 Sep 2016 08:10:30 +0000 (+0200) Subject: Merge branch 'pr/1792' X-Git-Tag: collectd-5.7.0~85 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=10139cda6f68ae1f921d79c930dd85944afb74a3;hp=edc55eb5b933d6b9f30c511d24172ee6a66cefcf;p=collectd.git Merge branch 'pr/1792' --- diff --git a/src/daemon/common.c b/src/daemon/common.c index 7806c716..e2c872cc 100644 --- a/src/daemon/common.c +++ b/src/daemon/common.c @@ -334,50 +334,60 @@ int strsplit (char *string, char **fields, size_t size) return ((int) i); } -int strjoin (char *buffer, size_t buffer_size, - char **fields, size_t fields_num, - const char *sep) -{ - size_t avail; - char *ptr; - size_t sep_len; +int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num, + const char *sep) { + size_t avail = 0; + char *ptr = buffer; + size_t sep_len = 0; - if ((buffer_size < 1) || (fields_num == 0)) - return (-1); + size_t buffer_req = 0; - memset (buffer, 0, buffer_size); - ptr = buffer; - avail = buffer_size - 1; + if (((fields_num != 0) && (fields == NULL)) || + ((buffer_size != 0) && (buffer == NULL))) + return (-EINVAL); - sep_len = 0; - if (sep != NULL) - sep_len = strlen (sep); + if (buffer != NULL) + buffer[0] = 0; - for (size_t i = 0; i < fields_num; i++) - { - size_t field_len; + if (buffer_size != 0) + avail = buffer_size - 1; - if ((i > 0) && (sep_len > 0)) - { - if (avail < sep_len) - return (-1); + if (sep != NULL) + sep_len = strlen(sep); - memcpy (ptr, sep, sep_len); - ptr += sep_len; - avail -= sep_len; - } + for (size_t i = 0; i < fields_num; i++) { + size_t field_len = strlen(fields[i]); - field_len = strlen (fields[i]); - if (avail < field_len) - return (-1); + if (i != 0) + buffer_req += sep_len; + buffer_req += field_len; - memcpy (ptr, fields[i], field_len); - ptr += field_len; - avail -= field_len; - } + if ((i != 0) && (sep_len > 0)) { + if (sep_len >= avail) { + /* prevent subsequent iterations from writing to the + * buffer. */ + avail = 0; + continue; + } + + memcpy(ptr, sep, sep_len); + + ptr += sep_len; + avail -= sep_len; + } + + if (field_len > avail) + field_len = avail; + + memcpy(ptr, fields[i], field_len); + ptr += field_len; + + avail -= field_len; + if (ptr != NULL) + *ptr = 0; + } - assert (buffer[buffer_size - 1] == 0); - return ((int) strlen (buffer)); + return (int)buffer_req; } int escape_string (char *buffer, size_t buffer_size) diff --git a/src/daemon/common.h b/src/daemon/common.h index 9e252932..6d01ca4b 100644 --- a/src/daemon/common.h +++ b/src/daemon/common.h @@ -147,10 +147,12 @@ int strsplit (char *string, char **fields, size_t size); * is equivalent to the Perl built-in `join'. * * PARAMETERS - * `dst' Buffer where the result is stored. + * `dst' Buffer where the result is stored. Can be NULL if you need to + * determine the required buffer size only. * `dst_len' Length of the destination buffer. No more than this many * bytes will be written to the memory pointed to by `dst', - * including the trailing null-byte. + * including the trailing null-byte. Must be zero if dst is + * NULL. * `fields' Array of strings to be joined. * `fields_num' Number of elements in the `fields' array. * `sep' String to be inserted between any two elements of `fields'. @@ -158,9 +160,10 @@ int strsplit (char *string, char **fields, size_t size); * Instead of passing "" (empty string) one can pass NULL. * * RETURN VALUE - * Returns the number of characters in `dst', NOT including the trailing - * null-byte. If an error occurred (empty array or `dst' too small) a value - * smaller than zero will be returned. + * Returns the number of characters in the resulting string, excluding a + * tailing null byte. If this value is greater than or equal to "dst_len", the + * result in "dst" is truncated (but still null terminated). On error a + * negative value is returned. */ int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const char *sep); diff --git a/src/daemon/common_test.c b/src/daemon/common_test.c index 202ddf6c..44e198dd 100644 --- a/src/daemon/common_test.c +++ b/src/daemon/common_test.c @@ -148,46 +148,54 @@ DEF_TEST(strsplit) return (0); } -DEF_TEST(strjoin) -{ - char buffer[16]; - char *fields[4]; - int status; - - fields[0] = "foo"; - fields[1] = "bar"; - fields[2] = "baz"; - fields[3] = "qux"; - - status = strjoin (buffer, sizeof (buffer), fields, 2, "!"); - OK(status == 7); - EXPECT_EQ_STR ("foo!bar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 1, "!"); - OK(status == 3); - EXPECT_EQ_STR ("foo", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 0, "!"); - OK(status < 0); - - status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht"); - OK(status == 10); - EXPECT_EQ_STR ("foorchtbar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 4, ""); - OK(status == 12); - EXPECT_EQ_STR ("foobarbazqux", buffer); +DEF_TEST(strjoin) { + struct { + char **fields; + size_t fields_num; + char *separator; + + int want_return; + char *want_buffer; + } cases + [] = { + /* Normal case. */ + {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"}, + /* One field only. */ + {(char *[]){"foo"}, 1, "!", 3, "foo"}, + /* No fields at all. */ + {NULL, 0, "!", 0, ""}, + /* Longer separator. */ + {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"}, + /* Empty separator. */ + {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"}, + /* NULL separator. */ + {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"}, + /* buffer not large enough -> string is truncated. */ + {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"}, + /* buffer not large enough -> last field fills buffer completely. */ + {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, + "aaaaaaa-bbbbbbb"}, + /* buffer not large enough -> string does *not* end in separator. */ + {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, + "aaaa-bbbb-cccc"}, + /* buffer not large enough -> string does not end with partial + separator. */ + {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char buffer[16]; + int status; - status = strjoin (buffer, sizeof (buffer), fields, 4, "!"); - OK(status == 15); - EXPECT_EQ_STR ("foo!bar!baz!qux", buffer); + memset(buffer, 0xFF, sizeof(buffer)); + status = strjoin(buffer, sizeof(buffer), cases[i].fields, + cases[i].fields_num, cases[i].separator); + EXPECT_EQ_INT(cases[i].want_return, status); + EXPECT_EQ_STR(cases[i].want_buffer, buffer); + } - fields[0] = "0123"; - fields[1] = "4567"; - fields[2] = "8901"; - fields[3] = "2345"; - status = strjoin (buffer, sizeof (buffer), fields, 4, "-"); - OK(status < 0); + /* use (NULL, 0) to determine required buffer size. */ + EXPECT_EQ_INT(3, strjoin(NULL, 0, (char *[]){"a", "b"}, 2, "-")); return (0); }