summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: f7d1db6)
raw | patch | inline | side by side (parent: f7d1db6)
author | Florian Forster <octo@collectd.org> | |
Thu, 15 Sep 2016 06:59:16 +0000 (08:59 +0200) | ||
committer | Florian Forster <octo@collectd.org> | |
Thu, 15 Sep 2016 07:03:00 +0000 (09:03 +0200) |
This new implementation truncates fields rather than aborting when there
is more space in the output buffer. Since strjoin() is mostly used to
fill plugin and type instances, which are otherwise usually filled with
sstrncpy(), i.e. also truncate the string rather than erroring out.
The unit test has also been rewritten to test the new functionality.
The new functions have been formatted with clang-format.
Fixes: #1792
is more space in the output buffer. Since strjoin() is mostly used to
fill plugin and type instances, which are otherwise usually filled with
sstrncpy(), i.e. also truncate the string rather than erroring out.
The unit test has also been rewritten to test the new functionality.
The new functions have been formatted with clang-format.
Fixes: #1792
src/daemon/common.c | patch | blob | history | |
src/daemon/common.h | patch | blob | history | |
src/daemon/common_test.c | patch | blob | history |
diff --git a/src/daemon/common.c b/src/daemon/common.c
index d1d8bf15dcfcd85124849b7653b3fcee9be560a3..d53f1ec0aa968f63a8130362047cf2b83458f05a 100644 (file)
--- a/src/daemon/common.c
+++ b/src/daemon/common.c
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;
- }
- if (avail == 0)
- return (-1);
+ 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)
- field_len = avail;
+ 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) ((buffer_size - 1) - avail));
+ 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 720e5f1bc0a408f097fe1d586b5bbb26dda9bcce..9a9ae0d3fd30cc019af7da63eedefaa379f1cdd3 100644 (file)
--- a/src/daemon/common.h
+++ b/src/daemon/common.h
* 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'.
* 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);
index 202ddf6cd3ab82aa0af180edf40a44e794908376..44e198ddfafd97ccbcc0abf0b2cffa088b3cb9f8 100644 (file)
--- a/src/daemon/common_test.c
+++ b/src/daemon/common_test.c
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);
}