1.新增msh组件(类Shell工具)。

This commit is contained in:
MacRsh
2023-12-30 03:51:50 +08:00
parent 2073e3d7fe
commit f2b55fd7b1
5 changed files with 501 additions and 59 deletions

73
Kconfig
View File

@@ -1,26 +1,52 @@
mainmenu "mr-library"
menu "Device configure"
# Heap
config MR_CFG_HEAP_SIZE
int "Heap size (Bytes)"
default 4096
range 32 2147483647
help
"Size of dynamic memory for system."
config MR_CFG_PRINTF_BUFSZ
int "Printf buffer size"
default 128
range 32 2147483647
help
"Size of the buffer used by the printf function."
"This option sets the size of the heap used by the library."
# Assert
config MR_USING_ASSERT
bool "Use assert"
default y
help
"Use this option allows the use of assert statements in the code."
# Console
config MR_USING_CONSOLE
bool "Use console"
default y
help
"Use this option allows for the use of the console."
menu "Console configure"
depends on MR_USING_CONSOLE
config MR_CFG_PRINTF_BUFSZ
int "Printf buffer size"
default 128
range 32 2147483647
help
"This option sets the buffer size used by the printf function."
config MR_CFG_CONSOLE_NAME
string "Console device name"
default "serial1"
help
"This option sets the name of the console device."
config MR_USING_CONSOLE_NONBLOCK
bool "Use console non-blocking"
default n
help
"Use this option allows for the use of the console device in non-blocking mode."
endmenu
# Log
config MR_USING_LOG
bool "Use log"
default y
@@ -67,19 +93,20 @@ menu "Device configure"
"Use this option allows for the use of success log."
endmenu
# Device
config MR_CFG_NAME_MAX
int "Name max length"
default 8
range 4 1024
help
"Maximum length of device name."
"This option sets the max length of the name."
config MR_CFG_DESC_MAX
int "Descriptors max number"
default 64
range 16 1024
help
"Maximum number of descriptors."
"This option sets the max number of descriptors."
config MR_USING_RDWR_CTL
bool "Use read/write control"
@@ -87,28 +114,6 @@ menu "Device configure"
help
"Use this option allows for read and write control of devices."
config MR_USING_CONSOLE
bool "Use console"
default y
help
"Use this option allows for the use of the console device."
menu "Console configure"
depends on MR_USING_CONSOLE
config MR_CFG_CONSOLE_NAME
string "Console name"
default "serial1"
help
"Name of the console device."
config MR_USING_CONSOLE_NONBLOCK
bool "Use console non-blocking"
default n
help
"Use this option allows for the use of the console device in non-blocking mode."
endmenu
config MR_USING_ADC
bool "Use ADC device"
default n
@@ -222,6 +227,8 @@ menu "Device configure"
"Use this option allows for the use of Timer devices."
endmenu
# Driver
source "driver/Kconfig"
source "components/Kconfig"
# Components
source "components/Kconfig"

View File

@@ -1,3 +1,46 @@
menu "No components configure"
menu "Components configure"
# Msh
config MR_USING_MSH
bool "Use msh"
default y
help
"Use this option allows for the use of the shell."
menu "Msh configure"
depends on MR_USING_MSH
config MR_CFG_MSH_BUFSZ
int "Msh buffer size"
default 32
range 16 1024
help
"This option sets the buffer size used by the shell."
config MR_CFG_MSH_NAME_MAX
int "Msh command name max length"
default 8
range 8 64
help
"This option sets the max length of the command name."
config MR_CFG_MSH_ARGS_MAX
int "Msh argument max number"
default 4
range 1 16
help
"This option sets the max number of arguments."
config MR_CFG_MSH_PROMPT
string "Msh prompt"
default "msh>"
help
"This option sets the prompt of the shell."
config MR_USING_MSH_ECHO
bool "Use msh echo"
default y
help
"Use this option allows for the use of the echo in the msh."
endmenu
endmenu

338
components/msh.c Normal file
View File

