📄 pcache.c
字号:
/* * Copyright (c) 2000, 2001 by Martin C. Shepherd. * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. */#include <stdlib.h>#include <string.h>#include <stdio.h>#include "libtecla.h"#include "pathutil.h"#include "homedir.h"#include "freelist.h"#include "direader.h"#include "stringrp.h"/* * The new_PcaPathConf() constructor sets the integer first member of * the returned object to the following magic number. This is then * checked for by pca_path_completions() as a sanity check. */#define PPC_ID_CODE 4567/* * A pointer to a structure of the following type can be passed to * the builtin path-completion callback function to modify its behavior. */struct PcaPathConf { int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ PathCache *pc; /* The path-list cache in which to look up the executables */ int escaped; /* If non-zero, backslashes in the input line are */ /* interpreted as escaping special characters and */ /* spaces, and any special characters and spaces in */ /* the listed completions will also be escaped with */ /* added backslashes. This is the default behaviour. */ /* If zero, backslashes are interpreted as being */ /* literal parts of the file name, and none are added */ /* to the completion suffixes. */ int file_start; /* The index in the input line of the first character */ /* of the file name. If you specify -1 here, */ /* pca_path_completions() identifies the */ /* the start of the file by looking backwards for */ /* an unescaped space, or the beginning of the line. */};/* * Prepended to each chached filename is a character which contains * one of the following status codes. When a given filename (minus * this byte) is passed to the application's check_fn(), the result * is recorded in this byte, such that the next time it is looked * up, we don't have to call check_fn() again. These codes are cleared * whenever the path is scanned and whenever the check_fn() callback * is changed. */typedef enum { PCA_F_ENIGMA='?', /* The file remains to be checked */ PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */} PcaFileStatus;/* * Encapsulate the memory management objects which supply memoy for * the arrays of filenames. */typedef struct { StringGroup *sg; /* The memory used to record the names of files */ size_t files_dim; /* The allocated size of files[] */ char **files; /* Memory for 'files_dim' pointers to files */ size_t nfiles; /* The number of filenames currently in files[] */} CacheMem;static CacheMem *new_CacheMem(void);static CacheMem *del_CacheMem(CacheMem *cm);static void rst_CacheMem(CacheMem *cm);/* * Lists of nodes of the following type are used to record the * names and contents of individual directories. */typedef struct PathNode PathNode;struct PathNode { PathNode *next; /* The next directory in the path */ int relative; /* True if the directory is a relative pathname */ CacheMem *mem; /* The memory used to store dir[] and files[] */ char *dir; /* The directory pathname (stored in pc->sg) */ int nfile; /* The number of filenames stored in 'files' */ char **files; /* Files of interest in the current directory, */ /* or NULL if dir[] is a relative pathname */ /* who's contents can't be cached. This array */ /* and its contents are taken from pc->abs_mem */ /* or pc->rel_mem */};/* * Append a new node to the list of directories in the path. */static int add_PathNode(PathCache *pc, const char *dirname);/* * Set the max length of the error-reporting string. There is no point * in this being longer than the width of a typical terminal window. * In composing error messages, I have assumed that this number is * at least 80, so you don't decrease it below this number. */#define ERRLEN 200/* * Set the maximum length allowed for usernames. * names. */#define USR_LEN 100/* * PathCache objects encapsulate the resources needed to record * files of interest from comma-separated lists of directories. */struct PathCache { FreeList *node_mem; /* A free-list of PathNode objects */ CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ CacheMem *rel_mem; /* Memory for the filenames of relative paths */ PathNode *head; /* The head of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathNode *tail; /* The tail of the list of directories in the */ /* path, or NULL if no path has been scanned yet. */ PathName *path; /* The fully qualified name of a file */ HomeDir *home; /* Home-directory lookup object */ DirReader *dr; /* A portable directory reader */ CplFileConf *cfc; /* Configuration parameters to pass to */ /* cpl_file_completions() */ CplCheckFn *check_fn; /* The callback used to determine if a given */ /* filename should be recorded in the cache. */ void *data; /* Annonymous data to be passed to pc->check_fn() */ char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ /* users. */ char errmsg[ERRLEN+1]; /* Error-report buffer */};/* * Empty the cache. */static void pca_clear_cache(PathCache *pc);/* * Read a username from string[] and record it in pc->usrnam[]. */static int pca_read_username(PathCache *pc, const char *string, int slen, int literal, const char **nextp);/* * Extract the next component of a colon separated list of directory * paths. */static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp);/* * Scan absolute directories for files of interest, recording their names * in mem->sg and recording pointers to these names in mem->files[]. */static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem);/* * 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. */static int pca_cmp_matches(const void *v1, const void *v2);/* * A qsort() comparison function for comparing a filename * against an element of an array of pointers to filename cache * entries. */static int pca_cmp_file(const void *v1, const void *v2);/* * Initialize a PcaPathConf configuration objects with the default * options. */static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc);/* * Make a copy of a completion suffix, suitable for passing to * cpl_add_completion(). */static int pca_prepare_suffix(PathCache *pc, const char *suffix, int add_escapes);/* * Return non-zero if the specified string appears to start with a pathname. */static int cpa_cmd_contains_path(const char *prefix, int prefix_len);/* * Return a given prefix with escapes optionally removed. */static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, size_t prefix_len, int escaped);/* * If there is a tilde expression at the beginning of the specified path, * place the corresponding home directory into pc->path. Otherwise * just clear pc->path. */static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, int literal, const char **endp);/* * Clear the filename status codes that are recorded before each filename * in the cache. */static void pca_remove_marks(PathCache *pc);/* * Specify how many PathNode's to allocate at a time. */#define PATH_NODE_BLK 30/* * Specify the amount by which the files[] arrays are to be extended * whenever they are found to be too small. */#define FILES_BLK_FACT 256/*....................................................................... * Create a new object who's function is to maintain a cache of * filenames found within a list of directories, and provide quick * lookup and completion of selected files in this cache. * * Output: * return PathCache * The new, initially empty cache, or NULL * on error. */PathCache *new_PathCache(void){ PathCache *pc; /* The object to be returned *//* * Allocate the container. */ pc = (PathCache *)malloc(sizeof(PathCache)); if(!pc) { fprintf(stderr, "new_PathCache: Insufficient memory.\n"); 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_PathCache(). */ pc->node_mem = NULL; pc->abs_mem = NULL; pc->rel_mem = NULL; pc->head = NULL; pc->tail = NULL; pc->path = NULL; pc->home = NULL; pc->dr = NULL; pc->cfc = NULL; pc->check_fn = 0; pc->data = NULL; pc->usrnam[0] = '\0'; pc->errmsg[0] = '\0';/* * Allocate the freelist of directory list nodes. */ pc->node_mem = _new_FreeList("new_PathCache", sizeof(PathNode), PATH_NODE_BLK); if(!pc->node_mem) return del_PathCache(pc);/* * Allocate memory for recording names of files in absolute paths. */ pc->abs_mem = new_CacheMem(); if(!pc->abs_mem) return del_PathCache(pc);/* * Allocate memory for recording names of files in relative paths. */ pc->rel_mem = new_CacheMem(); if(!pc->rel_mem) return del_PathCache(pc);/* * Allocate a pathname buffer. */ pc->path = _new_PathName(); if(!pc->path) return del_PathCache(pc);/* * Allocate an object for looking up home-directories. */ pc->home = _new_HomeDir(); if(!pc->home) return del_PathCache(pc);/* * Allocate an object for reading directories. */ pc->dr = _new_DirReader(); if(!pc->dr) return del_PathCache(pc);/* * Allocate a cpl_file_completions() configuration object. */ pc->cfc = new_CplFileConf(); if(!pc->cfc) return del_PathCache(pc);/* * Configure cpl_file_completions() to use check_fn() to select * files of interest. */ cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data);/* * Return the cache, ready for use. */ return pc;}/*....................................................................... * Delete a given cache of files, returning the resources that it * was using to the system. * * Input: * pc PathCache * The cache to be deleted (can be NULL). * Output: * return PathCache * The deleted object (ie. allways NULL). */PathCache *del_PathCache(PathCache *pc){ if(pc) {/* * Delete the memory of the list of path nodes. */ pc->node_mem = _del_FreeList(NULL, pc->node_mem, 1);/* * Delete the memory used to record filenames. */ pc->abs_mem = del_CacheMem(pc->abs_mem); pc->rel_mem = del_CacheMem(pc->rel_mem);/* * The list of PathNode's was already deleted when node_mem was * deleted. */ pc->head = NULL; pc->tail = NULL;/* * Delete the pathname buffer. */ pc->path = _del_PathName(pc->path);/* * Delete the home-directory lookup object. */ pc->home = _del_HomeDir(pc->home);/* * Delete the directory reader. */ pc->dr = _del_DirReader(pc->dr);/* * Delete the cpl_file_completions() config object. */ pc->cfc = del_CplFileConf(pc->cfc);/* * Delete the container. */ free(pc); }; return NULL;}/*....................................................................... * If you want subsequent calls to pca_lookup_file() and * pca_path_completions() to only return the filenames of certain * types of files, for example executables, or filenames ending in * ".ps", call this function to register a file-selection callback * function. This callback function takes the full pathname of a file, * plus application-specific data, and returns 1 if the file is of * interest, and zero otherwise. * * Input: * pc PathCache * The filename cache. * check_fn CplCheckFn * The function to call to see if the name of * a given file should be included in the * cache. This determines what type of files * will reside in the cache. To revert to * selecting all files, regardless of type, * pass 0 here. * data void * You can pass a pointer to anything you * like here, including NULL. It will be * passed to your check_fn() callback * function, for its private use. */void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data){ if(pc) {/* * If the callback or its data pointer have changed, clear the cached * statuses of files that were accepted or rejected by the previous * calback. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -