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 (text == NULL || *text == '\0') {
printf("用户未输入任何文本,不提供补全\n");
return NULL;
}
char current_dir[PATH_MAX];
char abs_path[PATH_MAX];
int reset_completion = 0;
int is_flash_mode = (strncmp(text, "flash:/", 7) == 0);
int is_f_prefix = (strncmp(text, "f", 1) == 0 && strncmp(text, "flash:/", 7) != 0);
// 核心优化:添加前缀匹配检查
int is_prefix_of_existing = 0;
if (comp_state.matches && comp_state.count > 0) {
for (int i = 0; i < comp_state.count; i++) {
if (comp_state.count == 1) {
is_prefix_of_existing = 0;
} else if (strncmp(text, comp_state.matches[i], strlen(text)) == 0) {
is_prefix_of_existing = 1;
break;
}
}
}
printf(" Text is prefix of existing matches, is_prefix_of_existing = %d\n", is_prefix_of_existing);
// 仅在需要时初始化新补全
if (state == 0) {
printf(" Starting new completion check\n");
// 获取当前工作目录
getcwd(current_dir, sizeof(current_dir));
snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, text);
// 核心优化:检查是否是现有匹配项的前缀
if (is_prefix_of_existing) {
printf(" Text is prefix of existing matches, no reset needed\n");
reset_completion = 0;
} else {
// 原始重置逻辑
if (access(abs_path, F_OK) != 0) {
reset_completion = 1;
}
}
printf("\n111filename_completion_function: reset_completion = %d, abs_path = '%s'\n", reset_completion, abs_path);
char *tmp_base = NULL;
char *tmp_prefix = NULL;
int tmp_level = 0;
parse_input_text(text, &tmp_base, &tmp_prefix, &tmp_level);
printf("\n222filename_completion_function: text = '%s', tmp_base = '%s', tmp_prefix = '%s'\n",
text, tmp_base, tmp_prefix);
int same_reset = 0;
struct stat st_tmp;
char tmp_path[PATH_MAX];
if (comp_state.matches && comp_state.matches[0]) {
for (int i = 0; comp_state.matches[i]; i++) {
printf("\n333filename_completion_function: comp_state.matches[%d] = '%s'\n", i, comp_state.matches[i]);
if (strncmp(comp_state.matches[i], tmp_prefix, strlen(tmp_prefix)) == 0) {
char *old_base = comp_state.base_dir ? comp_state.base_dir : "";
snprintf(tmp_path, sizeof(tmp_path), "%s/%s%s", current_dir, old_base, comp_state.matches[i]);
printf("\n444filename_completion_function: tmp_path = '%s', current_dir = '%s', comp_state.matches[%d] = '%s'\n",
tmp_path, current_dir, i, comp_state.matches[i]);
if (stat(tmp_path, &st_tmp) == 0 && S_ISDIR(st_tmp.st_mode)) {
same_reset = 1;
printf("\nfilename_completion_function: same_reset = %d\n", same_reset);
break;
}
}
}
}
printf("\n555filename_completion_function: current_dir = '%s', text = '%s', abs_path = '%s', reset_completion = %d, same_reset = %d\n",
current_dir, text, abs_path, reset_completion, same_reset);
printf("\n666filename_completion_function: comp_state.persistent_path = '%s'\n", comp_state.persistent_path);
int same_context = is_same_completion_context(text);
printf(" Same context: %d\n", same_context);
// 核心优化:添加匹配数量和类型判断
int should_reset = 0;
if (is_prefix_of_existing) {
printf(" Using existing matches for prefix\n");
should_reset = 0;
// 新增:检查单目录匹配情况
if (comp_state.matches && comp_state.count == 1) {
char *single_match = comp_state.matches[0];
char full_path[PATH_MAX];
// 构建完整路径
if (comp_state.base_dir) {
snprintf(full_path, sizeof(full_path), "%s/%s", comp_state.base_dir, single_match);
} else {
snprintf(full_path, sizeof(full_path), "%s", single_match);
}
// 检查文件类型
struct stat st;
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
printf(" Single directory match, forcing reset\n");
should_reset = 1;
}
}
} else if ((!same_context) || (reset_completion && same_reset) ||
(!comp_state.persistent_path || comp_state.persistent_path[0] == '\0')) {
should_reset = 1;
}
// 统一检查单目录匹配情况
if (!should_reset && comp_state.matches && comp_state.count == 1) {
char *single_match = comp_state.matches[0];
char full_path[PATH_MAX];
// 构建完整路径
if (comp_state.base_dir) {
snprintf(full_path, sizeof(full_path), "%s/%s", comp_state.base_dir, single_match);
} else {
snprintf(full_path, sizeof(full_path), "%s", single_match);
}
// 检查文件类型
struct stat st;
if (stat(full_path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
printf(" Single directory match, forcing reset\n");
should_reset = 1;
} else {
printf(" Single file match, no reset needed\n");
}
}
}
if (should_reset) {
reset_completion_state();
printf("\n -----------------common reset---------------\n");
char *current_base = NULL;
char *current_prefix = NULL;
int current_level = 0;
parse_input_text(text, ¤t_base, ¤t_prefix, ¤t_level);
printf("\n777filename_completion_function: current_base = '%s', current_prefix = '%s', current_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.original_prefix = XSTRDUP(MTYPE_TMP, current_prefix);
comp_state.current_level = current_level;
comp_state.max_level = current_level + 3;
// 检查是否用户已经明确进入了子目录
int ends_with_slash = text[strlen(text) - 1] == '/';
if (ends_with_slash) {
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, text);
comp_state.max_level = current_level + 3;
} else {
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, current_base);
comp_state.max_level = current_level;
}
// 生成当前目录的补全项
if (is_flash_mode) {
comp_state.matches = generate_current_dir_paths("/mnt/switch/", text + 7, 1);
comp_state.flash_mode = 1;
} else if (is_f_prefix) {
comp_state.matches = generate_current_dir_paths(current_base, current_prefix, 1);
if (current_level == 0) {
if (comp_state.matches == NULL || comp_state.matches[0] == NULL) {
comp_state.base_dir = XSTRDUP(MTYPE_TMP, "flash:/");
comp_state.matches = generate_current_dir_paths("/mnt/switch/", text, 1);
comp_state.flash_mode = 1;
} else {
int count = 0;
while (comp_state.matches[count]) count++;
int flash_already_in_list = 0;
for (int i = 0; comp_state.matches[i]; i++) {
if (strncmp(comp_state.matches[i], "flash:/", 7) == 0) {
flash_already_in_list = 1;
break;
}
}
if (!flash_already_in_list && count < 1024 - 1) {
char **temp = (char **)XREALLOC(MTYPE_TMP, comp_state.matches, (count + 2) * sizeof(char*));
comp_state.matches = temp;
comp_state.matches[count] = XSTRDUP(MTYPE_TMP, "flash:/");
comp_state.matches[count + 1] = NULL;
if (comp_state.matches && comp_state.matches[0]) {
printf("\n flash: comp_state.matches = ");
for (int i = 0; comp_state.matches[i]; i++) {
printf("%s, ", comp_state.matches[i]);
}
printf("\n");
}
}
}
}
} else {
comp_state.matches = generate_current_dir_paths(current_base, current_prefix, current_level == 0);
}
comp_state.count = 0;
if (comp_state.matches) {
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; // 重置索引
}
}
printf("\n888filename_completion_function: comp_state.base_dir = '%s', comp_state.current_text = '%s', comp_state.original_prefix = '%s', comp_state.persistent_path = '%s'\n",
comp_state.base_dir, comp_state.current_text, comp_state.original_prefix, comp_state.persistent_path);
// 没有匹配项
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];
// 构建完整路径
char *full_path = NULL;
if (strncmp(match, "flash:/", 7) == 0) {
full_path = XSTRDUP(MTYPE_TMP, match);
} else if (comp_state.flash_mode) {
full_path = XCALLOC(MTYPE_TMP, strlen("flash:/") + strlen(match) + 2);
snprintf(full_path, strlen("flash:/") + strlen(match) + 2, "flash:/%s", match);
} else {
if (comp_state.base_dir && strlen(comp_state.base_dir) > 0) {
int base_len = strlen(comp_state.base_dir);
int needs_slash = base_len > 0 && comp_state.base_dir[base_len - 1] != '/';
full_path = XCALLOC(MTYPE_TMP, strlen(match) + base_len + 2);
if (needs_slash) {
snprintf(full_path, strlen(match) + base_len + 2, "%s/%s", comp_state.base_dir, match);
} else {
snprintf(full_path, strlen(match) + base_len + 2, "%s%s", comp_state.base_dir, match);
}
} else {
full_path = XSTRDUP(MTYPE_TMP, match);
}
}
printf("[COMP] Returning: '%s'\n", full_path);
return full_path;
}
这个代码的打印:
<dahua>dir f
【cmlsh_completion】text = f
[MATCHES] Called with text='f'
[COMP] Entering: text='f', state=0, last_state=-1
Text is prefix of existing matches, is_prefix_of_existing = 0
Starting new completion check
111filename_completion_function: reset_completion = 1, abs_path = '/mnt/switch/f'
222filename_completion_function: text = 'f', tmp_base = '', tmp_prefix = 'f'
555filename_completion_function: current_dir = '/mnt/switch', text = 'f', abs_path = '/mnt/switch/f', reset_completion = 1, same_reset = 0
666filename_completion_function: comp_state.persistent_path = '(null)'
Same context: 0
-----------------common reset---------------
777filename_completion_function: current_base = '', current_prefix = 'f', current_level = 0
[generate] Scanning directory: '.' with prefix: 'f'
跳过软链接: main_uImage
[generate] Found 1 matches.
generate_current_dir_paths: matches = fa/,
flash: comp_state.matches = fa/, flash:/,
Generated 2 matches
888filename_completion_function: comp_state.base_dir = '', comp_state.current_text = 'f', comp_state.original_prefix = 'f', comp_state.persistent_path = ''
[COMP] Returning: 'fa/'
[MATCHES] Returning single match: 'fa/'
a/
【cmlsh_completion】text = fa/
[MATCHES] Called with text='fa/'
[COMP] Entering: text='fa/', state=0, last_state=0
Text is prefix of existing matches, is_prefix_of_existing = 1
Starting new completion check
Text is prefix of existing matches, no reset needed
111filename_completion_function: reset_completion = 0, abs_path = '/mnt/switch/fa/'
222filename_completion_function: text = 'fa/', tmp_base = 'fa/', tmp_prefix = ''
333filename_completion_function: comp_state.matches[0] = 'fa/'
444filename_completion_function: tmp_path = '/mnt/switch/fa/', current_dir = '/mnt/switch', comp_state.matches[0] = 'fa/'
filename_completion_function: same_reset = 1
555filename_completion_function: current_dir = '/mnt/switch', text = 'fa/', abs_path = '/mnt/switch/fa/', reset_completion = 0, same_reset = 1
666filename_completion_function: comp_state.persistent_path = ''
is_same_completion_context: text = fa/, temp_base = fa/, current_level = 1
is_same_completion_context: same_path=1, within_level=0
Same context: 0
Using existing matches for prefix
888filename_completion_function: comp_state.base_dir = '', comp_state.current_text = 'f', comp_state.original_prefix = 'f', comp_state.persistent_path = ''
[COMP] Returning: 'flash:/'
[MATCHES] Returning single match: 'flash:/' lash:/e
【cmlsh_completion】text = flash:/e
[MATCHES] Called with text='flash:/e'
[COMP] Entering: text='flash:/e', state=0, last_state=1
Text is prefix of existing matches, is_prefix_of_existing = 0
Starting new completion check
111filename_completion_function: reset_completion = 1, abs_path = '/mnt/switch/flash:/e'
222filename_completion_function: text = 'flash:/e', tmp_base = '', tmp_prefix = 'e'
333filename_completion_function: comp_state.matches[0] = 'fa/'
333filename_completion_function: comp_state.matches[1] = 'flash:/'
555filename_completion_function: current_dir = '/mnt/switch', text = 'flash:/e', abs_path = '/mnt/switch/flash:/e', reset_completion = 1, same_reset = 0
666filename_completion_function: comp_state.persistent_path = ''
is_same_completion_context: text = flash:/e, temp_base = , current_level = 0
is_same_completion_context: same_path=1, within_level=1
Same context: 1
-----------------common reset---------------
777filename_completion_function: current_base = '', current_prefix = 'e', current_level = 0
[generate] Scanning directory: '/mnt/switch/' with prefix: 'e'
跳过软链接: main_uImage
[generate] Found 2 matches.
generate_current_dir_paths: matches = ea.txt, etc/,
Generated 2 matches
888filename_completion_function: comp_state.base_dir = '', comp_state.current_text = 'flash:/e', comp_state.original_prefix = 'e', comp_state.persistent_path = ''
[COMP] Returning: 'flash:/ea.txt'
[MATCHES] Returning single match: 'flash:/ea.txt'
a.txt
【cmlsh_completion】text = flash:/ea.txt
[MATCHES] Called with text='flash:/ea.txt'
[COMP] Entering: text='flash:/ea.txt', state=0, last_state=0
Text is prefix of existing matches, is_prefix_of_existing = 0
Starting new completion check
111filename_completion_function: reset_completion = 1, abs_path = '/mnt/switch/flash:/ea.txt'
222filename_completion_function: text = 'flash:/ea.txt', tmp_base = ''
Username:
我最开始是输入是f,然后补全出flash:/后,我追加了个e,generate_current_dir_paths: matches = ea.txt, etc/,然后tab去补全,
正常是要显示的是flash:/ea.txt,flash:/etc/循环显示,但是111filename_completion_function: reset_completion = 1, abs_path = '/mnt/switch/flash:/ea.txt'显示拼接异常了,显示text = 'flash:/ea.txt',实际上虚拟的flash:/就是/mnt/switch/实际路径,这个要如何修复?在我给出的代码的基础上优化,不要新增函数,也不要很大的改动