@@ -0,0 +1,338 @@
/*
* @copyright (c) 2023, MR Development Team
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2023-12-25 MacRsh First version
*/
#include "include/components/msh.h"
#ifdef MR_USING_MSH
/**
* @brief MSH structure.
*/
static struct
{
#ifndef MR_CFG_MSH_BUFSZ
#define MR_CFG_MSH_BUFSZ (32)
#endif /* MR_CFG_MSHELL_BUFSZ */
char buf[MR_CFG_MSH_BUFSZ]; /**< Buffer for reading */
#ifndef MR_CFG_MSH_ARGS_MAX
#define MR_CFG_MSH_ARGS_MAX (8)
#endif /* MR_CFG_MSH_ARGS_MAX */
char *args[MR_CFG_MSH_ARGS_MAX]; /**< Arguments for the msh */
size_t cursor; /**< Cursor position */
size_t bytes; /**< Bytes in the buffer */
char key_buf[8]; /**< Buffer for keys */
size_t key_bytes; /**< Bytes in the key buffer */
int echo; /**< Echo or not */
} msh;
MR_MSH_EXPORT(start, MR_NULL, MR_NULL, "0");
MR_MSH_EXPORT(end, MR_NULL, MR_NULL, "1.end");
#define MSH_CURSOR_FORWARD(x) "\x1b["#x"D"
#define MSH_CURSOR_BACKWARD(x) "\x1b["#x"C"
#define MSH_DELETE_CHAR(x) "\x1b["#x"P"
#define MSH_INSERT_CHAR(x) "\x1b["#x"@"
static void msh_new_line(void)
{
/* Move the cursor to the beginning of the line and print a new line */
if (msh.echo == MR_ENABLE)
{
#ifndef MR_CFG_MSH_PROMPT
#define MR_CFG_MSH_PROMPT "msh>"
#endif /* MR_CFG_MSH_PROMPT */
mr_printf("\r\n"MR_CFG_MSH_PROMPT" ");
}
msh.cursor = 0;
msh.bytes = 0;
msh.key_bytes = 0;
}
static void msh_move_cursor_left(void)
{
if (msh.cursor > 0)
{
msh.cursor--;
mr_printf(MSH_CURSOR_FORWARD(1));
}
}
static void msh_move_cursor_right(void)
{
if (msh.cursor < msh.bytes)
{
msh.cursor++;
mr_printf(MSH_CURSOR_BACKWARD(1));
}
}
static void msh_delete_char(void)
{
if (msh.cursor > 0)
{
/* Move the cursor forward and delete the character */
mr_printf(MSH_CURSOR_FORWARD(1)MSH_DELETE_CHAR(1));
/* Check if you need to remove characters from the middle */
if (msh.cursor != msh.bytes)
{
/* Readjust string */
for (size_t i = msh.cursor; i < msh.bytes; i++)
{
msh.buf[i - 1] = msh.buf[i];
}
}
msh.cursor--;
msh.bytes--;
}
}
static void msh_insert_char(char c)
{
/* Check if there is enough space */
if ((msh.bytes + 1) == MR_CFG_MSH_BUFSZ)
{
return;
}
/* Check whether the cursor is at the end of the buffer */
if (msh.cursor == msh.bytes)
{
msh.buf[msh.bytes] = c;
} else
{
/* Insert the character */
mr_printf(MSH_INSERT_CHAR(1));
/* Readjust string */
for (size_t i = msh.cursor; i < msh.bytes; i++)
{
msh.buf[i + 1] = msh.buf[i];
}
msh.buf[msh.cursor] = c;
}
msh.cursor++;
msh.bytes++;
msh.buf[msh.bytes] = '\0';
/* Echo the character */
if (msh.echo == MR_ENABLE)
{
mr_printf("%c", c);
}
}
static void msh_parse_cmd(void)
{
/* Check whether the buffer is empty */
if (msh.bytes == 0)
{
return;
}
/* Find the command */
char *cmd = strchr(msh.buf, ' ');
if (cmd != MR_NULL)
{
*cmd = '\0';
}
/* Execute the command */
for (const struct mr_msh_cmd *msh_cmd = ((&_mr_msh_cmd_start) + 1); msh_cmd < &_mr_msh_cmd_end; msh_cmd++)
{
if (strcmp(msh_cmd->name, msh.buf) != 0)
{
continue;
}
/* Restore the command */
if (cmd != MR_NULL)
{
*cmd = ' ';
}
/* Parse the arguments */
char *old_arg = msh.buf;
int args_num = 0;
for (args_num = 0; args_num < MR_ARRAY_SIZE(msh.args); args_num++)
{
char *arg = strchr(old_arg, ' ');
if (arg == MR_NULL)
{
break;
}
*arg = '\0';
msh.args[args_num] = ++arg;
old_arg = arg;
}
mr_printf("\r\n");
msh_cmd->call(args_num, msh.args);
}
}
#define KEY_BACKSPACE "\x7f"
#define KEY_ENTER "\r"
#define KEY_LEFT "\x1b[D"
#define KEY_RIGHT "\x1b[C"
#define KEY_DELETE "\x1B[3~"
#define MSH_IS_PRINTABLE(c) ((c) >= 0x20 && (c) <= 0x7e)
#define MSH_IS_ESC_KEY(c) ((c) == 0x1b)
#define MSH_IS_END_KEY(c) \
((((c) >= 'A') && ((c) <= 'D')) || (((c) >= 'P') && ((c) <= 'S')) || ((c) >= '~') || ((c) == 'H') || ((c) == 'F'))
static void msh_key_enter(void)
{
msh_parse_cmd();
msh_new_line();
}
static void msh_key_backspace(void)
{
msh_delete_char();
}
static void msh_key_left(void)
{
msh_move_cursor_left();
}
static void msh_key_right(void)
{
msh_move_cursor_right();
}
static void msh_key_delete(void)
{
msh_move_cursor_right();
msh_delete_char();
}
/**
* @brief Msh long character key map structure.
*/
static struct
{
char *key;
void (*exec)(void);
} msh_key_map[] =
{
{KEY_ENTER, msh_key_enter},
{KEY_BACKSPACE, msh_key_backspace},
{KEY_LEFT, msh_key_left},
{KEY_RIGHT, msh_key_right},
{KEY_DELETE, msh_key_delete},
};
static void msh_parse_key(char c)
{
int parse_flag = MR_DISABLE;
/* key-bytes: 0 -> short character key, 1 -> long character key */
if (msh.key_bytes == 0)
{
if (MSH_IS_ESC_KEY(c))
{
msh.key_buf[msh.key_bytes++] = c;
} else
{
msh.key_buf[msh.key_bytes++] = c;
msh.key_buf[msh.key_bytes] = '\0';
parse_flag = MR_ENABLE;
}
} else
{
if (MSH_IS_END_KEY(c))
{
msh.key_buf[msh.key_bytes++] = c;
msh.key_buf[msh.key_bytes] = '\0';
parse_flag = MR_ENABLE;
} else
{
msh.key_buf[msh.key_bytes++] = c;
/* Check whether the key-buffer is full */
if (msh.key_bytes >= sizeof(msh.key_buf) - 1)
{
msh.key_bytes = 0;
}
}
}
/* Parse the long character key */
if (parse_flag == MR_ENABLE)
{
for (size_t i = 0; i < MR_ARRAY_SIZE(msh_key_map); i++)
{
if (strncmp(msh.key_buf, msh_key_map[i].key, msh.key_bytes) == 0)
{
msh_key_map[i].exec();
break;
}
}
msh.key_bytes = 0;
}
}
static int msh_cmd_help(int argc, void *args)
{
/* Print the help */
for (const struct mr_msh_cmd *msh_cmd = ((&_mr_msh_cmd_start) + 1); msh_cmd < &_mr_msh_cmd_end; msh_cmd++)
{
mr_printf("%-*s - %s\r\n", MR_CFG_MSH_NAME_MAX, msh_cmd->name, msh_cmd->help);
}
return MR_EOK;
}
static int msh_cmd_clear(int argc, void *args)
{
mr_printf("\033c");
msh_new_line();
return MR_EOK;
}
/**
* @brief This function receives a character for the msh.
*
* @param c The character to receive.
*/
void mr_msh_recv_char(char c)
{
/* Judgments are characters and keys */
if (MSH_IS_PRINTABLE(c) && (msh.key_bytes == 0))
{
msh_insert_char(c);
} else
{
msh_parse_key(c);
}
}
/**
* @brief This function initialize the msh.
*
* @return MR_ERR_OK on success, otherwise an error code.
*/
int mr_msh_init(void)
{
#ifdef MR_USING_MSH_ECHO
msh.echo = MR_ENABLE;
#endif /* MR_USING_MSH_ECHO */
/* Print the prompt */
msh_new_line();
return MR_EOK;
}
MR_INIT_DEV_EXPORT(mr_msh_init);
/**
* @brief Exports default MSH commands.
*/
MR_MSH_CMD_EXPORT(help, msh_cmd_help, "Show help information.");
MR_MSH_CMD_EXPORT(clear, msh_cmd_clear, "Clear the screen.");
#endif /* MR_USING_MSH */

