diff --git a/include/libssh/misc.h b/include/libssh/misc.h index 4f0b9439..3cc3b113 100644 --- a/include/libssh/misc.h +++ b/include/libssh/misc.h @@ -95,5 +95,6 @@ void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len); int ssh_mkdirs(const char *pathname, mode_t mode); int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len); +int ssh_newline_vis(const char *string, char *buf, size_t buf_len); #endif /* MISC_H_ */ diff --git a/src/misc.c b/src/misc.c index bfdf2820..0f1a7d49 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1692,4 +1692,47 @@ error: return SSH_ERROR; } +/** + * @internal + * + * @brief Given a string, encode existing newlines as the string "\\n" + * + * @param[in] string Input string + * @param[out] buf Output buffer. This buffer must be at least (2 * + * strlen(string)) + 1 long. In the worst case, + * each character can be encoded as 2 characters plus the + * terminating '\0'. + * @param[in] buf_len Size of the provided output buffer + * + * @returns SSH_ERROR on error; length of the resulting string not counting the + * terminating '\0' otherwise + */ +int ssh_newline_vis(const char *string, char *buf, size_t buf_len) +{ + const char *in = NULL; + char *out = NULL; + + if (string == NULL || buf == NULL || buf_len == 0) { + return SSH_ERROR; + } + + if ((2 * strlen(string) + 1) > buf_len) { + SSH_LOG(SSH_LOG_WARNING, "Buffer too small"); + return SSH_ERROR; + } + + out = buf; + for (in = string; *in != '\0'; in++) { + if (*in == '\n') { + *out++ = '\\'; + *out++ = 'n'; + } else { + *out++ = *in; + } + } + *out = '\0'; + + return out - buf; +} + /** @} */ diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c index eff93532..ef06d65f 100644 --- a/tests/unittests/torture_misc.c +++ b/tests/unittests/torture_misc.c @@ -637,6 +637,24 @@ static void torture_ssh_quote_file_name(UNUSED_PARAM(void **state)) assert_int_equal(rc, SSH_ERROR); } +static void torture_ssh_newline_vis(UNUSED_PARAM(void **state)) +{ + int rc; + char buffer[1024]; + + rc = ssh_newline_vis("\n", buffer, 1024); + assert_int_equal(rc, 2); + assert_string_equal(buffer, "\\n"); + + rc = ssh_newline_vis("\n\n\n\n", buffer, 1024); + assert_int_equal(rc, 8); + assert_string_equal(buffer, "\\n\\n\\n\\n"); + + rc = ssh_newline_vis("a\nb\n", buffer, 1024); + assert_int_equal(rc, 6); + assert_string_equal(buffer, "a\\nb\\n"); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -656,6 +674,7 @@ int torture_run_tests(void) { cmocka_unit_test(torture_timeout_update), cmocka_unit_test(torture_ssh_analyze_banner), cmocka_unit_test(torture_ssh_dir_writeable), + cmocka_unit_test(torture_ssh_newline_vis), cmocka_unit_test(torture_ssh_mkdirs), cmocka_unit_test(torture_ssh_quote_file_name), };