📄 pcache.c
字号:
* scan it for files of interest. */ if(node->relative) { rst_CacheMem(node->mem); if(pca_scan_dir(pc, node->dir, node->mem) < 1) continue; node->files = node->mem->files; node->nfile = node->mem->nfiles; };/* * If needed, make a copy of the file-name being matched, with * escapes removed. Note that we need to do this anew every loop * iteration, because the above call to pca_scan_dir() uses * pc->path. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1;/* * The directory entries are sorted, so we can perform a binary * search for an instance of the prefix being searched for. */ bot = 0; top = node->nfile - 1; while(top >= bot) { int mid = (top + bot)/2; int test = strncmp(node->files[mid]+1, prefix, prefix_len); if(test > 0) top = mid - 1; else if(test < 0) bot = mid + 1; else { top = bot = mid; break; }; };/* * If we found a match, look to see if any of its neigbors also match. */ if(top == bot) { while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) ; while(++top < node->nfile && strncmp(node->files[top]+1, prefix, prefix_len) == 0) ;/* * We will have gone one too far in each direction. */ bot++; top--;/* * Add the completions to the list after checking them against the * callers requirements. */ for( ; bot<=top; bot++) { char *match = node->files[bot];/* * Form the full pathname of the file. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { strcpy(pc->errmsg, "Insufficient memory to complete file name"); return 1; };/* * Should the file be included in the list of completions? */ if(!pc->check_fn || match[0] == PCA_F_WANTED || (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { match[0] = PCA_F_WANTED;/* * Copy the completion suffix into the work pathname pc->path->name, * adding backslash escapes if needed. */ if(pca_prepare_suffix(pc, match + 1 + prefix_len, ppc->escaped)) return 1;/* * Record the completion. */ if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, "", " ")) return 1;/* * The file was rejected by the application. */ } else { match[0] = PCA_F_IGNORE; }; }; }; };/* * We now need to search for subdirectories of the current directory which * have matching prefixes. First, if needed, make a copy of the word being * matched, with escapes removed. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1;/* * Now open the current directory. */ if(_dr_open_dir(pc->dr, FS_PWD, NULL)) return 0;/* * Scan the current directory for sub-directories whos names start with * the prefix that we are completing. */ while((filename = _dr_next_file(pc->dr))) {/* * Does the latest filename match the prefix, and is it a directory? */ if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){/* * Record the completion. */ if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, FS_DIR_SEP, FS_DIR_SEP)) return 1;/* * The prefix in pc->path->name will have been overwritten by * pca_prepare_suffix(). Restore it here. */ prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); if(!prefix) return 1; }; }; _dr_close_dir(pc->dr); return 0;}/*....................................................................... * Using the work buffer pc->path, make a suitably escaped copy of a * given completion suffix, ready to be passed to cpl_add_completion(). * * Input: * pc PathCache * The filename cache resource object. * suffix char * The suffix to be copied. * add_escapes int If true, escape special characters. * Output: * return int 0 - OK. * 1 - Error. */static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes){ const char *sptr; /* A pointer into suffix[] */ int nbsl; /* The number of backslashes to add to the suffix */ int i;/* * How long is the suffix? */ int suffix_len = strlen(suffix);/* * Clear the work buffer. */ _pn_clear_path(pc->path);/* * Count the number of backslashes that will have to be added to * escape spaces, tabs, backslashes and wildcard characters. */ nbsl = 0; if(add_escapes) { for(sptr = suffix; *sptr; sptr++) { switch(*sptr) { case ' ': case '\t': case '\\': case '*': case '?': case '[': nbsl++; break; }; }; };/* * Arrange for the output path buffer to have sufficient room for the * both the suffix and any backslashes that have to be inserted. */ if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { strcpy(pc->errmsg, "Insufficient memory to complete file name"); return 1; };/* * If the suffix doesn't need any escapes, copy it directly into the * work buffer. */ if(nbsl==0) { strcpy(pc->path->name, suffix); } else {/* * Make a copy with special characters escaped? */ if(nbsl > 0) { const char *src = suffix; char *dst = pc->path->name; for(i=0; i<suffix_len; i++) { switch(*src) { case ' ': case '\t': case '\\': case '*': case '?': case '[': *dst++ = '\\'; }; *dst++ = *src++; }; *dst = '\0'; }; }; return 0;}/*....................................................................... * Return non-zero if the specified string appears to start with a pathname. * * Input: * prefix const char * The filename prefix to check. * prefix_len int The length of the prefix. * Output: * return int 0 - Doesn't start with a path name. * 1 - Does start with a path name. */static int cpa_cmd_contains_path(const char *prefix, int prefix_len){ int i;/* * If the filename starts with a ~, then this implies a ~username * expression, which constitutes a pathname. */ if(*prefix == '~') return 1;/* * If the filename starts with the root directory, then it obviously * starts with a pathname. */ if(prefix_len >= FS_ROOT_DIR_LEN && strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) return 1;/* * Search the prefix for directory separators, returning as soon as * any are found, since their presence indicates that the filename * starts with a pathname specification (valid or otherwise). */ for(i=0; i<prefix_len; i++) { if(prefix_len - i >= FS_DIR_SEP_LEN && strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) return 1; };/* * The file name doesn't appear to start with a pathname specification. */ return 0;}/*....................................................................... * If needed make a new copy of the prefix being matched, in pc->path->name, * but with escapes removed. If no escapes are to be removed, simply return * the original prefix string. * * Input: * pc PathCache * The cache being searched. * prefix const char * The prefix to be processed. * prefix_len size_t The length of the prefix. * escaped int If true, return a copy with escapes removed. * Output: * return const char * The prepared prefix, or NULL on error, in * which case an error message will have been * left in pc->errmsg. */static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped){/* * Make a copy with escapes removed? */ if(escaped) { _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { strcpy(pc->errmsg, "Insufficient memory to complete filename"); return NULL; }; return pc->path->name; }; return prefix;}/*....................................................................... * If backslashes in the filename should be treated as literal * characters, call the following function with literal=1. Otherwise * the default is to treat them as escape characters, used for escaping * spaces etc.. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * literal int Pass non-zero here to enable literal interpretation * of backslashes. Pass 0 to turn off literal * interpretation. */void ppc_literal_escapes(PcaPathConf *ppc, int literal){ if(ppc) ppc->escaped = !literal;}/*....................................................................... * Call this function if you know where the index at which the * filename prefix starts in the input line. Otherwise by default, * or if you specify start_index to be -1, the filename is taken * to start after the first unescaped space preceding the cursor, * or the start of the line, which ever comes first. * * Input: * ppc PcaPathConf * The pca_path_completions() configuration object * to be configured. * start_index int The index of the start of the filename in * the input line, or -1 to select the default. */void ppc_file_start(PcaPathConf *ppc, int start_index){ if(ppc) ppc->file_start = start_index;}/*....................................................................... * Expand any ~user expression found at the start of a path, leaving * either an empty string in pc->path if there is no ~user expression, * or the corresponding home directory. * * Input: * pc PathCache * The filename cache. * path const char * The path to expand. * pathlen int The max number of characters to look at in path[]. * literal int If true, treat backslashes as literal characters * instead of escapes. * Input/Output: * endp const char * A pointer to the next unprocessed character in * path[] will be assigned to *endp. * Output: * return int 0 - OK * 1 - Error (a description will have been placed * in pc->errmsg[]). */static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp){ const char *pptr = path; /* A pointer into path[] */ const char *homedir=NULL; /* A home directory *//* * Clear the pathname buffer. */ _pn_clear_path(pc->path);/* * If the first character is a tilde, then perform home-directory * interpolation. */ if(*pptr == '~') {/* * Skip the tilde character and attempt to read the username that follows * it, into pc->usrnam[]. */ if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) return 1;/* * Attempt to lookup the home directory of the user. */ homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); if(!homedir) { strncpy(pc->errmsg, _hd_last_home_dir_error(pc->home), ERRLEN); pc->errmsg[ERRLEN] = '\0'; return 1; };/* * Append the home directory to the pathname string. */ if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { strcpy(pc->errmsg, "Insufficient memory for home directory expansion"); return 1; }; };/* * ~user and ~ are usually followed by a directory separator to * separate them from the file contained in the home directory. * If the home directory is the root directory, then we don't want * to follow the home directory by a directory separator, so we should * skip over it so that it doesn't get copied into the output pathname */ if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && (pptr-path) + FS_DIR_SEP_LEN < pathlen && strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { pptr += FS_DIR_SEP_LEN; };/* * Return a pointer to the next unprocessed character. */ *endp = pptr; return 0;}/*....................................................................... * Clear the filename status codes that are recorded before each filename * in the cache. * * Input: * pc PathCache * The filename cache. */static void pca_remove_marks(PathCache *pc){ PathNode *node; /* A node in the list of directories in the path */ int i;/* * Traverse the absolute directories of the path, clearing the * filename status marks that precede each filename. */ for(node=pc->head; node; node=node->next) { if(!node->relative) { for(i=0; i<node->nfile; i++) *node->files[i] = PCA_F_ENIGMA; }; }; return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -