Skip to content

Commit acef0f1

Browse files
committed
shell: Add shell history feature
Extending shell with terminal-like history feature. Signed-off-by: Krzysztof Chruscinski <[email protected]>
1 parent 0fc9a27 commit acef0f1

File tree

6 files changed

+339
-21
lines changed

6 files changed

+339
-21
lines changed

include/shell/shell.h

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <zephyr.h>
1111
#include <shell/shell_types.h>
12+
#include <shell/shell_history.h>
1213
#include <shell/shell_fprintf.h>
1314
#include <logging/log_backend.h>
1415
#include <logging/log_instance.h>
@@ -339,6 +340,8 @@ struct shell {
339340
const struct shell_transport *iface; /*!< Transport interface.*/
340341
struct shell_ctx *ctx; /*!< Internal context.*/
341342

343+
struct shell_history *history;
344+
342345
const struct shell_fprintf *fprintf_ctx;
343346

344347
LOG_INSTANCE_PTR_DECLARE(log);
@@ -360,26 +363,28 @@ struct shell {
360363
* '\\n' or '\\r'.
361364
* @param[in] log_queue_size Logger processing queue size.
362365
*/
363-
#define SHELL_DEFINE(_name, shell_prefix, transport_iface, \
364-
newline_ch, log_queue_size) \
365-
static const struct shell _name; \
366-
static struct shell_ctx UTIL_CAT(_name, _ctx); \
367-
static u8_t _name##_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; \
368-
SHELL_FPRINTF_DEFINE(_name## _fprintf, &_name, _name##_out_buffer, \
369-
CONFIG_SHELL_PRINTF_BUFF_SIZE, \
370-
true, shell_print_stream); \
371-
LOG_INSTANCE_REGISTER(shell, _name, LOG_LEVEL_NONE); \
372-
static struct k_thread _name##_thread; \
373-
static K_THREAD_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \
374-
static const struct shell _name = { \
375-
.name = shell_prefix, \
376-
.iface = transport_iface, \
377-
.ctx = &UTIL_CAT(_name, _ctx), \
378-
.fprintf_ctx = &_name##_fprintf, \
379-
LOG_INSTANCE_PTR_INIT(log, shell, _name) \
380-
.newline_char = newline_ch, \
381-
.thread = &_name##_thread, \
382-
.stack = _name##_stack \
366+
#define SHELL_DEFINE(_name, shell_prefix, transport_iface, \
367+
newline_ch, log_queue_size) \
368+
static const struct shell _name; \
369+
static struct shell_ctx UTIL_CAT(_name, _ctx); \
370+
static u8_t _name##_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; \
371+
SHELL_HISTORY_DEFINE(_name, 128, 8);/*todo*/ \
372+
SHELL_FPRINTF_DEFINE(_name## _fprintf, &_name, _name##_out_buffer, \
373+
CONFIG_SHELL_PRINTF_BUFF_SIZE, \
374+
true, shell_print_stream); \
375+
LOG_INSTANCE_REGISTER(shell, _name, LOG_LEVEL_NONE); \
376+
static K_THREAD_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE);\
377+
static struct k_thread _name##_thread; \
378+
static const struct shell _name = { \
379+
.name = shell_prefix, \
380+
.iface = transport_iface, \
381+
.ctx = &UTIL_CAT(_name, _ctx), \
382+
.history = SHELL_HISTORY_PTR(_name), \
383+
.fprintf_ctx = &_name##_fprintf, \
384+
LOG_INSTANCE_PTR_INIT(log, shell, _name) \
385+
.newline_char = newline_ch, \
386+
.thread = &_name##_thread, \
387+
.stack = _name##_stack \
383388
}
384389

