From fc0e10d0648fbe8c93734c8b3f4ff92551cf0416 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 30 Apr 2020 15:25:04 -0600 Subject: [PATCH] shell: Support output using a va_list At present it is not possible to write a printf()-like function in board code which outputs to the shell. Add shell_vfprintf() to permit this. Signed-off-by: Simon Glass --- include/shell/shell.h | 15 +++++++++++++++ subsys/shell/shell.c | 23 +++++++++++++++-------- tests/shell/src/main.c | 30 +++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/include/shell/shell.h b/include/shell/shell.h index c334adb4024..56c374e489d 100644 --- a/include/shell/shell.h +++ b/include/shell/shell.h @@ -726,6 +726,21 @@ int shell_stop(const struct shell *shell); void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, const char *fmt, ...); +/** + * @brief vprintf-like function which sends formatted data stream to the shell. + * + * This function can be used from the command handler or from threads, but not + * from an interrupt context. It is similar to shell_fprintf() but takes a + * va_list instead of variable arguments. + * + * @param[in] shell Pointer to the shell instance. + * @param[in] color Printed text color. + * @param[in] fmt Format string. + * @param[in] args List of parameters to print. + */ +void shell_vfprintf(const struct shell *shell, enum shell_vt100_color color, + const char *fmt, va_list args); + /** * @brief Print data in hexadecimal format. * diff --git a/subsys/shell/shell.c b/subsys/shell/shell.c index b8ecc08d96f..b2a7fd43044 100644 --- a/subsys/shell/shell.c +++ b/subsys/shell/shell.c @@ -1304,8 +1304,8 @@ void shell_process(const struct shell *shell) /* This function mustn't be used from shell context to avoid deadlock. * However it can be used in shell command handlers. */ -void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, - const char *fmt, ...) +void shell_vfprintf(const struct shell *shell, enum shell_vt100_color color, + const char *fmt, va_list args) { __ASSERT_NO_MSG(shell); __ASSERT(!k_is_in_isr(), "Thread context required."); @@ -1315,17 +1315,11 @@ void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, __ASSERT_NO_MSG(shell->fprintf_ctx); __ASSERT_NO_MSG(fmt); - va_list args; - k_mutex_lock(&shell->ctx->wr_mtx, K_FOREVER); if (!flag_cmd_ctx_get(shell)) { shell_cmd_line_erase(shell); } - - va_start(args, fmt); shell_internal_vfprintf(shell, color, fmt, args); - va_end(args); - if (!flag_cmd_ctx_get(shell)) { shell_print_prompt_and_cmd(shell); } @@ -1333,6 +1327,19 @@ void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, k_mutex_unlock(&shell->ctx->wr_mtx); } +/* This function mustn't be used from shell context to avoid deadlock. + * However it can be used in shell command handlers. + */ +void shell_fprintf(const struct shell *shell, enum shell_vt100_color color, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + shell_vfprintf(shell, color, fmt, args); + va_end(args); +} + static void shell_hexdump_line(const struct shell *shell, unsigned int offset, const u8_t *data, size_t len) { diff --git a/tests/shell/src/main.c b/tests/shell/src/main.c index a2152f284e7..31a2ba2fc4c 100644 --- a/tests/shell/src/main.c +++ b/tests/shell/src/main.c @@ -316,6 +316,33 @@ static void test_set_root_cmd(void) test_shell_execute_cmd("shell colors on", 0); } +static void test_shell_fprintf(void) +{ + static const char expect[] = "testing 1 2 3"; + const struct shell *shell; + const char *buf; + size_t size; + + shell = shell_backend_dummy_get_ptr(); + zassert_not_null(shell, "Failed to get shell"); + + /* Clear the output buffer */ + shell_backend_dummy_get_output(shell, &size); + + shell_fprintf(shell, SHELL_VT100_COLOR_DEFAULT, "testing %d %s %c", + 1, "2", '3'); + buf = shell_backend_dummy_get_output(shell, &size); + zassert_true(size >= sizeof(expect), "Expected size > %u, got %d", + sizeof(expect), size); + + /* + * There are prompts and various ANSI characters in the output, so just + * check that the string is in there somewhere. + */ + zassert_true(strstr(buf, expect), + "Expected string to contain '%s', got '%s'", expect, buf); +} + void test_main(void) { ztest_test_suite(shell_test_suite, @@ -328,7 +355,8 @@ void test_main(void) ztest_unit_test(test_cmd_resize), ztest_unit_test(test_shell_module), ztest_unit_test(test_shell_wildcards_static), - ztest_unit_test(test_shell_wildcards_dynamic)); + ztest_unit_test(test_shell_wildcards_dynamic), + ztest_unit_test(test_shell_fprintf)); ztest_run_test_suite(shell_test_suite); }