S_ISDIR(stat.sb_mode)函数功能

一般在之前都会先调用函数stat( FileName, &fp),意味着将FileName这个文件的信息保存到了地址fp中。此时fp.st_mode就是文件FileName的模式,所以S_ISDIR(fp.st_mode)的函数功能是判断fp所指向文件(也就是FileName)是否为目录(dir)类型。

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; // 新增:最大层级限制 int initialized; int flash_mode; } 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 = -1, .initialized = 0, .flash_mode = 0 }; // 重置补全状态 void reset_completion_state() { if (comp_state.matches) { for (int i = 0; comp_state.matches[i]; 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 = -1; comp_state.initialized = 0; comp_state.flash_mode = 0; } // 路径比较函数(目录优先) int compare_paths(const void *a, const void *b) { const char *sa = *(const char **)a; const char *sb = *(const char **)b; int is_dir_a = (sa[strlen(sa)-1] == '/'); int is_dir_b = (sb[strlen(sb)-1] == '/'); if (is_dir_a && !is_dir_b) return -1; if (!is_dir_a && is_dir_b) return 1; return strcmp(sa, sb); } // 构建完整路径(自动加 /) char *build_completion_path(const char *base, const char *match, int is_dir) { char *path = (char *)XCALLOC(MTYPE_TMP, strlen(base) + strlen(match) + 2); if (base && strlen(base) > 0) { int needs_slash = base[strlen(base) - 1] != '/'; if (needs_slash) { snprintf(path, strlen(base) + strlen(match) + 2, "%s/%s", base, match); } else { snprintf(path, strlen(base) + strlen(match) + 2, "%s%s", base, match); } } else { strcpy(path, match); } if (is_dir) { char *tmp = (char *)XREALLOC(MTYPE_TMP, path, strlen(path) + 2); strcat(tmp, "/"); XFREE(MTYPE_TMP, path); path = tmp; } return path; } // 路径解析函数(优化后) void parse_input_text(const char *text, char **base_dir, char **prefix, int *level) { *base_dir = NULL; *prefix = NULL; // 普通路径解析逻辑(相对路径或绝对路径) const char *slash = strrchr(text, '/'); if (slash) { size_t base_len = 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, slash + 1); *level = comp_state.current_level + 1; } else { *base_dir = XSTRDUP(MTYPE_TMP, ""); *prefix = XSTRDUP(MTYPE_TMP, text); *level = 0; } } // 上下文一致性判断(优化后) int is_same_completion_context(const char *text) { if (!comp_state.persistent_path || comp_state.max_level == -1) { return 0; } char *temp_base = NULL; char *temp_prefix = NULL; int current_level = 0; parse_input_text(text, &temp_base, &temp_prefix, &current_level); printf("is_same_completion_context: text = %s, temp_base = %s, current_level = %d\n", text, temp_base, current_level); int same_path = strncmp(text, comp_state.persistent_path, strlen(comp_state.persistent_path)) == 0; // 只有在当前路径是持久化路径的子目录或匹配时才继续 int within_level = (current_level <= comp_state.max_level); XFREE(MTYPE_TMP, temp_base); XFREE(MTYPE_TMP, temp_prefix); printf("is_same_completion_context: same_path=%d, within_level=%d\n", same_path, within_level); return same_path && within_level; } // 生成当前目录下的路径匹配项 char **generate_current_dir_paths(const char *base_dir, const char *prefix, int add_empty) { const char *scan_dir = base_dir; if (!scan_dir || !*scan_dir) { scan_dir = "."; } printf("[generate] Scanning directory: '%s' with prefix: '%s'\n", scan_dir, prefix ? prefix : "(none)"); DIR *dir = opendir(scan_dir); if (!dir) { 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) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; // 跳过隐藏文件 if (name[0] == '.') continue; // 跳过软链接(直接判断 d_type) if (entry->d_type == DT_LNK) { printf("跳过软链接: %s\n", name); continue; } if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)) != 0) continue; int is_dir = (entry->d_type == DT_DIR); if (entry->d_type == DT_UNKNOWN) { char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", scan_dir, name); struct stat st; if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { is_dir = 1; } } char *new_name; if (is_dir) { new_name = (char *)XCALLOC(MTYPE_TMP, strlen(name) + 2); snprintf(new_name, strlen(name) + 2, "%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); } matches = (char **)XREALLOC(MTYPE_TMP, matches, (count + 2) * sizeof(char *)); matches[count] = NULL; if (!add_empty) { matches[count] = XSTRDUP(MTYPE_TMP, ""); matches[count + 1] = NULL; } printf("\n[generate] Found %d matches.\n", count); if (matches && matches[0]) { printf("\ngenerate_current_dir_paths: matches = "); for (int i = 0; matches[i]; i++) { printf("%s, ", matches[i]); } printf("\n"); } 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); // 拦截只敲 Tab 的情况 if (text == NULL || *text == '\0') { printf("用户未输入任何文本,只敲 Tab,不提供补全\n"); return NULL; } char current_dir[PATH_MAX]; char abs_path[PATH_MAX]; int reset_completion = 0; // <<< 新增字段,用于判断是否是 flash:/ 模式 int is_flash_mode = (strncmp(text, "flash:/", 7) == 0); int is_f_prefix = (strncmp(text, "f", 1) == 0 && strncmp(text, "flash:/", 7) != 0); // 初始化新补全 if (state == 0) { printf(" Starting new completion\n"); // 获取当前工作目录 getcwd(current_dir, sizeof(current_dir)); snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, text); 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: 11111111 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); if ((!same_context) || (reset_completion && same_reset) || (!comp_state.persistent_path || comp_state.persistent_path[0] == '\0')) { reset_completion_state(); printf("\n -----------------common reset---------------\n"); char *current_base = NULL; char *current_prefix = NULL; int current_level = 0; parse_input_text(text, &current_base, &current_prefix, &current_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) { // <<< flash:/ 开头,直接扫描 /mnt/switch/ 路径 comp_state.matches = generate_current_dir_paths("/mnt/switch/", text + 7, current_level == 0); comp_state.flash_mode = 1; } else if (is_f_prefix) { // <<< f 开头,先扫描当前目录 comp_state.matches = generate_current_dir_paths(current_base, current_prefix, current_level == 0); if (current_level == 0) { if (comp_state.matches == NULL || comp_state.matches[0] == NULL) { // <<< 当前目录没有 f 开头的,自动切换到 flash:/ 路径 comp_state.base_dir = XSTRDUP(MTYPE_TMP, "flash:/"); comp_state.matches = generate_current_dir_paths("/mnt/switch/", text, current_level == 0); comp_state.flash_mode = 1; } else { // <<< 有本地 f 开头项,追加 flash:/ 虚拟项 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 + 3) * sizeof(char*)); comp_state.matches = temp; comp_state.matches[count] = XSTRDUP(MTYPE_TMP, "flash:/"); comp_state.matches[count + 1] = XSTRDUP(MTYPE_TMP, ""); comp_state.matches[count + 2] = 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 { // <<< 非 f 开头,正常路径补全 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) { // <<< 返回 flash:/ 项 full_path = XSTRDUP(MTYPE_TMP, match); } else if (comp_state.flash_mode) { // <<< flash:/ 模式,构建 flash:/xxx/yyy 格式 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; } 上边这段代码是可以实现父目录子目录来回切换,但是里边的flash:/虚拟路径的功能,在有其他f前缀的文件时会失败,并且不能实现循环匹配当前目录下某个前缀的文件或目录 下边的代码,能遍历当前目录下匹配的前缀的文件或目录,里边的flash:/虚拟路径的功能,在有其他f前缀的文件时是正常的,但是不能实现父目录子目录来回切换: // 文件类型枚举 typedef enum { FILE_TYPE_REGULAR, FILE_TYPE_DIRECTORY, FILE_TYPE_VIRTUAL } file_type_t; // 匹配项结构 typedef struct { char *path; file_type_t type; } match_item_t; // 补全状态结构 typedef struct { char base_path[MAX_PATH_LEN]; // 原始基础路径 char current_path[MAX_PATH_LEN]; // 当前解析的路径 char prefix[MAX_PATH_LEN]; // 当前需要补全的前缀 match_item_t matches[MAX_MATCHES]; // 匹配项列表 int match_count; // 匹配项数量 int current_index; // 当前选择的匹配项索引 char last_user_input[MAX_PATH_LEN]; // 用户最后一次输入 char last_returned_match[MAX_PATH_LEN]; // 上一次返回的匹配项 file_type_t last_match_type; // 上一次返回的匹配项类型 int in_single_directory_mode; // 是否处于单目录模式 int is_virtual_path; // 当前路径是否为虚拟路径 } completion_state; static completion_state comp_state = {0}; int compare_matches(const void *a, const void *b) { const match_item_t *ma = (const match_item_t *)a; const match_item_t *mb = (const match_item_t *)b; const char *sa = ma->path; const char *sb = mb->path; char first_a = sa[0]; char first_b = sb[0]; int is_lower_a = islower((unsigned char)first_a); int is_lower_b = islower((unsigned char)first_b); if (is_lower_a && !is_lower_b) return -1; if (!is_lower_a && is_lower_b) return 1; int is_upper_a = isupper((unsigned char)first_a); int is_upper_b = isupper((unsigned char)first_b); if (is_upper_a && !is_upper_b) return -1; if (!is_upper_a && is_upper_b) return 1; return strcasecmp(sa, sb); } // 重置补全状态 void reset_completion_state() { printf("Resetting state (had %d matches)\n", comp_state.match_count); //for (int i = 0; i < comp_state.match_count; i++) { // if (comp_state.matches[i].path) { // XFREE(MTYPE_TMP, comp_state.matches[i].path); // comp_state.matches[i].path = NULL; // } //} comp_state.match_count = 0; comp_state.current_index = 0; comp_state.last_returned_match[0] = '\0'; comp_state.last_match_type = FILE_TYPE_REGULAR; comp_state.in_single_directory_mode = 0; comp_state.is_virtual_path = 0; } // 虚拟路径映射 const char *map_virtual_path(const char *virt) { static char real_path[MAX_PATH_LEN]; if (strncmp(virt, "flash:/", 7) == 0) { snprintf(real_path, sizeof(real_path), "/mnt/switch/%s", virt + 7); return real_path; } // 对于普通路径,也复制(如果需要) strncpy(real_path, virt, sizeof(real_path) - 1); real_path[sizeof(real_path) - 1] = '\0'; return real_path; } // 解析输入文本 void parse_input_text(const char *text) { //assert(text != NULL); // 如果输入为空 if (text[0] == '\0') { comp_state.current_path[0] = '\0'; comp_state.prefix[0] = '\0'; return; } // 查找最后一个 '/' const char *slash = strrchr(text, '/'); if (slash == NULL) { // 无 '/' → 整个是前缀,路径为空 comp_state.current_path[0] = '\0'; strncpy(comp_state.prefix, text, MAX_PATH_LEN - 1); comp_state.prefix[MAX_PATH_LEN - 1] = '\0'; } else { // 有 '/' → 路径是到 '/' 为止,前缀是后面部分 size_t path_len = (slash - text) + 1; // 包括 '/' size_t copy_len = path_len > MAX_PATH_LEN - 1 ? MAX_PATH_LEN - 1 : path_len; memcpy(comp_state.current_path, text, copy_len); comp_state.current_path[copy_len] = '\0'; const char *prefix_start = slash + 1; size_t prefix_len = strlen(prefix_start); if (prefix_len >= MAX_PATH_LEN) prefix_len = MAX_PATH_LEN - 1; memcpy(comp_state.prefix, prefix_start, prefix_len); comp_state.prefix[prefix_len] = '\0'; } printf("[PARSE] text='%s' → path='%s', prefix='%s'\n", text, comp_state.current_path, comp_state.prefix); } // 添加匹配项 void add_match(const char *path, file_type_t type) { if (comp_state.match_count >= MAX_MATCHES) { printf("Match list full, skipping '%s'\n", path); return; } comp_state.matches[comp_state.match_count].path = XSTRDUP(MTYPE_TMP, path); comp_state.matches[comp_state.match_count].type = type; comp_state.match_count++; printf("Added match: %s (type: %d)\n", path, type); } // 生成匹配项 void generate_matches(void) { printf("Generating matches for path='%s' prefix='%s'\n", comp_state.current_path, comp_state.prefix); // 重置匹配计数 comp_state.match_count = 0; // 添加虚拟路径选项 (仅当输入以'f'开头) if (comp_state.prefix[0] == 'f' || comp_state.prefix[0] == 'F') { add_match("flash:/", FILE_TYPE_VIRTUAL); printf("Added virtual path option: flash:/\n"); } // 获取实际扫描路径 const char *scan_path = comp_state.current_path; if (strncmp(scan_path, "flash:/", 7) == 0) { scan_path = map_virtual_path(scan_path); // 确保 map_virtual_path 返回 "/mnt/switch/..." } // 处理空路径情况 if (*scan_path == '\0') { scan_path = "."; } // 尝试打开目录 DIR *dir = opendir(scan_path); if (!dir) { printf("Failed to open dir '%s'\n", scan_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { const char *name = entry->d_name; // 跳过隐藏文件和特殊目录 if (name[0] == '.' && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)) { continue; } // 前缀匹配 if (comp_state.prefix[0] != '\0' && strncasecmp(name, comp_state.prefix, strlen(comp_state.prefix)) != 0) { continue; } // 构建完整路径 char full_path[MAX_PATH_LEN] = {0}; snprintf(full_path, sizeof(full_path), "%s%s", comp_state.current_path, name); // 构建真实路径用于 stat() char check_path[MAX_PATH_LEN] = {0}; if (strncmp(comp_state.current_path, "flash:/", 7) == 0) { snprintf(check_path, sizeof(check_path), "/mnt/switch/%s/%s", comp_state.current_path + 7, name); } else { snprintf(check_path, sizeof(check_path), "%s/%s", scan_path, name); } struct stat st; if (stat(check_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { // 目录:添加 '/' snprintf(full_path, sizeof(full_path), "%s%s/", comp_state.current_path, name); add_match(full_path, FILE_TYPE_DIRECTORY); } else { add_match(full_path, FILE_TYPE_REGULAR); } } else { add_match(full_path, FILE_TYPE_REGULAR); printf("Failed to stat '%s', assuming regular file\n", check_path); } } closedir(dir); printf("Total matches generated: %d\n", comp_state.match_count); } // 检测输入变化 int detect_input_change(const char *text) { printf("[DEBUG] detect_input_change: last_input='%s', last_match='%s', text='%s'\n", comp_state.last_user_input, comp_state.last_returned_match, text); // 情况1: 首次调用 if (comp_state.last_user_input[0] == '\0') { printf("→ First call: input changed\n"); return 1; } // 情况2: 用户在上一个补全项后继续输入(如 etc/ → etc/s) if (comp_state.last_returned_match[0] != '\0') { size_t match_len = strlen(comp_state.last_returned_match); if (match_len > 0 && match_len < MAX_PATH_LEN && strncmp(text, comp_state.last_returned_match, match_len) == 0 && strlen(text) > match_len) { printf("→ Append after match: '%s' → '%s'\n", comp_state.last_returned_match, text); return 1; } } // 情况3: 输入变短(用户删除字符) if (strlen(text) < strlen(comp_state.last_user_input)) { printf("→ Input shortened: '%s' → '%s'\n", comp_state.last_user_input, text); return 1; } // 情况4: 输入前缀不一致(全新输入) size_t last_len = strlen(comp_state.last_user_input); if (last_len > 0 && strncmp(text, comp_state.last_user_input, last_len) != 0) { printf("→ New input prefix: '%s' vs '%s'\n", text, comp_state.last_user_input); return 1; } // 默认:无显著变化 → 循环模式 printf("→ No change detected (cycling mode)\n"); return 0; } // 处理单目录模式 void handle_single_directory_mode(void) { if (comp_state.match_count != 1) return; if (comp_state.matches[0].type != FILE_TYPE_DIRECTORY) return; const char *full = comp_state.matches[0].path; const char *slash = strrchr(full, '/'); if (!slash) return; const char *dirname = slash + 1; if (strlen(dirname) == 0) return; char new_path[MAX_PATH_LEN]; snprintf(new_path, sizeof(new_path), "%s%s/", comp_state.current_path, dirname); strncpy(comp_state.current_path, new_path, MAX_PATH_LEN - 1); comp_state.prefix[0] = '\0'; reset_completion_state(); generate_matches(); // ← 必须成功生成子文件 if (comp_state.match_count == 0) { printf("→ No files in directory: '%s'\n", comp_state.current_path); // 至少添加一个占位符,避免后续崩溃 add_match(new_path, FILE_TYPE_DIRECTORY); // 保持当前目录 return; } comp_state.current_index = 0; printf("→ Auto-expanded to directory: '%s' with %d items\n", comp_state.current_path, comp_state.match_count); } // 主补全函数 char *filename_completion_function(const char *text, int state) { printf("\n=== COMPLETION CALL: text='%s' state=%d ===\n", text, state); // 检测输入文本是否变化 int input_changed = detect_input_change(text); // 更新用户输入记录 if (input_changed) { strncpy(comp_state.last_user_input, text, MAX_PATH_LEN-1); comp_state.last_user_input[MAX_PATH_LEN-1] = '\0'; } // 处理不同模式 if (input_changed) { printf("Input changed: '%s' -> '%s'\n", comp_state.last_returned_match, text); // 重置状态 reset_completion_state(); // 解析输入 parse_input_text(text); // 生成匹配项 generate_matches(); // 如果唯一目录,自动展开并加载其内容 if (comp_state.match_count == 1 && comp_state.matches[0].type == FILE_TYPE_DIRECTORY) { handle_single_directory_mode(); // ← 已经 generate_matches() // 注意:此时 match_count 是子目录内容数量 } comp_state.current_index = 0; } // 处理目录追加模式 else if (comp_state.last_match_type == FILE_TYPE_DIRECTORY && comp_state.last_returned_match[0] != '\0' && strstr(text, comp_state.last_returned_match) == text && strlen(text) > strlen(comp_state.last_returned_match)) { printf("Directory append mode: '%s' + '%s'\n", comp_state.last_returned_match, text + strlen(comp_state.last_returned_match)); // 设置当前路径为上次返回的匹配项 strncpy(comp_state.current_path, comp_state.last_returned_match, MAX_PATH_LEN-1); // 提取新前缀 size_t prefix_start = strlen(comp_state.last_returned_match); size_t prefix_size = strlen(text) - prefix_start; if (prefix_size >= MAX_PATH_LEN) prefix_size = MAX_PATH_LEN - 1; strncpy(comp_state.prefix, text + prefix_start, prefix_size); comp_state.prefix[prefix_size] = '\0'; // 重新生成匹配项 reset_completion_state(); generate_matches(); comp_state.current_index = 0; } // 处理循环模式 else { if (comp_state.match_count == 0) { return NULL; } comp_state.current_index = (comp_state.current_index + 1) % comp_state.match_count; printf("Cycling to index %d\n", comp_state.current_index); } // 返回当前匹配项 if (comp_state.match_count == 0) { return NULL; } printf("→ comp_state.match_count = %d, comp_state.current_index = %d\n", comp_state.match_count, comp_state.current_index); const char *path = comp_state.matches[comp_state.current_index].path; char *ret = XSTRDUP(MTYPE_TMP, path); // ✅ 关键:只要返回了补全项,就更新 last_returned_match // 并且,如果这是自动补全(唯一匹配),也要更新 last_user_input! strncpy(comp_state.last_returned_match, ret, MAX_PATH_LEN - 1); comp_state.last_returned_match[MAX_PATH_LEN - 1] = '\0'; // ✅ 新增:如果 match_count == 1,说明自动补全,用户“输入”变成了补全结果 if (input_changed && comp_state.match_count == 1) { strncpy(comp_state.last_user_input, ret, MAX_PATH_LEN - 1); comp_state.last_user_input[MAX_PATH_LEN - 1] = '\0'; printf("→ Auto-completed. Updating last_user_input to: '%s'\n", comp_state.last_user_input); } return ret; } 将这两段代码的逻辑重新整合一下,整合成一份代码,实现所有的功能 (1)能按前缀匹配,并且能连续tab,然后显示匹配的数组的元素 (2)比如当前路径下有ea.txt,etc目录,然后我用e前缀补全,则先循环显示成ea.txt etc/(目录后自动加/),然后我连续tab,循环显示到etc/时,我再后边加个前缀s,则是etc/s,然后此时我再按tab,则是要补全etc/下的s为前缀的文件或目录(如果有多个,也是按照(1)的防止,循环显示),这里要求的是父目录与子目录灵活切换,灵活补全,这个要做好上下文状态切换 (3)在匹配前缀为f的基础上,增加个虚拟路径flash:/,这个实际上是/mnt/switch/根路径,然后显示的时候要是flash:/;也就是说,我输入的是f前缀,则先扫描当前路径下的f为前缀的文件或目录,然后再最后增加个虚拟的flash:/,如果没有其他f为前缀的文件或目录,则自动回显成flash:/
最新发布
08-14
#define MAX_PATH_LEN 1024 #define MAX_MATCHES 1024 #define DEBUG_PRINT(fmt, ...) \ do { fprintf(stderr, "[DEBUG] %s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); } while (0) // 文件类型枚举 typedef enum { FILE_TYPE_REGULAR, FILE_TYPE_DIRECTORY, FILE_TYPE_VIRTUAL } file_type_t; // 匹配项结构 typedef struct { char *path; file_type_t type; } match_item_t; // 补全状态结构 typedef struct { char base_path[MAX_PATH_LEN]; // 原始基础路径 char current_path[MAX_PATH_LEN]; // 当前解析的路径 char prefix[MAX_PATH_LEN]; // 当前需要补全的前缀 match_item_t matches[MAX_MATCHES]; // 匹配项列表 int match_count; // 匹配项数量 int current_index; // 当前选择的匹配项索引 char last_user_input[MAX_PATH_LEN]; // 用户最后一次输入 char last_returned_match[MAX_PATH_LEN]; // 上一次返回的匹配项 file_type_t last_match_type; // 上一次返回的匹配项类型 int in_single_directory_mode; // 是否处于单目录模式 int is_virtual_path; // 当前路径是否为虚拟路径 } completion_state; static completion_state comp_state = {0}; int compare_matches(const void *a, const void *b) { const match_item_t *ma = (const match_item_t *)a; const match_item_t *mb = (const match_item_t *)b; const char *sa = ma->path; const char *sb = mb->path; char first_a = sa[0]; char first_b = sb[0]; int is_lower_a = islower((unsigned char)first_a); int is_lower_b = islower((unsigned char)first_b); if (is_lower_a && !is_lower_b) return -1; if (!is_lower_a && is_lower_b) return 1; int is_upper_a = isupper((unsigned char)first_a); int is_upper_b = isupper((unsigned char)first_b); if (is_upper_a && !is_upper_b) return -1; if (!is_upper_a && is_upper_b) return 1; return strcasecmp(sa, sb); } // 重置补全状态 void reset_completion_state() { printf("Resetting state (had %d matches)\n", comp_state.match_count); //for (int i = 0; i < comp_state.match_count; i++) { // if (comp_state.matches[i].path) { // XFREE(MTYPE_TMP, comp_state.matches[i].path); // comp_state.matches[i].path = NULL; // } //} comp_state.match_count = 0; comp_state.current_index = 0; comp_state.last_returned_match[0] = '\0'; comp_state.last_match_type = FILE_TYPE_REGULAR; comp_state.in_single_directory_mode = 0; comp_state.is_virtual_path = 0; } // 虚拟路径映射 const char *map_virtual_path(const char *virt) { static char real_path[MAX_PATH_LEN]; if (strncmp(virt, "flash:/", 7) == 0) { snprintf(real_path, sizeof(real_path), "/mnt/switch/%s", virt + 7); return real_path; } // 对于普通路径,也复制(如果需要) strncpy(real_path, virt, sizeof(real_path) - 1); real_path[sizeof(real_path) - 1] = '\0'; return real_path; } // 解析输入文本 void parse_input_text(const char *text) { //assert(text != NULL); // 如果输入为空 if (text[0] == '\0') { comp_state.current_path[0] = '\0'; comp_state.prefix[0] = '\0'; return; } // 查找最后一个 '/' const char *slash = strrchr(text, '/'); if (slash == NULL) { // 无 '/' → 整个是前缀,路径为空 comp_state.current_path[0] = '\0'; strncpy(comp_state.prefix, text, MAX_PATH_LEN - 1); comp_state.prefix[MAX_PATH_LEN - 1] = '\0'; } else { // 有 '/' → 路径是到 '/' 为止,前缀是后面部分 size_t path_len = (slash - text) + 1; // 包括 '/' size_t copy_len = path_len > MAX_PATH_LEN - 1 ? MAX_PATH_LEN - 1 : path_len; memcpy(comp_state.current_path, text, copy_len); comp_state.current_path[copy_len] = '\0'; const char *prefix_start = slash + 1; size_t prefix_len = strlen(prefix_start); if (prefix_len >= MAX_PATH_LEN) prefix_len = MAX_PATH_LEN - 1; memcpy(comp_state.prefix, prefix_start, prefix_len); comp_state.prefix[prefix_len] = '\0'; } printf("[PARSE] text='%s' → path='%s', prefix='%s'\n", text, comp_state.current_path, comp_state.prefix); } // 添加匹配项 void add_match(const char *path, file_type_t type) { if (comp_state.match_count >= MAX_MATCHES) { printf("Match list full, skipping '%s'\n", path); return; } comp_state.matches[comp_state.match_count].path = XSTRDUP(MTYPE_TMP, path); comp_state.matches[comp_state.match_count].type = type; comp_state.match_count++; printf("Added match: %s (type: %d)\n", path, type); } // 生成匹配项 void generate_matches(void) { printf("Generating matches for path='%s' prefix='%s'\n", comp_state.current_path, comp_state.prefix); // 重置匹配计数 comp_state.match_count = 0; // 添加虚拟路径选项 (仅当输入以'f'开头) if (comp_state.prefix[0] == 'f' || comp_state.prefix[0] == 'F') { add_match("flash:/", FILE_TYPE_VIRTUAL); printf("Added virtual path option: flash:/\n"); } // 获取实际扫描路径 const char *scan_path = comp_state.current_path; if (strncmp(scan_path, "flash:/", 7) == 0) { scan_path = map_virtual_path(scan_path); // 确保 map_virtual_path 返回 "/mnt/switch/..." } // 处理空路径情况 if (*scan_path == '\0') { scan_path = "."; } // 尝试打开目录 DIR *dir = opendir(scan_path); if (!dir) { printf("Failed to open dir '%s'\n", scan_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { const char *name = entry->d_name; // 跳过隐藏文件和特殊目录 if (name[0] == '.' && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)) { continue; } // 前缀匹配 if (comp_state.prefix[0] != '\0' && strncasecmp(name, comp_state.prefix, strlen(comp_state.prefix)) != 0) { continue; } // 构建完整路径 char full_path[MAX_PATH_LEN] = {0}; snprintf(full_path, sizeof(full_path), "%s%s", comp_state.current_path, name); // 构建真实路径用于 stat() char check_path[MAX_PATH_LEN] = {0}; if (strncmp(comp_state.current_path, "flash:/", 7) == 0) { snprintf(check_path, sizeof(check_path), "/mnt/switch/%s/%s", comp_state.current_path + 7, name); } else { snprintf(check_path, sizeof(check_path), "%s/%s", scan_path, name); } struct stat st; if (stat(check_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { // 目录:添加 '/' snprintf(full_path, sizeof(full_path), "%s%s/", comp_state.current_path, name); add_match(full_path, FILE_TYPE_DIRECTORY); } else { add_match(full_path, FILE_TYPE_REGULAR); } } else { add_match(full_path, FILE_TYPE_REGULAR); printf("Failed to stat '%s', assuming regular file\n", check_path); } } closedir(dir); printf("Total matches generated: %d\n", comp_state.match_count); } // 检测输入变化 int detect_input_change(const char *text) { printf("[DEBUG] detect_input_change: last_input='%s', last_match='%s', text='%s'\n", comp_state.last_user_input, comp_state.last_returned_match, text); // 情况1: 首次调用 if (comp_state.last_user_input[0] == '\0') { printf("→ First call: input changed\n"); return 1; } // 情况2: 用户在上一个补全项后继续输入(如 etc/ → etc/s) if (comp_state.last_returned_match[0] != '\0') { size_t match_len = strlen(comp_state.last_returned_match); if (match_len > 0 && match_len < MAX_PATH_LEN && strncmp(text, comp_state.last_returned_match, match_len) == 0 && strlen(text) > match_len) { printf("→ Append after match: '%s' → '%s'\n", comp_state.last_returned_match, text); return 1; } } // 情况3: 输入变短(用户删除字符) if (strlen(text) < strlen(comp_state.last_user_input)) { printf("→ Input shortened: '%s' → '%s'\n", comp_state.last_user_input, text); return 1; } // 情况4: 输入前缀不一致(全新输入) size_t last_len = strlen(comp_state.last_user_input); if (last_len > 0 && strncmp(text, comp_state.last_user_input, last_len) != 0) { printf("→ New input prefix: '%s' vs '%s'\n", text, comp_state.last_user_input); return 1; } // 默认:无显著变化 → 循环模式 printf("→ No change detected (cycling mode)\n"); return 0; } // 处理单目录模式 void handle_single_directory_mode(void) { if (comp_state.match_count != 1) return; if (comp_state.matches[0].type != FILE_TYPE_DIRECTORY) return; const char *full = comp_state.matches[0].path; const char *slash = strrchr(full, '/'); if (!slash) return; const char *dirname = slash + 1; if (strlen(dirname) == 0) return; char new_path[MAX_PATH_LEN]; snprintf(new_path, sizeof(new_path), "%s%s/", comp_state.current_path, dirname); strncpy(comp_state.current_path, new_path, MAX_PATH_LEN - 1); comp_state.prefix[0] = '\0'; reset_completion_state(); generate_matches(); // ← 必须成功生成子文件 if (comp_state.match_count == 0) { printf("→ No files in directory: '%s'\n", comp_state.current_path); // 至少添加一个占位符,避免后续崩溃 add_match(new_path, FILE_TYPE_DIRECTORY); // 保持当前目录 return; } comp_state.current_index = 0; printf("→ Auto-expanded to directory: '%s' with %d items\n", comp_state.current_path, comp_state.match_count); } // 主补全函数 char *filename_completion_function(const char *text, int state) { printf("\n=== COMPLETION CALL: text='%s' state=%d ===\n", text, state); // 检测输入文本是否变化 int input_changed = detect_input_change(text); // 更新用户输入记录 if (input_changed) { strncpy(comp_state.last_user_input, text, MAX_PATH_LEN-1); comp_state.last_user_input[MAX_PATH_LEN-1] = '\0'; } // 处理不同模式 if (input_changed) { printf("Input changed: '%s' -> '%s'\n", comp_state.last_returned_match, text); // 重置状态 reset_completion_state(); // 解析输入 parse_input_text(text); // 生成匹配项 generate_matches(); // 如果唯一目录,自动展开并加载其内容 if (comp_state.match_count == 1 && comp_state.matches[0].type == FILE_TYPE_DIRECTORY) { handle_single_directory_mode(); // ← 已经 generate_matches() // 注意:此时 match_count 是子目录内容数量 } comp_state.current_index = 0; } // 处理目录追加模式 else if (comp_state.last_match_type == FILE_TYPE_DIRECTORY && comp_state.last_returned_match[0] != '\0' && strstr(text, comp_state.last_returned_match) == text && strlen(text) > strlen(comp_state.last_returned_match)) { printf("Directory append mode: '%s' + '%s'\n", comp_state.last_returned_match, text + strlen(comp_state.last_returned_match)); // 设置当前路径为上次返回的匹配项 strncpy(comp_state.current_path, comp_state.last_returned_match, MAX_PATH_LEN-1); // 提取新前缀 size_t prefix_start = strlen(comp_state.last_returned_match); size_t prefix_size = strlen(text) - prefix_start; if (prefix_size >= MAX_PATH_LEN) prefix_size = MAX_PATH_LEN - 1; strncpy(comp_state.prefix, text + prefix_start, prefix_size); comp_state.prefix[prefix_size] = '\0'; // 重新生成匹配项 reset_completion_state(); generate_matches(); comp_state.current_index = 0; } // 处理循环模式 else { if (comp_state.match_count == 0) { return NULL; } comp_state.current_index = (comp_state.current_index + 1) % comp_state.match_count; printf("Cycling to index %d\n", comp_state.current_index); } // 返回当前匹配项 if (comp_state.match_count == 0) { return NULL; } printf("→ comp_state.match_count = %d, comp_state.current_index = %d\n", comp_state.match_count, comp_state.current_index); const char *path = comp_state.matches[comp_state.current_index].path; char *ret = XSTRDUP(MTYPE_TMP, path); // ✅ 关键:只要返回了补全项,就更新 last_returned_match // 并且,如果这是自动补全(唯一匹配),也要更新 last_user_input! strncpy(comp_state.last_returned_match, ret, MAX_PATH_LEN - 1); comp_state.last_returned_match[MAX_PATH_LEN - 1] = '\0'; // ✅ 新增:如果 match_count == 1,说明自动补全,用户“输入”变成了补全结果 if (input_changed && comp_state.match_count == 1) { strncpy(comp_state.last_user_input, ret, MAX_PATH_LEN - 1); comp_state.last_user_input[MAX_PATH_LEN - 1] = '\0'; printf("→ Auto-completed. Updating last_user_input to: '%s'\n", comp_state.last_user_input); } return ret; } 现在是这样实现的,看看哪里还有问题?
08-14
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; // 新增:最大层级限制 int initialized; int flash_mode; int flash_selected; // flash:/是否被选择 } 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 = -1, .initialized = 0, .flash_mode = 0, .flash_selected = 0 }; // 重置补全状态 void reset_completion_state() { if (comp_state.matches) { for (int i = 0; comp_state.matches[i]; 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 = -1; comp_state.initialized = 0; comp_state.flash_mode = 0; comp_state.flash_selected = 0; } // 路径比较函数(目录优先) int compare_paths(const void *a, const void *b) { const char *sa = *(const char **)a; const char *sb = *(const char **)b; // 获取第一个字符 char first_a = sa[0]; char first_b = sb[0]; // 1. 处理小写字母开头的情况 int is_lower_a = islower(first_a); int is_lower_b = islower(first_b); if (is_lower_a && !is_lower_b) return -1; // 小写优先 if (!is_lower_a && is_lower_b) return 1; // 小写优先 // 2. 处理大写字母开头的情况 int is_upper_a = isupper(first_a); int is_upper_b = isupper(first_b); if (is_upper_a && !is_upper_b) return -1; // 大写优先于非字母 if (!is_upper_a && is_upper_b) return 1; // 大写优先于非字母 // 3. 同类别比较(都是小写或都是大写或都是非字母) return strcasecmp(sa, sb); // 不区分大小写比较 } // 构建完整路径(自动加 /) char *build_completion_path(const char *base, const char *match, int is_dir) { char *path = (char *)XCALLOC(MTYPE_TMP, strlen(base) + strlen(match) + 2); if (base && strlen(base) > 0) { int needs_slash = base[strlen(base) - 1] != '/'; if (needs_slash) { snprintf(path, strlen(base) + strlen(match) + 2, "%s/%s", base, match); } else { snprintf(path, strlen(base) + strlen(match) + 2, "%s%s", base, match); } } else { strcpy(path, match); } if (is_dir) { char *tmp = (char *)XREALLOC(MTYPE_TMP, path, strlen(path) + 2); strcat(tmp, "/"); XFREE(MTYPE_TMP, path); path = tmp; } return path; } // 路径解析函数(优化后) void parse_input_text(const char *text, char **base_dir, char **prefix, int *level) { *base_dir = NULL; *prefix = NULL; // 检查是否在flash模式且输入以flash:/开头 if (comp_state.flash_mode && strncmp(text, "flash:/", 7) == 0) { *base_dir = XSTRDUP(MTYPE_TMP, "/mnt/switch/"); const char *relative = text + 7; if (*relative == '\0') { *prefix = XSTRDUP(MTYPE_TMP, ""); } else { const char *last_slash = strrchr(relative, '/'); if (last_slash) { size_t base_len = last_slash - relative + 1; *prefix = (char *)XCALLOC(MTYPE_TMP, base_len + 1); strncpy(*prefix, relative, base_len); (*prefix)[base_len] = '\0'; } else { *prefix = XSTRDUP(MTYPE_TMP, relative); } } *level = comp_state.current_level + 1; return; } // 普通路径解析逻辑(相对路径或绝对路径) const char *slash = strrchr(text, '/'); if (slash) { size_t base_len = 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, slash + 1); *level = comp_state.current_level + 1; } else { *base_dir = XSTRDUP(MTYPE_TMP, ""); *prefix = XSTRDUP(MTYPE_TMP, text); *level = 0; } } // 上下文一致性判断(优化后) int is_same_completion_context(const char *text) { if (!comp_state.persistent_path || comp_state.max_level == -1) { return 0; } char *temp_base = NULL; char *temp_prefix = NULL; int current_level = 0; parse_input_text(text, &temp_base, &temp_prefix, &current_level); printf("is_same_completion_context: text = %s, temp_base = %s, current_level = %d\n", text, temp_base, current_level); int same_path = strncmp(text, comp_state.persistent_path, strlen(comp_state.persistent_path)) == 0; // 只有在当前路径是持久化路径的子目录或匹配时才继续 int within_level = (current_level <= comp_state.max_level); XFREE(MTYPE_TMP, temp_base); XFREE(MTYPE_TMP, temp_prefix); printf("is_same_completion_context: same_path=%d, within_level=%d\n", same_path, within_level); return same_path && within_level; } // 生成当前目录下的路径匹配项 char **generate_current_dir_paths(const char *base_dir, const char *prefix, int add_empty) { const char *scan_dir = base_dir; if (!scan_dir || !*scan_dir) { scan_dir = "."; } printf("[generate] Scanning directory: '%s' with prefix: '%s'\n", scan_dir, prefix ? prefix : "(none)"); DIR *dir = opendir(scan_dir); if (!dir) { 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; int prefix_len = prefix ? strlen(prefix) : 0; // 检查是否需要特殊处理flash int should_add_flash = 0; if (prefix && prefix_len > 0 && prefix[0] == 'f') { // 检查是否匹配flash前缀 const char *flash_prefix = "flash"; int match_len = prefix_len > 5 ? 5 : prefix_len; if (strncmp(prefix, flash_prefix, match_len) == 0) { should_add_flash = 1; } } while ((entry = readdir(dir)) != NULL) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; // 跳过隐藏文件 if (name[0] == '.') continue; // 跳过软链接(直接判断 d_type) if (entry->d_type == DT_LNK) { printf("跳过软链接: %s\n", name); continue; } if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)) != 0) continue; int is_dir = (entry->d_type == DT_DIR); if (entry->d_type == DT_UNKNOWN) { char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", scan_dir, name); struct stat st; if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { is_dir = 1; } } char *new_name; if (is_dir) { new_name = (char *)XCALLOC(MTYPE_TMP, strlen(name) + 2); snprintf(new_name, strlen(name) + 2, "%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); // 添加虚拟flash:/路径(如果需要) if (should_add_flash && !comp_state.flash_mode) { int flash_exists = 0; for (int i = 0; i < count; i++) { if (strcmp(matches[i], "flash:/") == 0) { flash_exists = 1; break; } } if (!flash_exists) { if (count >= capacity - 1) { capacity += 1; matches = (char **)XREALLOC(MTYPE_TMP, matches, capacity * sizeof(char *)); } matches[count++] = XSTRDUP(MTYPE_TMP, "flash:/"); } } if (count > 0) { qsort(matches, count, sizeof(char *), compare_paths); } matches = (char **)XREALLOC(MTYPE_TMP, matches, (count + 2) * sizeof(char *)); matches[count] = NULL; if (!add_empty) { matches[count] = XSTRDUP(MTYPE_TMP, ""); matches[count + 1] = NULL; } printf("\n[generate] Found %d matches.\n", count); if (matches && matches[0]) { printf("\ngenerate_current_dir_paths: matches = "); for (int i = 0; matches[i]; i++) { printf("%s, ", matches[i]); } printf("\n"); } 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); printf("000000000000000\n"); // 特殊处理:当输入是"f"时,优先返回flash:/和当前目录的匹配项 if (state == 0 && strcmp(text, "f") == 0) { reset_completion_state(); printf("\n--- Special 'f' handling ---\n"); // 设置状态 comp_state.base_dir = XSTRDUP(MTYPE_TMP, ""); comp_state.current_text = XSTRDUP(MTYPE_TMP, text); comp_state.original_prefix = XSTRDUP(MTYPE_TMP, "f"); comp_state.persistent_path = XSTRDUP(MTYPE_TMP, "."); // 生成当前目录下的匹配项 comp_state.matches = generate_current_dir_paths(".", "f", 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); } // 确保flash:/在列表中 int flash_exists = 0; for (int i = 0; i < comp_state.count; i++) { if (strcmp(comp_state.matches[i], "flash:/") == 0) { flash_exists = 1; break; } } if (!flash_exists) { // 添加flash:/到列表 comp_state.matches = XREALLOC(MTYPE_TMP, comp_state.matches, (comp_state.count + 2) * sizeof(char *)); comp_state.matches[comp_state.count] = XSTRDUP(MTYPE_TMP, "flash:/"); comp_state.matches[comp_state.count + 1] = NULL; comp_state.count++; // 重新排序 qsort(comp_state.matches, comp_state.count, sizeof(char *), compare_paths); } comp_state.last_state = -1; } // 处理flash:/路径 if (strncmp(text, "flash:/", 7) == 0) { if (state == 0) { reset_completion_state(); printf("\n--- Flash mode ---\n"); comp_state.flash_mode = 1; comp_state.base_dir = XSTRDUP(MTYPE_TMP, "/mnt/switch/"); comp_state.current_text = XSTRDUP(MTYPE_TMP, text); // 提取前缀(去掉flash:/部分) const char *sub_path = text + 7; comp_state.original_prefix = XSTRDUP(MTYPE_TMP, *sub_path ? sub_path : ""); comp_state.matches = generate_current_dir_paths("/mnt/switch/", comp_state.original_prefix, 1); comp_state.count = 0; if (comp_state.matches) { while (comp_state.matches[comp_state.count]) { comp_state.count++; } } comp_state.last_state = -1; } // 获取下一个匹配项 if (comp_state.count == 0) 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]; // 构建完整flash路径 char *full_path = XCALLOC(MTYPE_TMP, 8 + strlen(match) + 1); // 8 for "flash:/" snprintf(full_path, 8 + strlen(match) + 1, "flash:/%s", match); printf("\n000filename_completion_function: full_path = '%s'\n", full_path); return full_path; } char current_dir[PATH_MAX]; char abs_path[PATH_MAX]; int reset_completion = 0; // 初始化新补全 if (state == 0) { printf(" Starting new completion\n"); int same_context = is_same_completion_context(text); printf(" Same context: %d\n", same_context); getcwd(current_dir, sizeof(current_dir)); snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, text); 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("\n555filename_completion_function: same_reset = %d\n", same_reset); break; } } } } // 释放临时解析结果 //if (tmp_base) XFREE(MTYPE_TMP, tmp_base); //if (tmp_prefix) XFREE(MTYPE_TMP, tmp_prefix); printf("\n666filename_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("\n777filename_completion_function: 11111111 comp_state.persistent_path = '%s'\n", comp_state.persistent_path); if ((!same_context) || (reset_completion && same_reset) || (!comp_state.persistent_path || comp_state.persistent_path[0] == '\0')) { reset_completion_state(); printf("\n -----------------common reset---------------\n"); char *current_base = NULL; char *current_prefix = NULL; int current_level = 0; parse_input_text(text, &current_base, &current_prefix, &current_level); printf("\n888filename_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; } // 生成当前目录的补全项 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; // 重置索引 // 释放临时解析结果 //if (current_base) XFREE(MTYPE_TMP, current_base); //if (current_prefix) XFREE(MTYPE_TMP, current_prefix); } } printf("\n999filename_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 (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; } 我当前目录下有: Directory of flash: 0 drw- - Jan 01 1970 00:04:56 cfg 1 -rw- 2418 Jan 01 1970 00:08:34 ea.txt 2 drw- - Jan 01 1970 00:00:54 etc 3 drw- - Jan 01 1970 00:02:04 fa 4 drw- - Jan 01 1970 00:01:26 home 5 drw- - Jan 01 1970 00:15:14 installers 6 drw- - Jan 01 1970 00:04:55 logfile 然后我输入的是dir e,也就是想补全e开头的文件,想实现的效果是: dir ea.txt dir etc/ dir ea.txt 这样循环显示,但是现在有问题,会有异常退出,打印如下: <dahua>dir e 【cmlsh_completion】text = e [MATCHES] Called with text='e' [COMP] Entering: text='e', state = 0, last_state = -1 000000000000000 Starting new completion Same context: 0 111filename_completion_function: reset_completion = 1, abs_path = '/mnt/switch/e' 666filename_completion_function: current_dir = '/mnt/switch', text = 'e', abs_path = '/mnt/switch/e', reset_completion = 1, same_reset = 0 777filename_completion_function: 11111111 comp_state.persistent_path = '(null)' -----------------common reset--------------- 888filename_completion_function: current_base = '', current_prefix = 'e', current_level = 0 [generate] Scanning directory: '.' with prefix: 'e' 跳过软链接: main_uImage [generate] Found 2 matches. generate_current_dir_paths: matches = ea.txt, etc/, Generated 2 matches 999filename_completion_function: comp_state.base_dir = '', comp_state.current_text = 'e', comp_state.original_prefix = 'e', comp_state.persistent_path = '' [COMP] Returning: 'ea.txt' [MATCHES] Returning single match: 'ea.txt' a.txt 【cmlsh_completion】text = ea.txt [MATCHES] Called with text='ea.txt' [COMP] Entering: text='ea.txt', state = 0, last_state = 0 000000000000000 Starting new completion is_same_completion_context: text = ea.txt, temp_base = , current_level = 0 is_same_completion_context: same_path=1, within_level=1 Same context: 1 111filename_completion_function: reset_completion = 0, abs_path = '/mnt/ Username: admin Password: 分析一下是什么问题?
08-12
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值