// 补全状态结构(优化版)
typedef struct {
char **matches; // 所有匹配项
int count; // 匹配项总数
char *base_dir; // 当前基础目录
char *original_prefix; // 原始前缀
char *current_text; // 当前输入文本
int last_state; // 上一次使用的state值
int current_level; // 当前目录层级
char *persistent_path; // 持久化路径(同时作为初始化标志)
int is_directory;
int max_level; // 最大允许层级(关键新增)
} CompletionState;
static CompletionState comp_state = {
.matches = NULL,
.count = 0,
.base_dir = NULL,
.original_prefix = NULL,
.current_text = NULL,
.last_state = -1,
.current_level = 0,
.persistent_path = NULL,
.is_directory = 0,
.max_level = 0
};
// 重置补全状态
void reset_completion_state() {
if (comp_state.matches) {
for (int i = 0; i < comp_state.count; i++) {
XFREE(MTYPE_TMP, comp_state.matches[i]);
}
XFREE(MTYPE_TMP, comp_state.matches);
comp_state.matches = NULL;
}
if (comp_state.original_prefix) {
XFREE(MTYPE_TMP, comp_state.original_prefix);
}
if (comp_state.base_dir) {
XFREE(MTYPE_TMP, comp_state.base_dir);
}
if (comp_state.current_text) {
XFREE(MTYPE_TMP, comp_state.current_text);
}
if (comp_state.persistent_path) {
XFREE(MTYPE_TMP, comp_state.persistent_path);
}
comp_state.count = 0;
comp_state.original_prefix = NULL;
comp_state.base_dir = NULL;
comp_state.current_text = NULL;
comp_state.last_state = -1;
comp_state.current_level = 0;
comp_state.persistent_path = NULL; // 关键:清除持久化路径
comp_state.is_directory = 0;
comp_state.max_level = 0;
}
// 修复的路径解析函数
void parse_input_text(const char *text, char **base_dir, char **prefix, int *level) {
*level = 0;
const char *ptr = text;
while (*ptr) {
if (*ptr == '/') (*level)++;
ptr++;
}
const char *last_slash = strrchr(text, '/');
const char *last_char = text + strlen(text) - 1;
if (last_slash) {
if (last_char == last_slash) {
size_t base_len = last_slash - text + 1;
*base_dir = (char *)XCALLOC(MTYPE_TMP, base_len + 1);
strncpy(*base_dir, text, base_len);
(*base_dir)[base_len] = '\0';
*prefix = XSTRDUP(MTYPE_TMP, "");
} else {
size_t base_len = last_slash - text + 1;
*base_dir = (char *)XCALLOC(MTYPE_TMP, base_len + 1);
strncpy(*base_dir, text, base_len);
(*base_dir)[base_len] = '\0';
*prefix = XSTRDUP(MTYPE_TMP, last_slash + 1);
}
} else {
*base_dir = XSTRDUP(MTYPE_TMP, "");
*prefix = XSTRDUP(MTYPE_TMP, text);
}
printf("parse_input_text: text = %s, base_dir = %s, prefix = %s, level = %d\n", text, *base_dir, *prefix, *level);
}
// 修复的上下文检测函数
int is_same_completion_context(const char *text) {
if (!comp_state.persistent_path || !text) return 0;
// 临时解析获取当前层级
char *temp_base = NULL;
char *temp_prefix = NULL;
int current_level = 0;
parse_input_text(text, &temp_base, &temp_prefix, ¤t_level);
// 关键改进:检查是否超过最大层级
int within_level_limit = (current_level <= comp_state.max_level);
// 检查路径前缀是否相同
size_t len = strlen(comp_state.persistent_path);
int same_path = strncmp(text, comp_state.persistent_path, len) == 0;
XFREE(MTYPE_TMP, temp_base);
XFREE(MTYPE_TMP, temp_prefix);
printf("is_same_completion_context: text=%s persistent=%s level=%d/%d same_path=%d within_level=%d\n",
text, comp_state.persistent_path, current_level, comp_state.max_level, same_path, within_level_limit);
return same_path && within_level_limit;
}
// 路径比较函数
int compare_paths(const void *a, const void *b) {
const char *path1 = *(const char **)a;
const char *path2 = *(const char **)b;
return strcmp(path1, path2);
}
// 修复的目录内容生成函数
char **generate_current_dir_paths(const char *base_dir, const char *prefix, int add_empty) {
// 关键修复:正确处理空目录路径
const char *scan_dir = base_dir;
if (!scan_dir || strlen(scan_dir) == 0) {
scan_dir = ".";
}
printf("[generate] Scanning: '%s' with prefix '%s'\n",
scan_dir, prefix ? prefix : "(none)");
DIR *dir = opendir(scan_dir);
if (!dir) {
printf("opendir failed");
if (add_empty) {
char **matches = (char **)XCALLOC(MTYPE_TMP, sizeof(char *));
matches[0] = XSTRDUP(MTYPE_TMP, "");
return matches;
}
return NULL;
}
int capacity = 32;
int count = 0;
char **matches = (char **)XCALLOC(MTYPE_TMP, capacity * sizeof(char *));
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
// 跳过特殊目录
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
continue;
}
// 应用前缀过滤
if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)) != 0) {
continue;
}
// 检测目录类型
int is_dir = 0;
if (entry->d_type == DT_DIR) {
is_dir = 1;
} else if (entry->d_type == DT_UNKNOWN) {
char full_path[PATH_MAX];
snprintf(full_path, sizeof(full_path), "%s/%s", scan_dir, name);
struct stat statbuf;
if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
is_dir = 1;
}
}
// 创建带斜杠的目录名
char *new_name;
if (is_dir) {
new_name = (char *)XCALLOC(MTYPE_TMP, strlen(name) + 2);
sprintf(new_name, "%s/", name);
} else {
new_name = XSTRDUP(MTYPE_TMP, name);
}
// 添加到匹配列表
if (count >= capacity) {
capacity *= 2;
matches = (char **)XREALLOC(MTYPE_TMP, matches, capacity * sizeof(char *));
}
matches[count++] = new_name;
}
closedir(dir);
// 排序结果
if (count > 0) {
qsort(matches, count, sizeof(char *), compare_paths);
}
// Null-terminate
if (count >= capacity) {
matches = (char **)XREALLOC(MTYPE_TMP, matches, (count+1) * sizeof(char *));
}
matches[count] = NULL;
// 在非顶级目录添加空字符串作为循环结束标记
if (count > 0 && !add_empty) {
matches = (char **)XREALLOC(MTYPE_TMP, matches, (count+2) * sizeof(char *));
matches[count] = XSTRDUP(MTYPE_TMP, "");
matches[count+1] = NULL;
count++;
}
printf("[generate] Found %d matches\n", count);
return matches;
}
// 修复的智能文件补全函数
char *filename_completion_function(const char *text, int state) {
printf("\n[COMP] Entering: text='%s', state=%d, last_state=%d\n",
text, state, comp_state.last_state);
// 初始化新补全
if (state == 0) {
int same_context = is_same_completion_context(text);
printf(" Same context: %d\n", same_context);
if (!same_context) {
// 上下文变化,重置状态
reset_completion_state();
char *current_base = NULL;
char *current_prefix = NULL;
int current_level = 0;
// 解析当前输入文本
parse_input_text(text, ¤t_base, ¤t_prefix, ¤t_level);
printf(" Parsed: base='%s', prefix='%s', level=%d\n",
current_base, current_prefix, current_level);
// 初始化状态
comp_state.base_dir = current_base;
comp_state.current_text = XSTRDUP(MTYPE_TMP, text);
comp_state.current_level = current_level;
// 设置持久化路径(当前目录)
if (strlen(current_base) > 0) {
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, current_base);
} else {
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, ".");
}
// 仅在顶级目录添加空字符串项
int add_empty = (current_level == 0) ? 1 : 0;
// 生成匹配项
comp_state.matches = generate_current_dir_paths(current_base, current_prefix, add_empty);
// 计算匹配项数量
if (comp_state.matches) {
comp_state.count = 0;
while (comp_state.matches[comp_state.count]) {
comp_state.count++;
}
printf(" Generated %d matches\n", comp_state.count);
} else {
comp_state.count = 0;
}
comp_state.last_state = -1;
}
}
// 无匹配项时返回NULL
if (!comp_state.matches || comp_state.count == 0) {
printf("[COMP] No matches found\n");
return NULL;
}
// 循环获取下一个匹配项
int next_index = (comp_state.last_state + 1) % comp_state.count;
comp_state.last_state = next_index;
char *match = comp_state.matches[next_index];
printf(" Next index: %d/%d, match='%s'\n",
next_index, comp_state.count - 1, match);
// 构建完整路径
char *full_path = NULL;
if (comp_state.base_dir && strlen(comp_state.base_dir) > 0) {
int base_len = strlen(comp_state.base_dir);
int needs_slash = (comp_state.base_dir[base_len - 1] != '/');
int len = base_len + strlen(match) + (needs_slash ? 1 : 0) + 1;
full_path = (char *)XCALLOC(MTYPE_TMP, len);
if (needs_slash) {
snprintf(full_path, len, "%s/%s", comp_state.base_dir, match);
} else {
snprintf(full_path, len, "%s%s", comp_state.base_dir, match);
}
} else {
full_path = XSTRDUP(MTYPE_TMP, match);
}
printf("[COMP] Returning: '%s'\n", full_path);
return full_path;
}
// 补全匹配函数(返回单个匹配项)
char **filename_completion_matches(const char *text, CPFunction* genfunc) {
printf("\n[MATCHES] Called with text='%s'\n", text);
// 获取下一个匹配项
char *match = (*genfunc)(text, 0);
if (!match) {
printf("[MATCHES] No matches found\n");
return NULL;
}
// 创建只包含一个匹配项的数组
char **matches = (char **)XCALLOC(MTYPE_TMP, 2 * sizeof(char *));
matches[0] = match;
matches[1] = NULL;
printf("[MATCHES] Returning single match: '%s'\n", match);
return matches;
}
<dahua>dir e【cmlsh_completion】text = e
[MATCHES] Called with text='e'
[COMP] Entering: text='e', state=0, last_state=-1
Same context: 0
parse_input_text: text = e, base_dir = , prefix = e, level = 0
Parsed: base='', prefix='e', level=0
[generate] Scanning: '.' with prefix 'e'
[generate] Found 1 matches
Generated 1 matches
Next index: 0/0, match='etc/'
[COMP] Returning: 'etc/'
[MATCHES] Returning single match: 'etc/'
tc/【cmlsh_completion】text = etc/
[MATCHES] Called with text='etc/'
[COMP] Entering: text='etc/', state=0, last_state=0
parse_input_text: text = etc/, base_dir = etc/, prefix = , level = 1
is_same_completion_context: text=etc/ persistent=. level=1/0 same_path=0 within_level=0
Same context: 0
parse_input_text: text = etc/, base_dir = etc/, prefix = , level = 1
Parsed: base='etc/', prefix='', level=1
[generate] Scanning: 'etc/' with prefix ''
[generate] Found 5 matches
Generated 5 matches
Next index: 0/4, match='CML_DB.db'
[COMP] Returning: 'etc/CML_DB.db'
[MATCHES] Returning single match: 'etc/CML_DB.db'
CML_DB.db【cmlsh_completion】text = etc/CML_DB.db
[MATCHES] Called with text='etc/CML_DB.db'
[COMP] Entering: text='etc/CML_DB.db', state=0, last_state=0
parse_input_text: text = etc/CML_DB.db, base_dir = etc/, prefix = CML_DB.db, level = 1
is_same_completion_context: text=etc/CML_DB.db persistent=etc/ level=1/0 same_path=1 within_level=0
Same context: 0
parse_input_text: text = etc/CML_DB.db, base_dir = etc/, prefix = CML_DB.db, level = 1
Parsed: base='etc
Username:
上边的代码逻辑会异常退出
最新发布