77
include/components/msh.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* @copyright (c) 2023, MR Development Team
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2023-12-25 MacRsh First version
*/
#ifndef _MSH_H_
#define _MSH_H_
#include "include/mr_api.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef MR_USING_MSH
/**
* @brief MSH command structure.
*/
struct mr_msh_cmd
{
const char *name; /**< Name */
int (*call)(int argc, void *args); /**< Callback function */
const char *help; /**< Help information */
};
/**
* @brief Exports a MSH command with level.
*
* @param name The name of the command.
* @param fn The callback function.
* @param help The help information.
* @param level The level of the command.
*/
#define MR_MSH_EXPORT(name, fn, help, level) \
MR_USED const struct mr_msh_cmd _mr_msh_cmd_##name MR_SECTION(".msh_cmd."level) = {#name, fn, help};
/**
* @brief Exports a MSH command.
*
* @param name The name of the command.
* @param fn The callback function.
* @param help The help information.
*/
#define MR_MSH_CMD_EXPORT(name, fn, help) \
MR_MSH_EXPORT(name, fn, help, "1")
/**
* @brief Parses the arguments for the MSH command.
*
* @param index The index of the argument.
*
* @note This macro must be called from a function where the first parameter is argc and the second parameter is args.
* 1 -> argc, 2 -> args.
*/
#define MR_MSH_PARSE_ARGS(index) \
(((index) < (argc)) ? (((const char **)(args))[index]) : MR_NULL)
/**
* @addtogroup Msh.
* @{
*/
void mr_msh_recv_char(char c);
/** @} */
#else
#define MR_MSH_EXPORT(name, fn, help, level)
#define MR_MSH_CMD_EXPORT(name, fn, help)
#endif /* MR_USING_MSH */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _MSH_H_ */

View File

@@ -15,40 +15,17 @@
extern "C" {
#endif /* __cplusplus */
#ifdef MR_USING_ADC
#include "device/adc.h"
#endif
#ifdef MR_USING_CAN
#include "device/can.h"
#endif
#ifdef MR_USING_DAC
#include "device/dac.h"
#endif
#ifdef MR_USING_I2C
#include "device/i2c.h"
#ifdef MR_USING_SOFT_I2C
#include "device/soft_i2c.h"
#endif
#endif
#ifdef MR_USING_PIN
#include "device/pin.h"
#endif
#ifdef MR_USING_SERIAL
#include "device/serial.h"
#endif
#ifdef MR_USING_SPI
#include "device/spi.h"
#endif
#ifdef MR_USING_TIMER
#include "device/timer.h"
#endif
#include "components/msh.h"
#ifdef __cplusplus
}