【msh】新增msh自动补全子选项特性 (#8086)

This commit is contained in:
wangqinglin 2023-09-27 12:34:05 +08:00 committed by GitHub
parent d8a2084c05
commit 2d630e38d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 365 additions and 50 deletions

View File

@ -76,4 +76,8 @@ if RT_USING_MSH
int "The number of arguments for a shell command"
default 10
config FINSH_USING_OPTION_COMPLETION
bool "command option completion enable"
default y
endif

View File

@ -39,6 +39,7 @@
#ifdef RT_USING_FINSH
#include <finsh.h>
#define LIST_DFS_OPT_ID 0x100
#define LIST_FIND_OBJ_NR 8
static long clear(void)
@ -903,6 +904,7 @@ long list_device(void)
}
#endif /* RT_USING_DEVICE */
#ifndef FINSH_USING_OPTION_COMPLETION
int cmd_list(int argc, char **argv)
{
if(argc == 2)
@ -1013,6 +1015,95 @@ _usage:
return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_list, list, list objects);
#else
CMD_OPTIONS_STATEMENT(cmd_list)
int cmd_list(int argc, char **argv)
{
if (argc == 2)
{
switch (MSH_OPT_ID_GET(cmd_list))
{
case RT_Object_Class_Thread: list_thread(); break;
case RT_Object_Class_Timer: list_timer(); break;
#ifdef RT_USING_SEMAPHORE
case RT_Object_Class_Semaphore: list_sem(); break;
#endif /* RT_USING_SEMAPHORE */
#ifdef RT_USING_EVENT
case RT_Object_Class_Event: list_event(); break;
#endif /* RT_USING_EVENT */
#ifdef RT_USING_MUTEX
case RT_Object_Class_Mutex: list_mutex(); break;
#endif /* RT_USING_MUTEX */
#ifdef RT_USING_MAILBOX
case RT_Object_Class_MailBox: list_mailbox(); break;
#endif /* RT_USING_MAILBOX */
#ifdef RT_USING_MESSAGEQUEUE
case RT_Object_Class_MessageQueue: list_msgqueue(); break;
#endif /* RT_USING_MESSAGEQUEUE */
#ifdef RT_USING_MEMHEAP
case RT_Object_Class_MemHeap: list_memheap(); break;
#endif /* RT_USING_MEMHEAP */
#ifdef RT_USING_MEMPOOL
case RT_Object_Class_MemPool: list_mempool(); break;
#endif /* RT_USING_MEMPOOL */
#ifdef RT_USING_DEVICE
case RT_Object_Class_Device: list_device(); break;
#endif /* RT_USING_DEVICE */
#ifdef RT_USING_DFS
case LIST_DFS_OPT_ID:
{
extern int list_fd(void);
list_fd();
break;
}
#endif /* RT_USING_DFS */
default:
goto _usage;
break;
};
return 0;
}
_usage:
rt_kprintf("Usage: list [options]\n");
rt_kprintf("[options]:\n");
MSH_OPT_DUMP(cmd_list);
return 0;
}
CMD_OPTIONS_NODE_START(cmd_list)
CMD_OPTIONS_NODE(RT_Object_Class_Thread, thread, list threads)
CMD_OPTIONS_NODE(RT_Object_Class_Timer, timer, list timers)
#ifdef RT_USING_SEMAPHORE
CMD_OPTIONS_NODE(RT_Object_Class_Semaphore, sem, list semaphores)
#endif /* RT_USING_SEMAPHORE */
#ifdef RT_USING_EVENT
CMD_OPTIONS_NODE(RT_Object_Class_Event, event, list events)
#endif /* RT_USING_EVENT */
#ifdef RT_USING_MUTEX
CMD_OPTIONS_NODE(RT_Object_Class_Mutex, mutex, list mutexs)
#endif /* RT_USING_MUTEX */
#ifdef RT_USING_MAILBOX
CMD_OPTIONS_NODE(RT_Object_Class_MailBox, mailbox, list mailboxs)
#endif /* RT_USING_MAILBOX */
#ifdef RT_USING_MESSAGEQUEUE
CMD_OPTIONS_NODE(RT_Object_Class_MessageQueue, msgqueue, list message queues)
#endif /* RT_USING_MESSAGEQUEUE */
#ifdef RT_USING_MEMHEAP
CMD_OPTIONS_NODE(RT_Object_Class_MemHeap, memheap, list memory heaps)
#endif /* RT_USING_MEMHEAP */
#ifdef RT_USING_MEMPOOL
CMD_OPTIONS_NODE(RT_Object_Class_MemPool, mempool, list memory pools)
#endif /* RT_USING_MEMPOOL */
#ifdef RT_USING_DEVICE
CMD_OPTIONS_NODE(RT_Object_Class_Device, device, list devices)
#endif /* RT_USING_DEVICE */
#ifdef RT_USING_DFS
CMD_OPTIONS_NODE(LIST_DFS_OPT_ID, fd, list file descriptors)
#endif /* RT_USING_DFS */
CMD_OPTIONS_NODE_END
#endif /* FINSH_USING_OPTION_COMPLETION */
MSH_CMD_EXPORT_ALIAS(cmd_list, list, list objects, optenable);
#endif /* RT_USING_FINSH */