385390
/*

include/shell/shell_history.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2018 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef SHELL_HISTORY_H__
8+
#define SHELL_HISTORY_H__
9+
10+
#include <zephyr.h>
11+
#include <misc/util.h>
12+
#include <misc/dlist.h>
13+
#include <stdbool.h>
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
20+
struct shell_history {
21+
struct k_mem_slab *mem_slab;
22+
sys_dlist_t list;
23+
sys_dnode_t *current;
24+
};
25+
#if CONFIG_SHELL_HISTORY
26+
#define SHELL_HISTORY_DEFINE(_name, block_size, block_count) \
27+
\
28+
K_MEM_SLAB_DEFINE(_name##_history_memslab, \
29+
block_size, block_count, 4); \
30+
static struct shell_history _name##_history = { \
31+
.mem_slab = &_name##_history_memslab \
32+
}
33+
#define SHELL_HISTORY_PTR(_name) (&_name##_history)
34+
#else /* CONFIG_SHELL_HISTORY */
35+
#define SHELL_HISTORY_DEFINE(_name, block_size, block_count) /*empty*/
36+
#define SHELL_HISTORY_PTR(_name) NULL
37+
#endif
38+
39+
40+
void shell_history_init(struct shell_history *history);
41+
42+
void shell_history_purge(struct shell_history *history);
43+
44+
void shell_history_mode_exit(struct shell_history *history);
45+
46+
/* returns true if remains in history mode.*/
47+
bool shell_history_get(struct shell_history *history, bool up,
48+
u8_t *dst, size_t *len);
49+
50+
void shell_history_put(struct shell_history *history, u8_t *line, size_t len);
51+
52+
static inline bool shell_history_active(struct shell_history *history)
53+
{
54+
return (history->current) ? true : false;
55+
}
56+
57+
#ifdef __cplusplus
58+
}
59+
#endif
60+
61+
#endif /* SHELL_HISTORY_H__ */

subsys/shell/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ zephyr_sources_ifdef(
1717
shell_utils.c
1818
shell_ops.c
1919
shell_uart.c
20-
)
20+
)
21+
22+
zephyr_sources_ifdef(
23+
CONFIG_SHELL_HISTORY
24+
shell_history.c
25+
)

subsys/shell/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,21 @@ config SHELL_HELP_ON_WRONG_ARGUMENT_COUNT
118118
bool "Enable printing help on wrong argument count"
119119
default y
120120

121+
config SHELL_HISTORY
122+
bool "Enable history in shell"
123+
default y
124+
help
125+
Enable commands history. History can be accessed using up and down
126+
arrows
127+
128+
if SHELL_HISTORY
129+
130+
config SHELL_HISTORY_BUFFER
131+
int "History buffer in bytes"
132+
default 1024
133+
help
134+
Number of bytes dedicated for storing executed commands.
135+
136+
endif #SHELL_HISTORY
137+
121138
endif #SHELL

subsys/shell/shell.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,89 @@ static void tab_item_print(const struct shell *shell, const char *option,
265265
shell_op_cursor_horiz_move(shell, diff);
266266
}
267267

