还是使用
char* filename_completion_function(const char* text, int state)
{
static DIR* dir = NULL;
static char *filename = NULL, *dirname = NULL;
static size_t filename_len = 0;
struct dirent* entry;
char* temp;
size_t len;
// 状态重置:当state==0时,表示新的补全请求
if (state == 0) {
// 释放之前资源(增加空指针检查)
if (dir) {
closedir(dir);
dir = NULL;
}
if (filename) {
XFREE(MTYPE_TMP, filename);
filename = NULL;
}
if (dirname) {
XFREE(MTYPE_TMP, dirname);
dirname = NULL;
}
// 解析text,分离目录和文件名部分
const char *temp_ptr = strrchr(text, '/');
if (temp_ptr) {
// 提取文件名部分(最后一个斜杠之后的内容)
filename = XSTRDUP(MTYPE_TMP, temp_ptr + 1);
if (!filename) return NULL;
// 提取目录部分(包括最后一个斜杠)
len = temp_ptr - text + 1; // 包括斜杠
dirname = XMALLOC(MTYPE_TMP, len + 1);
if (!dirname) {
XFREE(MTYPE_TMP, filename);
return NULL;
}
strncpy(dirname, text, len);
dirname[len] = '\0'; // 确保终止符
} else {
// 没有斜杠,说明只有文件名(保持无前缀)
filename = XSTRDUP(MTYPE_TMP, text);
if (!filename) return NULL;
dirname = XSTRDUP(MTYPE_TMP, ""); // 空字符串代替"./"
if (!dirname) {
XFREE(MTYPE_TMP, filename);
return NULL;
}
}
// 处理家目录展开(~user)
if (dirname[0] == '~') {
char *expanded = tilde_expand(dirname);
if (expanded) {
XFREE(MTYPE_TMP, dirname);
dirname = expanded;
}
}
filename_len = filename ? strlen(filename) : 0;
// 打开目录(处理空目录名情况)
const char *open_path = (dirname[0] == '\0') ? "." : dirname;
dir = opendir(open_path);
if (!dir) {
XFREE(MTYPE_TMP, filename);
XFREE(MTYPE_TMP, dirname);
filename = NULL;
dirname = NULL;
return NULL;
}
}
// 如果目录流未打开或资源未初始化,返回NULL
if (!dir || !filename || !dirname) {
return NULL;
}
// 遍历目录,寻找匹配项
while ((entry = readdir(dir)) != NULL) {
// 跳过隐藏文件(以.开头),除非文件名以.开头
if (filename[0] != '.' && entry->d_name[0] == '.') {
// 但允许匹配显式指定的隐藏文件
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) {
continue;
}
}
// 检查文件名是否匹配
size_t entry_len = strlen(entry->d_name);
if (entry_len < filename_len) {
continue;
}
if (strncmp(entry->d_name, filename, filename_len) == 0) {
// 计算路径长度(避免空目录名问题)
size_t dir_len = strlen(dirname);
len = dir_len + entry_len + 2; // +1 for null, +1 for possible '/'
temp = XMALLOC(MTYPE_TMP, len);
if (!temp) continue; // 分配失败则跳过
// 拼接目录和文件名(处理空目录名)
if (dir_len > 0) {
snprintf(temp, len, "%s%s", dirname, entry->d_name);
} else {
strncpy(temp, entry->d_name, len);
}
// 检查是否为目录(使用d_type优化性能)
int is_dir = 0;
#if defined(_DIRENT_HAVE_D_TYPE) || defined(__linux__)
if (entry->d_type == DT_DIR) {
is_dir = 1;
} else if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) {
// 需要stat检查
struct stat st;
if (stat(temp, &st) == 0 && S_ISDIR(st.st_mode)) {
is_dir = 1;
}
}
#else
// 没有d_type支持的系统使用stat
struct stat st;
if (stat(temp, &st) == 0 && S_ISDIR(st.st_mode)) {
is_dir = 1;
}
#endif
// 如果是目录,添加斜杠
if (is_dir) {
size_t cur_len = strlen(temp);
if (cur_len + 1 < len) {
temp[cur_len] = '/';
temp[cur_len + 1] = '\0';
} else {
// 重新分配以容纳斜杠
char *new_temp = XREALLOC(MTYPE_TMP, temp, cur_len + 2);
if (new_temp) {
temp = new_temp;
strcat(temp, "/");
}
}
}
// 返回当前匹配项
printf("\nfilename_completion_function: temp = %s\n", temp);
return temp;
}
}
// 没有更多匹配项,清理资源
closedir(dir);
dir = NULL;
XFREE(MTYPE_TMP, filename);
XFREE(MTYPE_TMP, dirname);
filename = NULL;
dirname = NULL;
return NULL;
}
这个filename_completion_function函数不修改
但是上层调用的:
char** filename_completion_matches(const char* text, CPFunction* genfunc)
{
static int last_index = 0; // 当前循环索引
static char *last_text = NULL; // 上次输入的文本
static char **full_match_list = NULL; // 完整匹配列表缓存
static int full_match_count = 0; // 匹配项总数
// 状态重置:输入变化时重新收集匹配项
if (last_text == NULL || strcmp(text, last_text) != 0) {
// 释放旧资源
if (full_match_list) {
for (int i = 0; i < full_match_count; i++) {
XFREE(MTYPE_TMP, full_match_list[i]);
}
XFREE(MTYPE_TMP, full_match_list);
}
if (last_text) XFREE(MTYPE_TMP, last_text);
// 初始化新状态
last_index = 0;
last_text = XSTRDUP(MTYPE_TMP, text);
full_match_list = NULL;
full_match_count = 0;
// 收集所有匹配项
char **temp_list = NULL;
int count = 0;
char *match;
// 循环调用生成函数直到返回NULL
for (int state = 0; (match = (*genfunc)(text, state)) != NULL; state++) {
// 扩展数组
char **new_list = (char **)XREALLOC(MTYPE_TMP, temp_list, (count + 1) * sizeof(char*));
if (!new_list) {
// 分配失败,释放已收集的
for (int i = 0; i < count; i++) {
XFREE(MTYPE_TMP, temp_list[i]);
}
XFREE(MTYPE_TMP, temp_list);
return NULL;
}
temp_list = new_list;
temp_list[count] = match;
count++;
}
// 保存结果
full_match_list = temp_list;
full_match_count = count;
}
// 无匹配项时返回NULL
if (full_match_count == 0) {
return (char**)NULL;
}
// 计算当前循环位置
int current_index = last_index % full_match_count;
last_index = (last_index + 1) % full_match_count;
// 构建返回列表(单条目)
char **result_list = (char **)XMALLOC(MTYPE_TMP, 3 * sizeof(char*));
if (!result_list) {
return NULL;
}
// 复制匹配项
result_list[0] = XSTRDUP(MTYPE_TMP, full_match_list[current_index]); // 公共前缀
result_list[1] = XSTRDUP(MTYPE_TMP, full_match_list[current_index]); // 实际匹配项
result_list[2] = NULL; // 结束标记
printf("\nfilename_completion_matches: result_list[0] = %s, result_list[1] = %s, result_list[2] = %s\n", result_list[0],result_list[1],result_list[2]);
return result_list;
}
可能要修改一下
现在的现象是:
<dahua>dir e
cmlsh_completion: enter------------------
filename_completion_function: temp = etc/
filename_completion_matches: result_list[0] = etc/, result_list[1] = etc/, result_list[2] = (null)
cmlsh_completion: matches = etc/, etc/,
tc/
cmlsh_completion: enter------------------
filename_completion_function: temp = etc/ssh/
filename_completion_function: temp = etc/nos.conf
filename_completion_function: temp = etc/nos.conf.bak
filename_completion_function: temp = etc/CML_DB.db
filename_completion_matches: result_list[0] = etc/ssh/, result_list[1] = etc/ssh/, result_list[2] = (null)
cmlsh_completion: matches = etc/ssh/, etc/ssh/,
ssh/
cmlsh_completion: enter------------------
filename_completion_function: temp = etc/ssh/ssh_host_rsa_key.pub
filename_completion_function: temp = etc/ssh/ssh_host_ecdsa_key.pub
filename_completion_function: temp = etc/ssh/ssh_host_rsa_key
filename_completion_function: temp = etc/ssh/ssh_host_ed25519_key.pub
filename_completion_function: temp = etc/ssh/ssh_host_ed25519_key
filename_completion_function: temp = etc/ssh/ssh_host_ecdsa_key
filename_completion_matches: result_list[0] = etc/ssh/ssh_host_rsa_key.pub, result_list[1] = etc/ssh/ssh_host_rsa_key.pub, result_list[2] = (null)
cmlsh_completion: matches = etc/ssh/ssh_host_rsa_key.pub, etc/ssh/ssh_host_rsa_key.pub,
ssh_host_rsa_key.pub
cmlsh_completion: enter------------------
虽然filename_completion_function函数中的打印temp是能遍历etc下的普通文件和子目录,但是最外边显示的就没有普通文件,哪里还有问题?
最新发布