View File

@ -16,21 +16,35 @@
#pragma section("FSymTab$f",read)
#endif /* _MSC_VER */
#ifdef FINSH_USING_OPTION_COMPLETION
#define FINSH_COND(opt) opt,
#else
#define FINSH_COND(opt)
#endif
#ifdef FINSH_USING_DESCRIPTION
#define FINSH_DESC(cmd, desc) __fsym_##cmd##_desc,
#else
#define FINSH_DESC(cmd, desc)
#endif
typedef long (*syscall_func)(void);
#ifdef FINSH_USING_SYMTAB
#ifdef __TI_COMPILER_VERSION__
#define __TI_FINSH_EXPORT_FUNCTION(f) PRAGMA(DATA_SECTION(f,"FSymTab"))
#endif /* __TI_COMPILER_VERSION__ */
#ifdef FINSH_USING_DESCRIPTION
#ifdef _MSC_VER
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc, opt) \
const char __fsym_##cmd##_name[] = #cmd; \
const char __fsym_##cmd##_desc[] = #desc; \
__declspec(allocate("FSymTab$f")) \
const struct finsh_syscall __fsym_##cmd = \
{ \
__fsym_##cmd##_name, \
__fsym_##cmd##_desc, \
FINSH_DESC(cmd, desc) \
FINSH_COND(opt) \
(syscall_func)&name \
};
#pragma comment(linker, "/merge:FSymTab=mytext")
@ -41,63 +55,55 @@ typedef long (*syscall_func)(void);
#else
#define RT_NOBLOCKED
#endif
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
__TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd); \
const char __fsym_##cmd##_name[] = #cmd; \
const char __fsym_##cmd##_desc[] = #desc; \
rt_used RT_NOBLOCKED const struct finsh_syscall __fsym_##cmd = \
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc, opt) \
__TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd); \
const char __fsym_##cmd##_name[] = #cmd; \
const char __fsym_##cmd##_desc[] = #desc; \
rt_used RT_NOBLOCKED const struct finsh_syscall __fsym_##cmd = \
{ \
__fsym_##cmd##_name, \
__fsym_##cmd##_desc, \
FINSH_DESC(cmd, desc) \
FINSH_COND(opt) \
(syscall_func)&name \
};
#else
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc, opt) \
const char __fsym_##cmd##_name[] rt_section(".rodata.name") = #cmd; \
const char __fsym_##cmd##_desc[] rt_section(".rodata.name") = #desc; \
rt_used const struct finsh_syscall __fsym_##cmd rt_section("FSymTab")= \
{ \
__fsym_##cmd##_name, \
__fsym_##cmd##_desc, \
FINSH_DESC(cmd, desc) \
FINSH_COND(opt) \
(syscall_func)&name \
};
#endif
#else
#ifdef _MSC_VER
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
const char __fsym_##cmd##_name[] = #cmd; \
__declspec(allocate("FSymTab$f")) \
const struct finsh_syscall __fsym_##cmd = \
{ \
__fsym_##cmd##_name, \
(syscall_func)&name \
};
#pragma comment(linker, "/merge:FSymTab=mytext")
#elif defined(__TI_COMPILER_VERSION__)
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
__TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd); \
const char __fsym_##cmd##_name[] = #cmd; \
const struct finsh_syscall __fsym_##cmd = \
{ \
__fsym_##cmd##_name, \
(syscall_func)&name \
};
#else
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
const char __fsym_##cmd##_name[] = #cmd; \
rt_used const struct finsh_syscall __fsym_##cmd rt_section("FSymTab")= \
{ \
__fsym_##cmd##_name, \
(syscall_func)&name \
};
#endif
#endif /* end of FINSH_USING_DESCRIPTION */
#endif /* _MSC_VER */
#endif /* end of FINSH_USING_SYMTAB */
#define __MSH_GET_MACRO(_1, _2, _3, _FUN, ...) _FUN
#define __MSH_GET_EXPORT_MACRO(_1, _2, _3, _4, _FUN, ...) _FUN
#define _MSH_FUNCTION_CMD2(a0, a1) \
MSH_FUNCTION_EXPORT_CMD(a0, a0, a1, 0)
#define _MSH_FUNCTION_CMD3_OPT(a0, a1, a2) \
MSH_FUNCTION_EXPORT_CMD(a0, a0, a1, a0##_msh_options)
#define _MSH_FUNCTION_CMD3_NO_OPT(a0, a1, a2) \
MSH_FUNCTION_EXPORT_CMD(a0, a0, a1, 0)
#define _MSH_FUNCTION_EXPORT_CMD3(a0, a1, a2) \
MSH_FUNCTION_EXPORT_CMD(a0, a1, a2, 0)
#define _MSH_FUNCTION_EXPORT_CMD4_OPT(a0, a1, a2, a3) \
MSH_FUNCTION_EXPORT_CMD(a0, a1, a2, a0##_msh_options)
#define _MSH_FUNCTION_EXPORT_CMD4_NO_OPT(a0, a1, a2, a3) \
MSH_FUNCTION_EXPORT_CMD(a0, a1, a2, 0)
/**
* @ingroup finsh
*
@ -126,9 +132,18 @@ typedef long (*syscall_func)(void);
*
* @param command is the name of the command.
* @param desc is the description of the command, which will show in help list.
* @param opt This is an option, enter any content to enable option completion
*/
#define MSH_CMD_EXPORT(command, desc) \
MSH_FUNCTION_EXPORT_CMD(command, command, desc)
/* MSH_CMD_EXPORT(command, desc) or MSH_CMD_EXPORT(command, desc, opt) */
#ifdef FINSH_USING_OPTION_COMPLETION
#define MSH_CMD_EXPORT(...) \
__MSH_GET_MACRO(__VA_ARGS__, _MSH_FUNCTION_CMD3_OPT, \
_MSH_FUNCTION_CMD2)(__VA_ARGS__)
#else
#define MSH_CMD_EXPORT(...) \
__MSH_GET_MACRO(__VA_ARGS__, _MSH_FUNCTION_CMD3_NO_OPT, \
_MSH_FUNCTION_CMD2)(__VA_ARGS__)
#endif /* FINSH_USING_OPTION_COMPLETION */
/**
* @ingroup msh
@ -138,9 +153,19 @@ typedef long (*syscall_func)(void);
* @param command is the name of the command.
* @param alias is the alias of the command.
* @param desc is the description of the command, which will show in help list.
* @param opt This is an option, enter any content to enable option completion
*/
#define MSH_CMD_EXPORT_ALIAS(command, alias, desc) \
MSH_FUNCTION_EXPORT_CMD(command, alias, desc)
/* #define MSH_CMD_EXPORT_ALIAS(command, alias, desc) or
#define MSH_CMD_EXPORT_ALIAS(command, alias, desc, opt) */
#ifdef FINSH_USING_OPTION_COMPLETION
#define MSH_CMD_EXPORT_ALIAS(...) \
__MSH_GET_EXPORT_MACRO(__VA_ARGS__, _MSH_FUNCTION_EXPORT_CMD4_OPT, \
_MSH_FUNCTION_EXPORT_CMD3)(__VA_ARGS__)
#else
#define MSH_CMD_EXPORT_ALIAS(...) \
__MSH_GET_EXPORT_MACRO(__VA_ARGS__, _MSH_FUNCTION_EXPORT_CMD4_NO_OPT, \
_MSH_FUNCTION_EXPORT_CMD3)(__VA_ARGS__)
#endif /* FINSH_USING_OPTION_COMPLETION */
/* system call table */
struct finsh_syscall
@ -149,6 +174,10 @@ struct finsh_syscall
#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
const char *desc; /* description of system call */
#endif
#ifdef FINSH_USING_OPTION_COMPLETION
struct msh_cmd_opt *opt;
#endif
syscall_func func; /* the function address of system call */
};
@ -159,6 +188,31 @@ struct finsh_syscall_item
struct finsh_syscall syscall; /* syscall */
};
#ifdef FINSH_USING_OPTION_COMPLETION
typedef struct msh_cmd_opt
{
rt_uint32_t id;
const char *name;
const char *des;
} msh_cmd_opt_t;
#define CMD_OPTIONS_STATEMENT(command) static struct msh_cmd_opt command##_msh_options[];
#define CMD_OPTIONS_NODE_START(command) static struct msh_cmd_opt command##_msh_options[] = {
#define CMD_OPTIONS_NODE(_id, _name, _des) {.id = _id, .name = #_name, .des = #_des},
#define CMD_OPTIONS_NODE_END {0},};
void msh_opt_list_dump(void *options);
int msh_cmd_opt_id_get(int argc, char *argv[], void *options);
#define MSH_OPT_ID_GET(fun) msh_cmd_opt_id_get(argc, argv, (void*) fun##_msh_options)
#define MSH_OPT_DUMP(fun) msh_opt_list_dump((void*) fun##_msh_options)
#else
#define CMD_OPTIONS_STATEMENT(command)
#define CMD_OPTIONS_NODE_START(command)
#define CMD_OPTIONS_NODE(_id, _name, _des)
#define CMD_OPTIONS_NODE_END
#endif
extern struct finsh_syscall_item *global_syscall_list;
extern struct finsh_syscall *_syscall_table_begin, *_syscall_table_end;

