feat(libcmd): add command processing framework with built-in linenoise
- 纯 C 接口:命令注册/查找/补全/帮助 - CMD_REGISTER / CMD_REGISTER_C 宏(__attribute__((constructor)) 自动注册) - 内置 linenoise 行编辑(termios raw mode + 历史 + Tab 补全) - x86 编译通过,产物 libcmd.a
This commit is contained in:
parent
d9546b7c2a
commit
dfb0a46146
|
|
@ -0,0 +1,26 @@
|
||||||
|
# API-libcmd — 命令处理框架
|
||||||
|
|
||||||
|
> 命令注册/分发/补全,内置 linenoise 行编辑,供 libdatacenter 等模块调试使用。
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
| 函数/宏 | 说明 |
|
||||||
|
|---------|------|
|
||||||
|
| `cmd_manager_add_command(name, func, desc, complete)` | 注册命令 |
|
||||||
|
| `cmd_manager_get_commands(out_count)` | 获取所有已注册命令列表 |
|
||||||
|
| `cmd_help(argc, argv)` | 打印帮助信息 |
|
||||||
|
| `cmd_find(name)` | 按名称查找命令 |
|
||||||
|
| `cmd_complete(buf, completions, ncomp)` | Tab 自动补全回调 |
|
||||||
|
| `cmd_sub_complete(buf, completions, ncomp, subs, sub_count)` | 子命令补全 |
|
||||||
|
| `linenoise(prompt)` | 行编辑+历史交互式输入 |
|
||||||
|
| `linenoiseFree(ptr)` | 释放 linenoise 返回的内存 |
|
||||||
|
| `linenoiseHistoryAdd(line)` | 添加历史记录 |
|
||||||
|
| `linenoiseHistoryFree()` | 释放历史 |
|
||||||
|
| `lineniseSetCompletionCallback(cb)` | 设置自动补全回调 |
|
||||||
|
| `CMD_REGISTER(name, func, desc)` | 注册命令宏(无补全) |
|
||||||
|
| `CMD_REGISTER_C(name, func, desc, complete)` | 注册命令宏(带补全) |
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- `myBase.h`
|
||||||
|
- 无外部依赖(linenoise 内置实现)
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* @file myCmd.h
|
||||||
|
* @brief 命令处理框架 — 纯 C 接口
|
||||||
|
* @details 命令注册/分发/补全,内置 linenoise 行编辑。
|
||||||
|
* 通过 `__attribute__((constructor))` 实现模块自动注册。
|
||||||
|
*/
|
||||||
|
#ifndef _MY_CMD_H_
|
||||||
|
#define _MY_CMD_H_
|
||||||
|
|
||||||
|
#include "myBase.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
void (*func)(int argc, char *argv[]);
|
||||||
|
const char *desc;
|
||||||
|
void (*complete)(const char *buf, char ***completions, int *ncomp);
|
||||||
|
} stru_cmd;
|
||||||
|
|
||||||
|
/** 注册命令 */
|
||||||
|
void cmd_manager_add_command(const char *name, void (*func)(int argc, char *argv[]),
|
||||||
|
const char *desc, void (*complete)(const char *, char ***, int *));
|
||||||
|
|
||||||
|
/** 获取所有已注册命令列表 */
|
||||||
|
stru_cmd *cmd_manager_get_commands(unsigned int *out_count);
|
||||||
|
|
||||||
|
/** 打印帮助信息 */
|
||||||
|
void cmd_help(int argc, char *argv[]);
|
||||||
|
|
||||||
|
/** 按名称查找命令 */
|
||||||
|
stru_cmd *cmd_find(const char *name);
|
||||||
|
|
||||||
|
/** Tab 自动补全回调 */
|
||||||
|
void cmd_complete(const char *buf, char ***completions, int *ncomp);
|
||||||
|
|
||||||
|
/** 子命令补全 */
|
||||||
|
void cmd_sub_complete(const char *buf, char ***completions, int *ncomp,
|
||||||
|
const char **subs, int sub_count);
|
||||||
|
|
||||||
|
/** 行编辑交互式输入 */
|
||||||
|
char *linenoise(const char *prompt);
|
||||||
|
|
||||||
|
/** 释放 linenoise 返回的内存 */
|
||||||
|
void linenoiseFree(void *ptr);
|
||||||
|
|
||||||
|
/** 添加历史记录 */
|
||||||
|
int linenoiseHistoryAdd(const char *line);
|
||||||
|
|
||||||
|
/** 释放所有历史 */
|
||||||
|
void linenoiseHistoryFree(void);
|
||||||
|
|
||||||
|
/** 设置自动补全回调 */
|
||||||
|
void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *));
|
||||||
|
|
||||||
|
/** 注册命令宏(无补全) */
|
||||||
|
#define CMD_REGISTER(cmd_name, func_ptr, cmd_desc) \
|
||||||
|
__attribute__((constructor)) static void register_##func_ptr(void) \
|
||||||
|
{ \
|
||||||
|
cmd_manager_add_command(cmd_name, func_ptr, cmd_desc, NULL); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 注册命令宏(带补全) */
|
||||||
|
#define CMD_REGISTER_C(cmd_name, func_ptr, cmd_desc, complete) \
|
||||||
|
__attribute__((constructor)) static void register_##func_ptr(void) \
|
||||||
|
{ \
|
||||||
|
cmd_manager_add_command(cmd_name, func_ptr, cmd_desc, complete); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
include ./../../../linux.mk
|
||||||
|
L := $(notdir $(realpath $(CURDIR)/..))
|
||||||
|
M := libcmd
|
||||||
|
O := $(LIB_REL)/$(M).a
|
||||||
|
S := $(SRC_ROOT_DIR)/$(L)/libcmd/src
|
||||||
|
I := -I$(SRC_ROOT_DIR)/$(L)/libcmd/inc
|
||||||
|
B := $(CURDIR)/$(M)/obj
|
||||||
|
SRCS := $(wildcard $(S)/*.cpp)
|
||||||
|
OBJS := $(patsubst $(S)/%.cpp, $(B)/%.o, $(SRCS))
|
||||||
|
F := $(CXX_FLAGS) $(I)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all:
|
||||||
|
@mkdir -p $(ROOT_DIR)/release/inc
|
||||||
|
@cp -f $(S:src=inc)/*.h $(ROOT_DIR)/release/inc/ 2>/dev/null; true
|
||||||
|
@$(MAKE) $(O)
|
||||||
|
|
||||||
|
$(O): $(OBJS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(AR) rcs $@ $^
|
||||||
|
@echo "[$(M)] built"
|
||||||
|
|
||||||
|
$(B)/%.o: $(S)/%.cpp
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(F) -c $< -o $@
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(B) $(O)
|
||||||
|
|
||||||
|
.PHONY: rebuild
|
||||||
|
rebuild: clean all
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* @file myCmd.h
|
||||||
|
* @brief 命令处理框架 — 纯 C 接口
|
||||||
|
* @details 命令注册/分发/补全,内置 linenoise 行编辑。
|
||||||
|
* 通过 `__attribute__((constructor))` 实现模块自动注册。
|
||||||
|
*/
|
||||||
|
#ifndef _MY_CMD_H_
|
||||||
|
#define _MY_CMD_H_
|
||||||
|
|
||||||
|
#include "myBase.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
void (*func)(int argc, char *argv[]);
|
||||||
|
const char *desc;
|
||||||
|
void (*complete)(const char *buf, char ***completions, int *ncomp);
|
||||||
|
} stru_cmd;
|
||||||
|
|
||||||
|
/** 注册命令 */
|
||||||
|
void cmd_manager_add_command(const char *name, void (*func)(int argc, char *argv[]),
|
||||||
|
const char *desc, void (*complete)(const char *, char ***, int *));
|
||||||
|
|
||||||
|
/** 获取所有已注册命令列表 */
|
||||||
|
stru_cmd *cmd_manager_get_commands(unsigned int *out_count);
|
||||||
|
|
||||||
|
/** 打印帮助信息 */
|
||||||
|
void cmd_help(int argc, char *argv[]);
|
||||||
|
|
||||||
|
/** 按名称查找命令 */
|
||||||
|
stru_cmd *cmd_find(const char *name);
|
||||||
|
|
||||||
|
/** Tab 自动补全回调 */
|
||||||
|
void cmd_complete(const char *buf, char ***completions, int *ncomp);
|
||||||
|
|
||||||
|
/** 子命令补全 */
|
||||||
|
void cmd_sub_complete(const char *buf, char ***completions, int *ncomp,
|
||||||
|
const char **subs, int sub_count);
|
||||||
|
|
||||||
|
/** 行编辑交互式输入 */
|
||||||
|
char *linenoise(const char *prompt);
|
||||||
|
|
||||||
|
/** 释放 linenoise 返回的内存 */
|
||||||
|
void linenoiseFree(void *ptr);
|
||||||
|
|
||||||
|
/** 添加历史记录 */
|
||||||
|
int linenoiseHistoryAdd(const char *line);
|
||||||
|
|
||||||
|
/** 释放所有历史 */
|
||||||
|
void linenoiseHistoryFree(void);
|
||||||
|
|
||||||
|
/** 设置自动补全回调 */
|
||||||
|
void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *));
|
||||||
|
|
||||||
|
/** 注册命令宏(无补全) */
|
||||||
|
#define CMD_REGISTER(cmd_name, func_ptr, cmd_desc) \
|
||||||
|
__attribute__((constructor)) static void register_##func_ptr(void) \
|
||||||
|
{ \
|
||||||
|
cmd_manager_add_command(cmd_name, func_ptr, cmd_desc, NULL); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 注册命令宏(带补全) */
|
||||||
|
#define CMD_REGISTER_C(cmd_name, func_ptr, cmd_desc, complete) \
|
||||||
|
__attribute__((constructor)) static void register_##func_ptr(void) \
|
||||||
|
{ \
|
||||||
|
cmd_manager_add_command(cmd_name, func_ptr, cmd_desc, complete); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,482 @@
|
||||||
|
/**
|
||||||
|
* @file myCmd.cpp
|
||||||
|
* @brief 命令处理框架实现 — 命令注册/分发 + 内置 linenoise 行编辑
|
||||||
|
*/
|
||||||
|
#include "myCmd.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
|
||||||
|
/* ===== 命令注册表 ===== */
|
||||||
|
|
||||||
|
static std::vector<stru_cmd> &get_cmd_list(void)
|
||||||
|
{
|
||||||
|
static std::vector<stru_cmd> s_cmd_list;
|
||||||
|
|
||||||
|
return s_cmd_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_manager_add_command(const char *name, void (*func)(int argc, char *argv[]),
|
||||||
|
const char *desc, void (*complete)(const char *, char ***, int *))
|
||||||
|
{
|
||||||
|
if (!name || !func || !desc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &list = get_cmd_list();
|
||||||
|
|
||||||
|
list.push_back({name, func, desc, complete});
|
||||||
|
}
|
||||||
|
|
||||||
|
stru_cmd *cmd_manager_get_commands(unsigned int *out_count)
|
||||||
|
{
|
||||||
|
auto &list = get_cmd_list();
|
||||||
|
|
||||||
|
if (out_count)
|
||||||
|
{
|
||||||
|
*out_count = (unsigned int)list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.empty())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
stru_cmd *cmd_find(const char *name)
|
||||||
|
{
|
||||||
|
if (!name)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &list = get_cmd_list();
|
||||||
|
|
||||||
|
for (auto &cmd : list)
|
||||||
|
{
|
||||||
|
if (0 == strcmp(cmd.name, name))
|
||||||
|
{
|
||||||
|
return &cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_complete(const char *buf, char ***completions, int *ncomp)
|
||||||
|
{
|
||||||
|
auto &list = get_cmd_list();
|
||||||
|
|
||||||
|
/* 判断是否有空格 —— 有空格则进入子命令补全 */
|
||||||
|
const char *space = strchr(buf, ' ');
|
||||||
|
|
||||||
|
if (space)
|
||||||
|
{
|
||||||
|
size_t cmd_len = (size_t)(space - buf);
|
||||||
|
|
||||||
|
for (auto &cmd : list)
|
||||||
|
{
|
||||||
|
if (0 == strncmp(cmd.name, buf, cmd_len) && '\0' == cmd.name[cmd_len])
|
||||||
|
{
|
||||||
|
if (cmd.complete)
|
||||||
|
{
|
||||||
|
cmd.complete(buf, completions, ncomp);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ncomp = 0;
|
||||||
|
*completions = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无空格:命令名前缀匹配 */
|
||||||
|
int cnt = 0;
|
||||||
|
char **c = NULL;
|
||||||
|
|
||||||
|
for (auto &cmd : list)
|
||||||
|
{
|
||||||
|
if (!strncmp(cmd.name, buf, strlen(buf)))
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
c = (char **)malloc(sizeof(char *) * (list.size() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
c[cnt] = strdup(cmd.name);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
*ncomp = 0;
|
||||||
|
*completions = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ncomp = cnt;
|
||||||
|
*completions = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_sub_complete(const char *buf, char ***completions, int *ncomp,
|
||||||
|
const char **subs, int sub_count)
|
||||||
|
{
|
||||||
|
const char *prefix = strrchr(buf, ' ');
|
||||||
|
|
||||||
|
if (!prefix)
|
||||||
|
{
|
||||||
|
*ncomp = 0;
|
||||||
|
*completions = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix++;
|
||||||
|
|
||||||
|
int cnt = 0;
|
||||||
|
char **c = NULL;
|
||||||
|
size_t prefix_len = strlen(prefix);
|
||||||
|
|
||||||
|
for (int i = 0; i < sub_count; i++)
|
||||||
|
{
|
||||||
|
if (0 == strncmp(subs[i], prefix, prefix_len))
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
c = (char **)malloc(sizeof(char *) * (size_t)(sub_count + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
c[cnt] = strdup(subs[i]);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*completions = c;
|
||||||
|
*ncomp = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_help(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
auto &list = get_cmd_list();
|
||||||
|
|
||||||
|
for (auto &cmd : list)
|
||||||
|
{
|
||||||
|
printf("%-24s [%s]\n", cmd.name, cmd.desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 内置 linenoise ===== */
|
||||||
|
|
||||||
|
#define SAFE_WRITE(fd, buf, sz) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
ssize_t _r = write(fd, buf, sz); \
|
||||||
|
(void)_r; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static char *g_history[100];
|
||||||
|
static int g_history_len = 0;
|
||||||
|
static void (*g_completion_cb)(const char *, char ***, int *) = NULL;
|
||||||
|
|
||||||
|
static struct termios g_orig_termios;
|
||||||
|
static int g_raw_enabled = 0;
|
||||||
|
|
||||||
|
static void disable_raw(void)
|
||||||
|
{
|
||||||
|
if (!g_raw_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_orig_termios);
|
||||||
|
g_raw_enabled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigint_handler(int sig)
|
||||||
|
{
|
||||||
|
(void)sig;
|
||||||
|
|
||||||
|
if (g_raw_enabled)
|
||||||
|
{
|
||||||
|
disable_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int enable_raw(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, sigint_handler);
|
||||||
|
tcgetattr(STDIN_FILENO, &g_orig_termios);
|
||||||
|
atexit(disable_raw);
|
||||||
|
|
||||||
|
struct termios raw = g_orig_termios;
|
||||||
|
|
||||||
|
raw.c_lflag &= ~(ECHO | ICANON);
|
||||||
|
raw.c_cc[VMIN] = 1;
|
||||||
|
raw.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||||
|
|
||||||
|
if (0 == ret)
|
||||||
|
{
|
||||||
|
g_raw_enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int linenoise_edit(char *buf, int bufsize, const char *prompt)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
memset(buf, 0, (size_t)bufsize);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
|
||||||
|
|
||||||
|
int history_idx = g_history_len;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
ssize_t n = read(STDIN_FILENO, &c, 1);
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\n' || c == '\r')
|
||||||
|
{
|
||||||
|
buf[len] = '\0';
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, "\n", 1);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 127 || c == '\b')
|
||||||
|
{
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
len--;
|
||||||
|
pos--;
|
||||||
|
buf[len] = '\0';
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, "\b \b", 3);
|
||||||
|
history_idx = g_history_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 27)
|
||||||
|
{
|
||||||
|
char seq[2];
|
||||||
|
|
||||||
|
if (read(STDIN_FILENO, &seq[0], 1) == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(STDIN_FILENO, &seq[1], 1) == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seq[0] == '[')
|
||||||
|
{
|
||||||
|
if (seq[1] == 'A')
|
||||||
|
{
|
||||||
|
if (g_history_len == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
history_idx--;
|
||||||
|
|
||||||
|
if (history_idx < 0)
|
||||||
|
{
|
||||||
|
history_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *hist = g_history[history_idx];
|
||||||
|
|
||||||
|
strncpy(buf, hist, (size_t)(bufsize - 1));
|
||||||
|
len = pos = (int)strlen(buf);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);
|
||||||
|
}
|
||||||
|
else if (seq[1] == 'B')
|
||||||
|
{
|
||||||
|
if (g_history_len == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
history_idx++;
|
||||||
|
|
||||||
|
if (history_idx >= g_history_len)
|
||||||
|
{
|
||||||
|
history_idx = g_history_len;
|
||||||
|
memset(buf, 0, (size_t)bufsize);
|
||||||
|
len = pos = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *hist = g_history[history_idx];
|
||||||
|
|
||||||
|
strncpy(buf, hist, (size_t)(bufsize - 1));
|
||||||
|
len = pos = (int)strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 9)
|
||||||
|
{
|
||||||
|
char **completions = NULL;
|
||||||
|
int ncomp = 0;
|
||||||
|
|
||||||
|
if (g_completion_cb)
|
||||||
|
{
|
||||||
|
g_completion_cb(buf, &completions, &ncomp);
|
||||||
|
|
||||||
|
if (ncomp > 0)
|
||||||
|
{
|
||||||
|
char *last_space = NULL;
|
||||||
|
|
||||||
|
for (int i = pos - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (' ' == buf[i])
|
||||||
|
{
|
||||||
|
last_space = &buf[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_space)
|
||||||
|
{
|
||||||
|
int keep_len = (int)(last_space - buf + 1);
|
||||||
|
|
||||||
|
strncpy(last_space + 1, completions[0],
|
||||||
|
(size_t)(bufsize - keep_len - 1));
|
||||||
|
buf[bufsize - 1] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(buf, completions[0], (size_t)(bufsize - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
len = pos = (int)strlen(buf);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);
|
||||||
|
|
||||||
|
for (int j = 0; j < ncomp; j++)
|
||||||
|
{
|
||||||
|
free(completions[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(completions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
history_idx = g_history_len;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < bufsize - 1)
|
||||||
|
{
|
||||||
|
buf[len++] = c;
|
||||||
|
pos++;
|
||||||
|
SAFE_WRITE(STDOUT_FILENO, &c, 1);
|
||||||
|
history_idx = g_history_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *linenoise(const char *prompt)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
if (0 != enable_raw())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = linenoise_edit(buf, (int)sizeof(buf), prompt);
|
||||||
|
|
||||||
|
disable_raw();
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *copy = strdup(buf);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linenoiseFree(void *ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int linenoiseHistoryAdd(const char *line)
|
||||||
|
{
|
||||||
|
if (g_history_len >= 100)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_history[g_history_len] = strdup(line);
|
||||||
|
g_history_len++;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linenoiseHistoryFree(void)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < g_history_len; j++)
|
||||||
|
{
|
||||||
|
free(g_history[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *))
|
||||||
|
{
|
||||||
|
g_completion_cb = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* extern "C" */
|
||||||
Loading…
Reference in New Issue