268+
static void history_init(const struct shell *shell)
269+
{
270+
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
271+
return;
272+
}
273+
274+
shell_history_init(shell->history);
275+
}
276+
277+
static void history_purge(const struct shell *shell)
278+
{
279+
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
280+
return;
281+
}
282+
283+
shell_history_purge(shell->history);
284+
}
285+
286+
static void history_mode_exit(const struct shell *shell)
287+
{
288+
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
289+
return;
290+
}
291+
292+
shell_history_mode_exit(shell->history);
293+
}
294+
295+
static void history_put(const struct shell *shell, u8_t *line, size_t length)
296+
{
297+
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
298+
return;
299+
}
300+
301+
shell_history_put(shell->history, line, length);
302+
}
303+
304+
static void history_handle(const struct shell *shell, bool up)
305+
{
306+
bool history_mode;
307+
size_t len;
308+
309+
/*optional feature */
310+
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
311+
return;
312+
}
313+
314+
/* Backup command if history is entered */
315+
if (!shell_history_active(shell->history)) {
316+
if (up) {
317+
u16_t cmd_len = shell_strlen(shell->ctx->cmd_buff);
318+
319+
if (cmd_len) {
320+
strcpy(shell->ctx->temp_buff,
321+
shell->ctx->cmd_buff);
322+
} else {
323+
shell->ctx->temp_buff[0] = '\0';
324+
}
325+
} else {
326+
/* Pressing 'down' not in history mode has no effect. */
327+
return;
328+
}
329+
}
330+
331+
/* Start by checking if history is not empty. */
332+
history_mode = shell_history_get(shell->history, true,
333+
shell->ctx->cmd_buff, &len);
334+
335+
/* On exiting history mode print backed up command. */
336+
if (!history_mode) {
337+
strcpy(shell->ctx->cmd_buff, shell->ctx->temp_buff);
338+
len = shell_strlen(shell->ctx->cmd_buff);
339+
}
340+
341+
if (len) {
342+
shell_op_cursor_home_move(shell);
343+
clear_eos(shell);
344+
shell_fprintf(shell, SHELL_NORMAL, "%s", shell->ctx->cmd_buff);
345+
shell->ctx->cmd_buff_pos = len;
346+
shell->ctx->cmd_buff_len = len;
347+
shell_op_cond_next_line(shell);
348+
}
349+
}
350+
268351
static const struct shell_static_entry *find_cmd(
269352
const struct shell_cmd_entry *cmd,
270353
size_t lvl,
@@ -334,6 +417,11 @@ static bool shell_tab_prepare(const struct shell *shell,
334417
return false;
335418
}
336419

420+
/* If the Tab key is pressed, "history mode" must be terminated because
421+
* tab and history handlers are sharing the same array: temp_buff.
422+
*/
423+
history_mode_exit(shell);
424+
337425
/* Copy command from its beginning to cursor position. */
338426
memcpy(shell->ctx->temp_buff, shell->ctx->cmd_buff,
339427
shell->ctx->cmd_buff_pos);
@@ -665,6 +753,7 @@ static void shell_state_collect(const struct shell *shell)
665753
case SHELL_RECEIVE_DEFAULT:
666754
if (data == shell->newline_char) {
667755
if (!shell->ctx->cmd_buff_len) {
756+
history_mode_exit(shell);
668757
cursor_next_line_move(shell);
669758
} else {
670759
/* Command execution */
@@ -732,6 +821,14 @@ static void shell_state_collect(const struct shell *shell)
732821
}
733822

734823
switch (data) {
824+
case 'A': /* UP arrow */
825+
history_handle(shell, true);
826+
break;
827+
828+
case 'B': /* DOWN arrow */
829+
history_handle(shell, false);
830+
break;
831+
735832
case 'C': /* RIGHT arrow */
736833
shell_op_right_arrow(shell);
737834
break;
@@ -838,6 +935,9 @@ static void shell_execute(const struct shell *shell)
838935

839936
cmd_trim(shell);
840937

938+
history_put(shell, shell->ctx->cmd_buff,
939+
shell->ctx->cmd_buff_len);
940+
841941
/* create argument list */
842942
quote = shell_make_argv(&argc, &argv[0], shell->ctx->cmd_buff,
843943
CONFIG_SHELL_ARGC_MAX);
@@ -949,6 +1049,8 @@ static int shell_instance_init(const struct shell *shell, const void *p_config,
9491049
return err;
9501050
}
9511051

1052+
history_init(shell);
1053+
9521054
memset(shell->ctx, 0, sizeof(*shell->ctx));
9531055

9541056
if (IS_ENABLED(CONFIG_SHELL_BACKSPACE_MODE_DELETE)) {
@@ -1049,6 +1151,8 @@ static int shell_instance_uninit(const struct shell *shell)
10491151
return err;
10501152
}
10511153

1154+
history_purge(shell);
1155+
10521156
shell->ctx->state = SHELL_STATE_UNINITIALIZED;
10531157

10541158
return 0;

0 commit comments

Comments
 (0)