View File

@ -789,4 +789,162 @@ void msh_auto_complete(char *prefix)
return ;
}
#ifdef FINSH_USING_OPTION_COMPLETION
static msh_cmd_opt_t *msh_get_cmd_opt(char *opt_str)
{
struct finsh_syscall *index;
msh_cmd_opt_t *opt = RT_NULL;
char *ptr;
int len;
if ((ptr = strchr(opt_str, ' ')))
{
len = ptr - opt_str;
}
else
{
len = strlen(opt_str);
}
for (index = _syscall_table_begin;
index < _syscall_table_end;
FINSH_NEXT_SYSCALL(index))
{
if (strncmp(index->name, opt_str, len) == 0 && index->name[len] == '\0')
{
opt = index->opt;
break;
}
}
return opt;
}
static int msh_get_argc(char *prefix, char **last_argv)
{
int argc = 0;
char *ch = prefix;
while (*ch)
{
if ((*ch == ' ') && *(ch + 1) && (*(ch + 1) != ' '))
{
*last_argv = ch + 1;
argc++;
}
ch++;
}
return argc;
}
static void msh_opt_complete(char *opts_str, struct msh_cmd_opt *cmd_opt)
{
struct msh_cmd_opt *opt = cmd_opt;
const char *name_ptr = RT_NULL;
int min_length = 0, length, opts_str_len;
opts_str_len = strlen(opts_str);
for (opt = cmd_opt; opt->id; opt++)
{
if (!strncmp(opt->name, opts_str, opts_str_len))
{
if (min_length == 0)
{
/* set name_ptr */
name_ptr = opt->name;
/* set initial length */
min_length = strlen(name_ptr);
}
length = str_common(name_ptr, opt->name);
if (length < min_length)
{
min_length = length;
}
rt_kprintf("%s\n", opt->name);
}
}
rt_kprintf("\n");
if (name_ptr != NULL)
{
strncpy(opts_str, name_ptr, min_length);
}
}
static void msh_opt_help(msh_cmd_opt_t *cmd_opt)
{
msh_cmd_opt_t *opt = cmd_opt;
for (; opt->id; opt++)
{
rt_kprintf("%-16s - %s\n", opt->name, opt->des);
}
rt_kprintf("\n");
}
void msh_opt_auto_complete(char *prefix)
{
int argc;
char *opt_str = RT_NULL;
msh_cmd_opt_t *opt = RT_NULL;
if ((argc = msh_get_argc(prefix, &opt_str)))
{
opt = msh_get_cmd_opt(prefix);
}
else if (!msh_get_cmd(prefix, strlen(prefix)) && (' ' == prefix[strlen(prefix) - 1]))
{
opt = msh_get_cmd_opt(prefix);
}
if (opt && opt->id)
{
switch (argc)
{
case 0:
msh_opt_help(opt);
break;
case 1:
msh_opt_complete(opt_str, opt);
break;
default:
break;
}
}
}
int msh_cmd_opt_id_get(int argc, char *argv[], void *options)
{
msh_cmd_opt_t *opt = (msh_cmd_opt_t *) options;
int opt_id;
for (opt_id = 0; (argc >= 2) && opt && opt->id; opt++)
{
if (!strcmp(opt->name, argv[1]))
{
opt_id = opt->id;
break;
}
}
return opt_id;
}
void msh_opt_list_dump(void *options)
{
msh_cmd_opt_t *opt = (msh_cmd_opt_t *) options;
for (; opt && opt->id; opt++)
{
rt_kprintf(" %-16s - %s\n", opt->name, opt->des);
}
}
#endif /* FINSH_USING_OPTION_COMPLETION */
#endif /* RT_USING_FINSH */

View File

@ -19,4 +19,8 @@ void msh_auto_complete(char *prefix);
int msh_exec_module(const char *cmd_line, int size);
int msh_exec_script(const char *cmd_line, int size);
#ifdef FINSH_USING_OPTION_COMPLETION
void msh_opt_auto_complete(char *prefix);
#endif /* FINSH_USING_OPTION_COMPLETION */
#endif

View File

@ -381,6 +381,10 @@ static void shell_auto_complete(char *prefix)
rt_kprintf("\n");
msh_auto_complete(prefix);
#ifdef FINSH_USING_OPTION_COMPLETION
msh_opt_auto_complete(prefix);
#endif
rt_kprintf("%s%s", FINSH_PROMPT, prefix);
}