diff options
author | Thomas Haller <thaller@redhat.com> | 2018-09-06 15:20:21 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-09-07 11:24:17 +0200 |
commit | 0a8248af104c3be18bf6bd3ed8b8b3e88039a3f5 (patch) | |
tree | e879a469d2c527b1e46685c92e1415b7bebd9ec3 | |
parent | 0feeeaac636e39e724a2b3dc848b0fe12858d859 (diff) | |
download | NetworkManager-0a8248af104c3be18bf6bd3ed8b8b3e88039a3f5.tar.gz |
shared: add nm_utils_strbuf_seek_end() helper
-rw-r--r-- | shared/nm-utils/nm-shared-utils.c | 84 | ||||
-rw-r--r-- | shared/nm-utils/nm-shared-utils.h | 1 | ||||
-rw-r--r-- | src/tests/test-general.c | 43 |
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. */ |