git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9275 35acf78f-673a-0410-8e92-d51de3d6d3f4

master
Giovanni Di Sirio 2016-04-10 12:14:23 +00:00
parent 524016ca3a
commit 8dd80c1aa5
3 changed files with 451 additions and 46 deletions

View File

@ -88,11 +88,6 @@ static char *parse_arguments(char *str, char **saveptr) {
return *p != '\0' ? p : NULL;
}
static void usage(BaseSequentialStream *chp, char *p) {
chprintf(chp, "Usage: %s\r\n", p);
}
static void list_commands(BaseSequentialStream *chp, const ShellCommand *scp) {
while (scp->sc_name != NULL) {
@ -114,6 +109,212 @@ static bool cmdexec(const ShellCommand *scp, BaseSequentialStream *chp,
return true;
}
#if (SHELL_USE_HISTORY == TRUE) || defined(__DOXYGEN__)
static void del_histbuff_entry(ShellHistory *shp) {
int pos = shp->sh_beg + *(shp->sh_buffer + shp->sh_beg) + 1;
if (pos >= shp->sh_size)
pos -= shp->sh_size;
shp->sh_beg = pos;
}
static bool is_histbuff_space(ShellHistory *shp, int length) {
if (shp->sh_end >= shp->sh_beg) {
if (length < (shp->sh_size - (shp->sh_end - shp->sh_beg + 1)))
return true;
}
else {
if (length < (shp->sh_beg - shp->sh_end - 1))
return true;
}
return false;
}
static void save_history(ShellHistory *shp, char *line, int length) {
if (length > shp->sh_size - 2)
return;
while ((*(line + length -1) == ' ') && (length > 0))
length--;
if (length <= 0)
return;
while (!is_histbuff_space(shp, length))
del_histbuff_entry(shp);
if (length < shp->sh_size - shp->sh_end - 1)
memcpy(shp->sh_buffer + shp->sh_end + 1, line, length);
else {
/*
* Since there isn't enough room left at the end of the buffer,
* split the line to save up to the end of the buffer and then
* wrap back to the beginning of the buffer.
*/
int part_len = shp->sh_size - shp->sh_end - 1;
memcpy(shp->sh_buffer + shp->sh_end + 1, line, part_len);
memcpy(shp->sh_buffer, line + part_len, length - part_len);
}
/* Save the length of the current line and move the buffer end pointer */
*(shp->sh_buffer + shp->sh_end) = (char)length;
shp->sh_end += length + 1;
if (shp->sh_end >= shp->sh_size)
shp->sh_end -= shp->sh_size;
*(shp->sh_buffer + shp->sh_end) = 0;
shp->sh_cur = 0;
}
static int get_history(ShellHistory *shp, char *line, int dir) {
int count=0;
/* Count the number of lines saved in the buffer */
int idx = shp->sh_beg;
while (idx != shp->sh_end) {
idx += *(shp->sh_buffer + idx) + 1;
if (idx >= shp->sh_size)
idx -= shp->sh_size;
count++;
}
if (dir == SHELL_HIST_DIR_FW) {
if (shp->sh_cur > 0)
shp->sh_cur -= 2;
else
return 0;
}
if (count >= shp->sh_cur) {
idx = shp->sh_beg;
int i = 0;
while (idx != shp->sh_end && shp->sh_cur != (count - i - 1)) {
idx += *(shp->sh_buffer + idx) + 1;
if (idx >= shp->sh_size)
idx -= shp->sh_size;
i++;
}
int length = *(shp->sh_buffer + idx);
if (length > 0) {
shp->sh_cur++;
memset(line, 0, SHELL_MAX_LINE_LENGTH);
if ((idx + length) < shp->sh_size) {
memcpy(line, (shp->sh_buffer + idx + 1), length);
}
else {
/*
* Since the saved line was split at the end of the buffer,
* get the line in two parts.
*/
int part_len = shp->sh_size - idx - 1;
memcpy(line, shp->sh_buffer + idx + 1, part_len);
memcpy(line + part_len, shp->sh_buffer, length - part_len);
}
return length;
}
else if (dir == SHELL_HIST_DIR_FW) {
shp->sh_cur++;
return 0;
}
}
return -1;
}
#endif
#if (SHELL_USE_COMPLETION == TRUE)
static void get_completions(ShellConfig *scfg, char *line) {
ShellCommand *lcp = shell_local_commands;
const ShellCommand *scp = scfg->sc_commands;
char **scmp = scfg->sc_completion;
char help_cmp[] = "help";
if (strstr(help_cmp, line) == help_cmp) {
*scmp++ = help_cmp;
}
while (lcp->sc_name != NULL) {
if (strstr(lcp->sc_name, line) == lcp->sc_name) {
*scmp++ = (char *)lcp->sc_name;
}
lcp++;
}
if (scp != NULL) {
while (scp->sc_name != NULL) {
if (strstr(scp->sc_name, line) == scp->sc_name) {
*scmp++ = (char *)scp->sc_name;
}
scp++;
}
}
*scmp = NULL;
}
static int process_completions(ShellConfig *scfg, char *line, int length, unsigned size) {
char **scmp = scfg->sc_completion;
char **cmp = scmp + 1;
char *c = line + length;
int clen = 0;
if (*scmp != NULL) {
if (*cmp == NULL) {
clen = strlen(*scmp);
int i = length;
while ((c < line + clen) && (c < line + size - 1))
*c++ = *(*scmp + i++);
if (c < line + size -1) {
*c = ' ';
clen++;
}
}
else {
while (*(*scmp + clen) != 0) {
while ((*(*scmp + clen) == *(*cmp + clen)) &&
(*(*cmp + clen) != 0) && (*cmp != NULL)) {
cmp++;
}
if (*cmp == NULL) {
if ((c < line + size - 1) && (clen >= length))
*c++ = *(*scmp + clen);
cmp = scmp + 1;
clen++;
}
else {
break;
}
}
}
*(line + clen) = 0;
}
return clen;
}
static void write_completions(ShellConfig *scfg, char *line, int pos) {
BaseSequentialStream *chp = scfg->sc_channel;
char **scmp = scfg->sc_completion;
if (*(scmp + 1) != NULL) {
chprintf(chp, SHELL_NEWLINE_STR);
while (*scmp != NULL)
chprintf(chp, " %s", *scmp++);
chprintf(chp, SHELL_NEWLINE_STR);
chprintf(chp, SHELL_PROMPT_STR);
chprintf(chp, "%s", line);
}
else {
chprintf(chp, "%s", line + pos);
}
}
#endif
/*===========================================================================*/
/* Module exported functions. */
/*===========================================================================*/
@ -125,17 +326,20 @@ static bool cmdexec(const ShellCommand *scp, BaseSequentialStream *chp,
*/
THD_FUNCTION(shellThread, p) {
int n;
ShellConfig *scfg = p;
BaseSequentialStream *chp = ((ShellConfig *)p)->sc_channel;
const ShellCommand *scp = ((ShellConfig *)p)->sc_commands;
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
char *args[SHELL_MAX_ARGUMENTS + 1];
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
chprintf(chp, SHELL_NEWLINE_STR);
chprintf(chp, "ChibiOS/RT Shell"SHELL_NEWLINE_STR);
while (true) {
chprintf(chp, "ch> ");
if (shellGetLine(chp, line, sizeof(line))) {
chprintf(chp, SHELL_PROMPT_STR);
if (shellGetLine(scfg, line, sizeof(line))) {
#if (SHELL_CMD_EXIT_ENABLED == TRUE) && !defined(_CHIBIOS_NIL_)
chprintf(chp, "\r\nlogout");
chprintf(chp, SHELL_NEWLINE_STR);
chprintf(chp, "logout");
break;
#else
/* Putting a delay in order to avoid an endless loop trying to read
@ -148,7 +352,7 @@ THD_FUNCTION(shellThread, p) {
n = 0;
while ((lp = parse_arguments(NULL, &tokp)) != NULL) {
if (n >= SHELL_MAX_ARGUMENTS) {
chprintf(chp, "too many arguments\r\n");
chprintf(chp, "too many arguments"SHELL_NEWLINE_STR);
cmd = NULL;
break;
}
@ -158,19 +362,19 @@ THD_FUNCTION(shellThread, p) {
if (cmd != NULL) {
if (strcmp(cmd, "help") == 0) {
if (n > 0) {
usage(chp, "help");
shellUsage(chp, "help");
continue;
}
chprintf(chp, "Commands: help ");
list_commands(chp, shell_local_commands);
if (scp != NULL)
list_commands(chp, scp);
chprintf(chp, "\r\n");
chprintf(chp, SHELL_NEWLINE_STR);
}
else if (cmdexec(shell_local_commands, chp, cmd, n, args) &&
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
chprintf(chp, "%s", cmd);
chprintf(chp, " ?\r\n");
chprintf(chp, " ?"SHELL_NEWLINE_STR);
}
}
}
@ -217,7 +421,7 @@ void shellExit(msg_t msg) {
* - Other values below 0x20 are not echoed.
* .
*
* @param[in] chp pointer to a @p BaseSequentialStream object
* @param[in] scfg pointer to a @p ShellConfig object
* @param[in] line pointer to the line buffer
* @param[in] size buffer maximum length
* @return The operation status.
@ -226,14 +430,66 @@ void shellExit(msg_t msg) {
*
* @api
*/
bool shellGetLine(BaseSequentialStream *chp, char *line, unsigned size) {
bool shellGetLine(ShellConfig *scfg, char *line, unsigned size) {
char *p = line;
BaseSequentialStream *chp = scfg->sc_channel;
bool escape = false;
bool bracket = false;
#if (SHELL_USE_HISTORY == TRUE)
ShellHistory *shp = scfg->sc_history;
#endif
while (true) {
char c;
if (streamRead(chp, (uint8_t *)&c, 1) == 0)
return true;
#if (SHELL_USE_ESC_SEQ == TRUE)
if (c == 27) {
escape = true;
continue;
}
if (escape) {
escape = false;
if (c == '[') {
escape = true;
bracket = true;
continue;
}
if (bracket) {
bracket = false;
#if (SHELL_USE_HISTORY == TRUE)
if (c == 'A') {
int len = get_history(shp, line, SHELL_HIST_DIR_BK);
if (len > 0) {
_shell_reset_cur(chp);
_shell_clr_line(chp);
chprintf(chp, "%s", line);
p = line + len;
}
continue;
}
if (c == 'B') {
int len = get_history(shp, line, SHELL_HIST_DIR_FW);
if (len == 0)
*line = 0;
if (len >= 0) {
_shell_reset_cur(chp);
_shell_clr_line(chp);
chprintf(chp, "%s", line);
p = line + len;
}
continue;
}
#endif
}
continue;
}
#endif
#if (SHELL_CMD_EXIT_ENABLED == TRUE) && !defined(_CHIBIOS_NIL_)
if (c == 4) {
chprintf(chp, "^D");
@ -250,10 +506,57 @@ bool shellGetLine(BaseSequentialStream *chp, char *line, unsigned size) {
continue;
}
if (c == '\r') {
chprintf(chp, "\r\n");
chprintf(chp, SHELL_NEWLINE_STR);
#if (SHELL_USE_HISTORY == TRUE)
if (shp != NULL) {
save_history(shp, line, p - line);
}
#endif
*p = 0;
return false;
}
#if (SHELL_USE_COMPLETION == TRUE)
if (c == '\t') {
if (p < line + size - 1) {
*p = 0;
get_completions(scfg, line);
int len = process_completions(scfg, line, p - line, size);
if (len > 0) {
write_completions(scfg, line, p - line);
p = line + len;
}
}
continue;
}
#endif
#if (SHELL_USE_HISTORY == TRUE)
if (c == 14) {
int len = get_history(shp, line, SHELL_HIST_DIR_FW);
if (len == 0)
*line = 0;
if (len >= 0) {
_shell_reset_cur(chp);
_shell_clr_line(chp);
chprintf(chp, "%s", line);
p = line + len;
}
continue;
}
if (c == 16) {
int len = get_history(shp, line, SHELL_HIST_DIR_BK);
if (len > 0) {
_shell_reset_cur(chp);
_shell_clr_line(chp);
chprintf(chp, "%s", line);
p = line + len;
}
continue;
}
#endif
if (c < 0x20)
continue;
if (p < line + size - 1) {

View File

@ -29,6 +29,22 @@
/* Module constants. */
/*===========================================================================*/
/**
* @brief Prompt string
*/
#define SHELL_PROMPT_STR "ch> "
/**
* @brief Newline string
*/
#define SHELL_NEWLINE_STR "\r\n"
/**
* @brief Shell History Constants
*/
#define SHELL_HIST_DIR_BK 0
#define SHELL_HIST_DIR_FW 1
/*===========================================================================*/
/* Module pre-compile time settings. */
/*===========================================================================*/
@ -47,6 +63,41 @@
#define SHELL_MAX_ARGUMENTS 4
#endif
/**
* @brief Shell maximum command history.
*/
#if !defined(SHELL_MAX_HIST_BUFF) || defined(__DOXYGEN__)
#define SHELL_MAX_HIST_BUFF 8 * SHELL_MAX_LINE_LENGTH
#endif
/**
* @brief Enable shell command history
*/
#if !defined(SHELL_USE_HISTORY) || defined(__DOXYGEN__)
#define SHELL_USE_HISTORY TRUE
#endif
/**
* @brief Enable shell command completion
*/
#if !defined(SHELL_USE_COMPLETION) || defined(__DOXYGEN__)
#define SHELL_USE_COMPLETION TRUE
#endif
/**
* @brief Shell Maximum Completions (Set to max commands with common prefix)
*/
#if !defined(SHELL_MAX_COMPLETIONS) || defined(__DOXYGEN__)
#define SHELL_MAX_COMPLETIONS 8
#endif
/**
* @brief Enable shell escape sequence processing
*/
#if !defined(SHELL_USE_ESC_SEQ) || defined(__DOXYGEN__)
#define SHELL_USE_ESC_SEQ TRUE
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
@ -68,6 +119,22 @@ typedef struct {
shellcmd_t sc_function; /**< @brief Command function. */
} ShellCommand;
/**
* @brief Shell history type.
*/
typedef struct {
char *sh_buffer; /**< @brief Buffer to store command
history. */
const int sh_size; /**< @brief Shell history buffer
size. */
int sh_beg; /**< @brief Beginning command index
in buffer. */
int sh_end; /**< @brief Ending command index
in buffer. */
int sh_cur; /**< @brief Currently selected
command in buffer. */
} ShellHistory;
/**
* @brief Shell descriptor type.
*/
@ -76,12 +143,52 @@ typedef struct {
to the shell. */
const ShellCommand *sc_commands; /**< @brief Shell extra commands
table. */
#if (SHELL_USE_HISTORY == TRUE) || defined(__DOXYGEN__)
ShellHistory *sc_history; /**< @brief Shell command history
buffer. */
#endif
#if (SHELL_USE_COMPLETION == TRUE) || defined(__DOXYGEN__)
char **sc_completion; /**< @brief Shell command completion
buffer. */
#endif
} ShellConfig;
/*===========================================================================*/
/* Module macros. */
/*===========================================================================*/
/**
* @brief Send escape codes to move cursor to the beginning of the line
*
* @param[in] stream pointer to a @p BaseSequentialStream object
*
* @notapi
*/
#define _shell_reset_cur(stream) chprintf(stream, "\033[%dD\033[%dC", \
SHELL_MAX_LINE_LENGTH + \
strlen(SHELL_PROMPT_STR) + 2, \
strlen(SHELL_PROMPT_STR))
/**
* @brief Send escape codes to clear the rest of the line
*
* @param[in] stream pointer to a @p BaseSequentialStream object
*
* @notapi
*/
#define _shell_clr_line(stream) chprintf(stream, "\033[K")
/**
* @brief Prints out usage message
*
* @param[in] stream pointer to a @p BaseSequentialStream object
* @param[in] message pointer to message string
*
* @api
*/
#define shellUsage(stream, message) \
chprintf(stream, "Usage: %s"SHELL_NEWLINE_STR, message)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
@ -96,7 +203,7 @@ extern "C" {
void shellInit(void);
THD_FUNCTION(shellThread, p);
void shellExit(msg_t msg);
bool shellGetLine(BaseSequentialStream *chp, char *line, unsigned size);
bool shellGetLine(ShellConfig *scfg, char *line, unsigned size);
#ifdef __cplusplus
}
#endif

View File

@ -54,18 +54,13 @@
/* Module local functions. */
/*===========================================================================*/
static void usage(BaseSequentialStream *chp, char *p) {
chprintf(chp, "Usage: %s\r\n", p);
}
#if ((SHELL_CMD_EXIT_ENABLED == TRUE) && !defined(_CHIBIOS_NIL_)) || \
defined(__DOXYGEN__)
static void cmd_exit(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
usage(chp, "exit");
shellUsage(chp, "exit");
return;
}
@ -78,30 +73,30 @@ static void cmd_info(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
usage(chp, "info");
shellUsage(chp, "info");
return;
}
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
chprintf(chp, "Kernel: %s"SHELL_NEWLINE_STR, CH_KERNEL_VERSION);
#ifdef PORT_COMPILER_NAME
chprintf(chp, "Compiler: %s\r\n", PORT_COMPILER_NAME);
chprintf(chp, "Compiler: %s"SHELL_NEWLINE_STR, PORT_COMPILER_NAME);
#endif
chprintf(chp, "Architecture: %s\r\n", PORT_ARCHITECTURE_NAME);
chprintf(chp, "Architecture: %s"SHELL_NEWLINE_STR, PORT_ARCHITECTURE_NAME);
#ifdef PORT_CORE_VARIANT_NAME
chprintf(chp, "Core Variant: %s\r\n", PORT_CORE_VARIANT_NAME);
chprintf(chp, "Core Variant: %s"SHELL_NEWLINE_STR, PORT_CORE_VARIANT_NAME);
#endif
#ifdef PORT_INFO
chprintf(chp, "Port Info: %s\r\n", PORT_INFO);
chprintf(chp, "Port Info: %s"SHELL_NEWLINE_STR, PORT_INFO);
#endif
#ifdef PLATFORM_NAME
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
chprintf(chp, "Platform: %s"SHELL_NEWLINE_STR, PLATFORM_NAME);
#endif
#ifdef BOARD_NAME
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
chprintf(chp, "Board: %s"SHELL_NEWLINE_STR, BOARD_NAME);
#endif
#ifdef __DATE__
#ifdef __TIME__
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
chprintf(chp, "Build time: %s%s%s"SHELL_NEWLINE_STR, __DATE__, " - ", __TIME__);
#endif
#endif
}
@ -112,10 +107,10 @@ static void cmd_echo(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc != 1) {
usage(chp, "echo \"message\"");
shellUsage(chp, "echo \"message\"");
return;
}
chprintf(chp, "%s\r\n", argv[0]);
chprintf(chp, "%s"SHELL_NEWLINE_STR, argv[0]);
}
#endif
@ -124,10 +119,10 @@ static void cmd_systime(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
usage(chp, "systime");
shellUsage(chp, "systime");
return;
}
chprintf(chp, "%lu\r\n", (unsigned long)chVTGetSystemTime());
chprintf(chp, "%lu"SHELL_NEWLINE_STR, (unsigned long)chVTGetSystemTime());
}
#endif
@ -137,14 +132,14 @@ static void cmd_mem(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
chprintf(chp, "Usage: mem\r\n");
shellUsage(chp, "mem");
return;
}
n = chHeapStatus(NULL, &total, &largest);
chprintf(chp, "core free memory : %u bytes\r\n", chCoreGetStatusX());
chprintf(chp, "heap fragments : %u\r\n", n);
chprintf(chp, "heap free total : %u bytes\r\n", total);
chprintf(chp, "heap free largest: %u bytes\r\n", largest);
chprintf(chp, "core free memory : %u bytes"SHELL_NEWLINE_STR, chCoreGetStatusX());
chprintf(chp, "heap fragments : %u"SHELL_NEWLINE_STR, n);
chprintf(chp, "heap free total : %u bytes"SHELL_NEWLINE_STR, total);
chprintf(chp, "heap free largest: %u bytes"SHELL_NEWLINE_STR, largest);
}
#endif
@ -155,10 +150,10 @@ static void cmd_threads(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
chprintf(chp, "Usage: threads\r\n");
shellUsage(chp, "threads");
return;
}
chprintf(chp, "stklimit stack addr refs prio state name\r\n");
chprintf(chp, "stklimit stack addr refs prio state name\r\n"SHELL_NEWLINE_STR);
tp = chRegFirstThread();
do {
#if CH_DBG_ENABLE_STACK_CHECK == TRUE
@ -166,7 +161,7 @@ static void cmd_threads(BaseSequentialStream *chp, int argc, char *argv[]) {
#else
uint32_t stklimit = 0U;
#endif
chprintf(chp, "%08lx %08lx %08lx %4lu %4lu %9s %12s\r\n",
chprintf(chp, "%08lx %08lx %08lx %4lu %4lu %9s %12s"SHELL_NEWLINE_STR,
stklimit, (uint32_t)tp->ctx.sp, (uint32_t)tp,
(uint32_t)tp->refs - 1, (uint32_t)tp->prio, states[tp->state],
tp->name == NULL ? "" : tp->name);
@ -181,14 +176,14 @@ static void cmd_test(BaseSequentialStream *chp, int argc, char *argv[]) {
(void)argv;
if (argc > 0) {
chprintf(chp, "Usage: test\r\n");
shellUsage(chp, "test");
return;
}
tp = chThdCreateFromHeap(NULL, SHELL_CMD_TEST_WA_SIZE,
"test", chThdGetPriorityX(),
(tfunc_t)test_execute, chp);
if (tp == NULL) {
chprintf(chp, "out of memory\r\n");
chprintf(chp, "out of memory"SHELL_NEWLINE_STR);
return;
}
chThdWait(tp);