📄 pcache.c
字号:
pc->tail = node; } else { pc->head = pc->tail = node; }; return 0;}/*....................................................................... * Scan a given directory for files of interest, record their names * in mem->sg and append pointers to them to the mem->files[] array. * * Input: * pc PathCache * The filename cache. * dirname const char * The pathname of the directory to be scanned. * mem CacheMem * The memory in which to store filenames of * interest. * Output: * return int The number of files recorded, or -1 if a * memory error occurs. Note that the * inability to read the contents of the * directory is not counted as an error. */static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem){ int nfile = 0; /* The number of filenames recorded */ const char *filename; /* The name of the file being looked at *//* * Attempt to open the directory. If the directory can't be read then * there are no accessible files of interest in the directory. */ if(_dr_open_dir(pc->dr, dirname, NULL)) return 0;/* * Record the names of all files in the directory in the cache. */ while((filename = _dr_next_file(pc->dr))) { char *copy; /* A copy of the filename *//* * Make a temporary copy of the filename with an extra byte prepended. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { strcpy(pc->errmsg, "Insufficient memory to record filename"); return -1; };/* * Store the filename. */ copy = _sg_store_string(mem->sg, pc->path->name, 0); if(!copy) { strcpy(pc->errmsg, "Insufficient memory to cache file name."); return -1; };/* * Mark the filename as unchecked. */ copy[0] = PCA_F_ENIGMA;/* * Make room to store a pointer to the copy in mem->files[]. */ if(mem->nfiles + 1 > mem->files_dim) { int needed = mem->files_dim + FILES_BLK_FACT; char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); if(!files) { strcpy(pc->errmsg, "Insufficient memory to extend filename cache."); return 1; }; mem->files = files; mem->files_dim = needed; };/* * Record a pointer to the copy of the filename at the end of the files[] * array. */ mem->files[mem->nfiles++] = copy;/* * Keep a record of the number of files matched so far. */ nfile++; };/* * Sort the list of files into lexical order. */ qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), pca_cmp_matches);/* * Return the number of files recorded in mem->files[]. */ return nfile;}/*....................................................................... * A qsort() comparison function for comparing the cached filename * strings pointed to by two (char **) array elements. Note that * this ignores the initial cache-status byte of each filename. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */static int pca_cmp_matches(const void *v1, const void *v2){ const char **s1 = (const char **) v1; const char **s2 = (const char **) v2; return strcmp(*s1+1, *s2+1);}/*....................................................................... * Given the simple name of a file, search the cached list of files * in the order in which they where found in the list of directories * previously presented to pca_scan_path(), and return the pathname * of the first file which has this name. If a pathname to a file is * given instead of a simple filename, this is returned without being * looked up in the cache, but with any initial ~username expression * expanded, and optionally, unescaped backslashes removed. * * Input: * pc PathCache * The cached list of files. * name const char * The name of the file to lookup. * name_len int The length of the filename string at the * beginning of name[], or -1 to indicate that * the filename occupies the whole of the * string. * literal int If this argument is zero, lone backslashes * in name[] are ignored during comparison * with filenames in the cache, under the * assumption that they were in the input line * soley to escape the special significance of * characters like spaces. To have them treated * as normal characters, give this argument a * non-zero value, such as 1. * Output: * return char * The pathname of the first matching file, * or NULL if not found. Note that the returned * pointer points to memory owned by *pc, and * will become invalid on the next call to any * function in the PathCache module. */char *pca_lookup_file(PathCache *pc, const char *name, int name_len, int literal){ PathNode *node; /* A node in the list of directories in the path */ char **match; /* A pointer to a matching filename string in the cache *//* * Check the arguments. */ if(!pc || !name || name_len==0) return NULL;/* * If no length was specified, determine the length of the string to * be looked up. */ if(name_len < 0) name_len = strlen(name);/* * If the word starts with a ~username expression, the root directory, * of it contains any directory separators, then treat it isn't a simple * filename that can be looked up in the cache, but rather appears to * be the pathname of a file. If so, return a copy of this pathname with * escapes removed, if requested, and any initial ~username expression * expanded. */ if(cpa_cmd_contains_path(name, name_len)) { const char *nptr; if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), !literal) == NULL) return NULL; return pc->path->name; };/* * Look up the specified filename in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) {/* * If the directory of the latest node is a relative pathname, * 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; };/* * Copy the filename into a temporary buffer, while interpretting * escape characters if needed. */ _pn_clear_path(pc->path); if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) return NULL;/* * Perform a binary search for the requested filename. */ match = (char **)bsearch(pc->path->name, node->files, node->nfile, sizeof(*node->files), pca_cmp_file); if(match) {/* * Prepend the pathname in which the directory was found, which we have * guaranteed to end in a directory separator, to the located filename. */ if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) return NULL;/* * Return the matching pathname unless it is rejected by the application. */ 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; return pc->path->name; } else { *(match)[0] = PCA_F_IGNORE; }; }; };/* * File not found. */ return NULL;}/*....................................................................... * A qsort() comparison function for comparing a filename string to * a cached filename string pointed to by a (char **) array element. * This ignores the initial code byte at the start of the cached filename * string. * * Input: * v1, v2 void * Pointers to the pointers of two strings to be compared. * Output: * return int -1 -> v1 < v2. * 0 -> v1 == v2 * 1 -> v1 > v2 */static int pca_cmp_file(const void *v1, const void *v2){ const char *file_name = (const char *) v1; const char **cache_name = (const char **) v2; return strcmp(file_name, *cache_name + 1);}/*....................................................................... * The PcaPathConf structure may have options added to it in the future. * To allow your application to be linked against a shared version of the * tecla library, without these additions causing your application to * crash, you should use new_PcaPathConf() to allocate such structures. * This will set all of the configuration options to their default values, * which you can then change before passing the structure to * pca_path_completions(). * * Input: * pc PathCache * The filename cache in which to look for * file name completions. * Output: * return PcaPathConf * The new configuration structure, or NULL * on error. A descripition of the error * can be found by calling pca_last_error(pc). */PcaPathConf *new_PcaPathConf(PathCache *pc){ PcaPathConf *ppc; /* The object to be returned *//* * Check the arguments. */ if(!pc) return NULL;/* * Allocate the container. */ ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); if(!ppc) { strcpy(pc->errmsg, "Insufficient memory."); return NULL; };/* * Before attempting any operation that might fail, initialize the * container at least up to the point at which it can safely be passed * to del_PcaPathConf(). */ if(pca_init_PcaPathConf(ppc, pc)) return del_PcaPathConf(ppc); return ppc;}/*....................................................................... * Initialize a PcaPathConf configuration structure with defaults. * * Input: * ppc PcaPathConf * The structre to be initialized. * pc PathCache * The cache in which completions will be looked up. * Output: * return int 0 - OK. * 1 - Error. A description of the error can be * obtained by calling pca_last_error(pc). */static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc){/* * Check the arguments. */ if(!pc) return 1;/* * Set the default options. */ ppc->id = PPC_ID_CODE; ppc->pc = pc; ppc->escaped = 1; ppc->file_start = -1; return 0;}/*....................................................................... * Delete a PcaPathConf object. * * Input: * ppc PcaPathConf * The object to be deleted. * Output: * return PcaPathConf * The deleted object (always NULL). */PcaPathConf *del_PcaPathConf(PcaPathConf *ppc){ if(ppc) { ppc->pc = NULL; /* It is up to the caller to delete the cache *//* * Delete the container. */ free(ppc); }; return NULL;}/*....................................................................... * pca_path_completions() is a completion callback function for use * directly with cpl_complete_word() or gl_customize_completions(), or * indirectly from your own completion callback function. It requires * that a CpaPathArgs object be passed via its 'void *data' argument. */CPL_MATCH_FN(pca_path_completions){ PcaPathConf *ppc; /* The configuration arguments */ PathCache *pc; /* The cache in which to look for completions */ PathNode *node; /* A node in the list of directories in the path */ const char *filename; /* The name of the file being looked at */ const char *start_path; /* The pointer to the start of the pathname */ /* in line[]. */ int word_start; /* The index in line[] corresponding to start_path */ const char *prefix; /* The file-name prefix being searched for */ size_t prefix_len; /* The length of the prefix being completed */ int bot; /* The lowest index of the array not searched yet */ int top; /* The highest index of the array not searched yet *//* * Check the arguments. */ if(!cpl) return 1; if(!line || word_end < 0 || !data) { cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); return 1; };/* * Get the configuration arguments. */ ppc = (PcaPathConf *) data;/* * Check that the callback data is a PcaPathConf structure returned * by new_PcaPathConf(). */ if(ppc->id != PPC_ID_CODE) { cpl_record_error(cpl, "Invalid callback data passed to pca_path_completions()"); return 1; };/* * Get the filename cache. */ pc = ppc->pc;/* * Get the start of the file name. If not specified by the caller, * identify it by searching backwards in the input line for an * unescaped space or the start of the line. */ if(ppc->file_start < 0) { start_path = _pu_start_of_path(line, word_end); if(!start_path) { cpl_record_error(cpl, "Unable to find the start of the file name."); return 1; }; } else { start_path = line + ppc->file_start; };/* * Get the index of the start of the word being completed. */ word_start = start_path - line;/* * Work out the length of the prefix that is bein completed. */ prefix_len = word_end - word_start;/* * If the word starts with a ~username expression or the root directory, * of it contains any directory separators, then completion must be * delegated to cpl_file_completions(). */ if(cpa_cmd_contains_path(start_path, prefix_len)) { cfc_file_start(pc->cfc, word_start); return cpl_file_completions(cpl, pc->cfc, line, word_end); };/* * Look up the specified file name in each of the directories of the path, * in the same order that they were listed in the path, and stop as soon * as an instance of the file is found. */ for(node=pc->head; node; node=node->next) {/* * If the directory of the latest node is a relative pathname,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -