zephyr/subsys/shell/shell_help.c
Jakub Rzeszutko b633e431a4 shell: add select command
The "select" command has been implemented, which allows user to
narrow down the command tree.

This implementation differs from the "select" command available
in the legacy shell. In a new implementation, if the selected
command has a handler and if the user has not entered the
registered subcommand, the shell will call the handler of selected
command and pass the text as arguments.

This may be useful, for example, if someone wants to use the
shell as an interface to a modem that supports AT commands.
Instead of each time you write e.g:
	at at+command1
	at at+command2
	at at+command3
user can execute following commands:
	select at
	at+command1
	at+command2
	at+command3

where:
at - root command for passing at commands to the modem
at+commandX - at command passed to the modem.

Signed-off-by: Jakub Rzeszutko <jakub.rzeszutko@nordicsemi.no>
2019-05-24 11:04:42 +02:00

212 lines
4.9 KiB
C

/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ctype.h>
#include "shell_ops.h"
#include "shell_help.h"
#include "shell_utils.h"
/* Function prints a string on terminal screen with requested margin.
* It takes care to not divide words.
* shell Pointer to shell instance.
* p_str Pointer to string to be printed.
* terminal_offset Requested left margin.
* offset_first_line Add margin to the first printed line.
*/
static void formatted_text_print(const struct shell *shell, const char *str,
size_t terminal_offset, bool offset_first_line)
{
size_t offset = 0;
size_t length;
if (str == NULL) {
return;
}
if (offset_first_line) {
shell_op_cursor_horiz_move(shell, terminal_offset);
}
/* Skipping whitespace. */
while (isspace((int) *(str + offset))) {
++offset;
}
while (true) {
size_t idx = 0;
length = shell_strlen(str) - offset;
if (length <=
shell->ctx->vt100_ctx.cons.terminal_wid - terminal_offset) {
for (idx = 0; idx < length; idx++) {
if (*(str + offset + idx) == '\n') {
transport_buffer_flush(shell);
shell_write(shell, str + offset, idx);
offset += idx + 1;
cursor_next_line_move(shell);
shell_op_cursor_horiz_move(shell,
terminal_offset);
break;
}
}
/* String will fit in one line. */
shell_raw_fprintf(shell->fprintf_ctx, str + offset);
break;
}
/* String is longer than terminal line so text needs to
* divide in the way to not divide words.
*/
length = shell->ctx->vt100_ctx.cons.terminal_wid
- terminal_offset;
while (true) {
/* Determining line break. */
if (isspace((int) (*(str + offset + idx)))) {
length = idx;
if (*(str + offset + idx) == '\n') {
break;
}
}
if ((idx + terminal_offset) >=
shell->ctx->vt100_ctx.cons.terminal_wid) {
/* End of line reached. */
break;
}
++idx;
}
/* Writing one line, fprintf IO buffer must be flushed
* before calling shell_write.
*/
transport_buffer_flush(shell);
shell_write(shell, str + offset, length);
offset += length;
/* Calculating text offset to ensure that next line will
* not begin with a space.
*/
while (isspace((int) (*(str + offset)))) {
++offset;
}
cursor_next_line_move(shell);
shell_op_cursor_horiz_move(shell, terminal_offset);
}
cursor_next_line_move(shell);
}
static void help_item_print(const struct shell *shell, const char *item_name,
u16_t item_name_width, const char *item_help)
{
static const u8_t tabulator[] = " ";
const u16_t offset = 2 * strlen(tabulator) + item_name_width + 1;
if (item_name == NULL) {
return;
}
if (!IS_ENABLED(CONFIG_NEWLIB_LIBC) && !IS_ENABLED(CONFIG_ARCH_POSIX)) {
/* print option name */
shell_internal_fprintf(shell, SHELL_NORMAL, "%s%-*s%s:",
tabulator,
item_name_width, item_name,
tabulator);
} else {
u16_t tmp = item_name_width - strlen(item_name);
char space = ' ';
shell_internal_fprintf(shell, SHELL_NORMAL, "%s%s", tabulator,
item_name);
for (u16_t i = 0; i < tmp; i++) {
shell_write(shell, &space, 1);
}
shell_internal_fprintf(shell, SHELL_NORMAL, "%s:", tabulator);
}
if (item_help == NULL) {
cursor_next_line_move(shell);
return;
}
/* print option help */
formatted_text_print(shell, item_help, offset, false);
}
/* Function is printing command help, its subcommands name and subcommands
* help string.
*/
void shell_help_subcmd_print(const struct shell *shell)
{
const struct shell_static_entry *entry = NULL;
struct shell_static_entry static_entry;
u16_t longest_syntax = 0U;
size_t cmd_idx = 0;
/* Checking if there are any subcommands available. */
if (!shell->ctx->active_cmd.subcmd) {
return;
}
/* Searching for the longest subcommand to print. */
do {
shell_cmd_get(shell, shell->ctx->active_cmd.subcmd,
!SHELL_CMD_ROOT_LVL,
cmd_idx++, &entry, &static_entry);
if (!entry) {
break;
}
u16_t len = shell_strlen(entry->syntax);
longest_syntax = longest_syntax > len ? longest_syntax : len;
} while (cmd_idx != 0); /* too many commands */
if (cmd_idx == 1) {
return;
}
shell_internal_fprintf(shell, SHELL_NORMAL, "Subcommands:\n");
/* Printing subcommands and help string (if exists). */
cmd_idx = 0;
while (true) {
shell_cmd_get(shell, shell->ctx->active_cmd.subcmd,
!SHELL_CMD_ROOT_LVL,
cmd_idx++, &entry, &static_entry);
if (entry == NULL) {
break;
}
help_item_print(shell, entry->syntax, longest_syntax,
entry->help);
}
}
void shell_help_cmd_print(const struct shell *shell)
{
static const char cmd_sep[] = " - "; /* commands separator */
u16_t field_width = shell_strlen(shell->ctx->active_cmd.syntax) +
shell_strlen(cmd_sep);
shell_internal_fprintf(shell, SHELL_NORMAL, "%s%s",
shell->ctx->active_cmd.syntax, cmd_sep);
formatted_text_print(shell, shell->ctx->active_cmd.help,
field_width, false);
}