summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-09-06 15:20:21 +0200
committerThomas Haller <thaller@redhat.com>2018-09-07 11:24:17 +0200
commit0a8248af104c3be18bf6bd3ed8b8b3e88039a3f5 (patch)
treee879a469d2c527b1e46685c92e1415b7bebd9ec3
parent0feeeaac636e39e724a2b3dc848b0fe12858d859 (diff)
downloadNetworkManager-0a8248af104c3be18bf6bd3ed8b8b3e88039a3f5.tar.gz
shared: add nm_utils_strbuf_seek_end() helper
-rw-r--r--shared/nm-utils/nm-shared-utils.c84
-rw-r--r--shared/nm-utils/nm-shared-utils.h1
-rw-r--r--src/tests/test-general.c43
3 files changed, 126 insertions, 2 deletions
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index b8f95f3d62..19b17f68f1 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -106,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
retval = g_vsnprintf (p, *len, format, args);
va_end (args);
- if (retval >= *len) {
+ if ((gsize) retval >= *len) {
*buf = &p[*len];
*len = 0;
} else {
@@ -115,6 +115,88 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
}
}
+/**
+ * nm_utils_strbuf_seek_end:
+ * @buf: the input/output buffer
+ * @len: the input/output lenght of the buffer.
+ *
+ * Commonly, one uses nm_utils_strbuf_append*(), to incrementally
+ * append strings to the buffer. However, sometimes we need to use
+ * existing API to write to the buffer.
+ * After doing so, we want to adjust the buffer counter.
+ * Essentially,
+ *
+ * g_snprintf (buf, len, ...);
+ * nm_utils_strbuf_seek_end (&buf, &len);
+ *
+ * is almost the same as
+ *
+ * nm_utils_strbuf_append (&buf, &len, ...);
+ *
+ * They only behave differently, if the string fits exactly
+ * into the buffer without truncation. The former cannot distinguish
+ * the two cases, while the latter can.
+ */
+void
+nm_utils_strbuf_seek_end (char **buf, gsize *len)
+{
+ gsize l;
+ char *end;
+
+ nm_assert (len);
+ nm_assert (buf && *buf);
+
+ if (*len == 0)
+ return;
+
+ end = memchr (*buf, 0, *len);
+ if (!end) {
+ /* hm, no NUL character within len bytes.
+ * Just NUL terminate the array and consume them
+ * all. */
+ *buf += *len;
+ (*buf)[-1] = '\0';
+ *len = 0;
+ return;
+ }
+
+ l = end - *buf;
+ nm_assert (l < *len);
+
+ *buf = end;
+ *len -= l;
+ if (*len == 1) {
+ /* the last character of a buffer is the '\0'. There are two
+ * cases why that may happen:
+ * - but string was truncated
+ * - the string fit exactly into the buffer.
+ * Here we cannot distinguish between the two, so assume the string
+ * was truncated and signal that by setting @len to 0 and pointing the
+ * buffer *past* the end (like all other nm_utils_strbuf_*() functions).
+ *
+ * Note that nm_utils_strbuf_append_str() can distinguish between
+ * the two cases, and leaves @len at 1, if the string was not actually
+ * truncated.
+ *
+ * For consistancy, it might be better not to do this and just
+ * seek to end of the buffer (not past it). However, that would mean,
+ * in a series of
+ * g_snprintf()
+ * nm_utils_strbuf_seek_end()
+ * the length would never reach zero, but stay at 1. With this,
+ * it reaches len 0 early.
+ * It seems better to declare the buffer as fully consumed and set
+ * the length to zero.
+ *
+ * If the caller does not care about truncation, then this behavior
+ * is more sensible. If the caller cares about truncation, it must
+ * check earlier (right when the truncation occures).
+ */
+ (*buf)++;
+ *len = 0;
+ }
+}
+
/*****************************************************************************/
/**
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 778938e09c..b3099738f3 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -257,6 +257,7 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
+void nm_utils_strbuf_seek_end (char **buf, gsize *len);
const char *nm_strquote (char *buf, gsize buf_len, const char *str);
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index 3b83a66e60..1c8b944512 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -1445,7 +1445,7 @@ test_nm_utils_strbuf_append (void)
t_buf = buf;
t_len = buf_len;
- test_mode = nmtst_get_rand_int () % 4;
+ test_mode = nmtst_get_rand_int () % 5;
switch (test_mode) {
case 0:
@@ -1466,6 +1466,47 @@ test_nm_utils_strbuf_append (void)
case 3:
nm_utils_strbuf_append (&t_buf, &t_len, "%s", str);
break;
+ case 4:
+ g_snprintf (t_buf, t_len, "%s", str);
+ if ( t_len > 0
+ && strlen (str) >= buf_len
+ && (nmtst_get_rand_int () % 2)) {
+ /* the string was truncated by g_snprintf(). That means, at the last position in the
+ * buffer is now NUL.
+ * Replace the NUL by the actual character, and check that nm_utils_strbuf_seek_end()
+ * does the right thing: NUL terminate the buffer and seek past the end of the buffer. */
+ g_assert_cmpmem (t_buf, t_len - 1, str, t_len - 1);
+ g_assert (t_buf[t_len - 1] == '\0');
+ g_assert (str[t_len - 1] != '\0');
+ t_buf[t_len - 1] = str[t_len - 1];
+ nm_utils_strbuf_seek_end (&t_buf, &t_len);
+ g_assert (t_len == 0);
+ g_assert (t_buf == &buf[buf_len]);
+ g_assert (t_buf[-1] == '\0');
+ } else {
+ nm_utils_strbuf_seek_end (&t_buf, &t_len);
+ if (strlen (str) + 1 == buf_len) {
+ /* Special case: we appended a string that fit into the buffer
+ * exactly, without truncation.
+ * If we would append the string via nm_utils_strbuf_append(),
+ * then it would have recognized that the string was not truncated
+ * and leave len==1, and pointing the buffer to the terminating NUL
+ * (at the very end, not past it).
+ *
+ * But nm_utils_strbuf_seek_end() cannot distinguish whether
+ * truncation occured, and assumes the buffer was indeed truncated.
+ *
+ * Assert for that, but also adjust the numbers, so that the assertions
+ * below pass (the assertions below theck for the nm_utils_strbuf_append()
+ * case). */
+ g_assert (t_len == 0);
+ g_assert (t_buf == &buf[buf_len]);
+ g_assert (t_buf[-1] == '\0');
+ t_len = 1;
+ t_buf--;
+ }
+ }
+ break;
}
/* Assert that the source-buffer is unmodified. */