pcache.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004 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,
* 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.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* If file-system access is to be excluded, this module has no function,
* so all of its code should be excluded.
*/
#ifndef WITHOUT_FILE_SYSTEM
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "libtecla.h"
#include "pathutil.h"
#include "homedir.h"
#include "freelist.h"
#include "direader.h"
#include "stringrp.h"
#include "errmsg.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() */
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 {
/*
* Encapsulate the memory management objects which supply memoy for
* the arrays of filenames.
*/
typedef struct {
char **files; /* Memory for 'files_dim' pointers to files */
} CacheMem;
static CacheMem *new_CacheMem(void);
/*
* Lists of nodes of the following type are used to record the
* names and contents of individual directories.
*/
struct PathNode {
int relative; /* True if the directory is a relative pathname */
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.
*/
/*
* 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 {
/* path, or NULL if no path has been scanned yet. */
/* path, or NULL if no path has been scanned yet. */
/* cpl_file_completions() */
/* filename should be recorded in the cache. */
void *data; /* Annonymous data to be passed to pc->check_fn() */
/* users. */
};
/*
* Empty the cache.
*/
/*
* Read a username from string[] and record it in pc->usrnam[].
*/
/*
* Extract the next component of a colon separated list of directory
* paths.
*/
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[].
*/
/*
* 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.
*/
/*
* A qsort() comparison function for comparing a filename
* against an element of an array of pointers to filename cache
* entries.
*/
/*
* Initialize a PcaPathConf configuration objects with the default
* options.
*/
/*
* Make a copy of a completion suffix, suitable for passing to
* cpl_add_completion().
*/
int add_escapes);
/*
* Return non-zero if the specified string appears to start with a pathname.
*/
/*
* Return a given prefix with escapes optionally removed.
*/
/*
* 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.
*/
/*
* Clear the filename status codes that are recorded before each filename
* in the cache.
*/
/*
* 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)
{
/*
* Allocate the container.
*/
if(!pc) {
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().
*/
/*
* Allocate a place to record error messages.
*/
return del_PathCache(pc);
/*
* Allocate the freelist of directory list nodes.
*/
return del_PathCache(pc);
/*
* Allocate memory for recording names of files in absolute paths.
*/
return del_PathCache(pc);
/*
* Allocate memory for recording names of files in relative paths.
*/
return del_PathCache(pc);
/*
* Allocate a pathname buffer.
*/
return del_PathCache(pc);
/*
* Allocate an object for looking up home-directories.
*/
return del_PathCache(pc);
/*
* Allocate an object for reading directories.
*/
return del_PathCache(pc);
/*
* Allocate a cpl_file_completions() configuration object.
*/
return del_PathCache(pc);
/*
* Configure cpl_file_completions() to use check_fn() to select
* files of interest.
*/
/*
* 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).
*/
{
if(pc) {
/*
* Delete the error message buffer.
*/
/*
* Delete the memory of the list of path nodes.
*/
/*
* Delete the memory used to record filenames.
*/
/*
* The list of PathNode's was already deleted when node_mem was
* deleted.
*/
/*
* Delete the pathname buffer.
*/
/*
* Delete the home-directory lookup object.
*/
/*
* Delete the directory reader.
*/
/*
* Delete the cpl_file_completions() config object.
*/
/*
* Delete the container.
*/
};
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.
*/
{
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.
*/
/*
* Record the new callback locally.
*/
/*
* Configure cpl_file_completions() to use the same callback to
* select files of interest.
*/
};
return;
}
/*.......................................................................
* Return a description of the last path-caching error that occurred.
*
* Input:
* pc PathCache * The filename cache that suffered the error.
* Output:
* return char * The description of the last error.
*/
{
}
/*.......................................................................
* Discard all cached filenames.
*
* Input:
* pc PathCache * The cache to be cleared.
*/
{
if(pc) {
/*
* Return all path-nodes to the freelist.
*/
/*
* Delete all filename strings.
*/
};
return;
}
/*.......................................................................
* Build the list of files of interest contained in a given
* colon-separated list of directories.
*
* Input:
* pc PathCache * The cache in which to store the names of
* the files that are found in the list of
* directories.
* path const char * A colon-separated list of directory
* paths. Under UNIX, when searching for
* executables, this should be the return
* value of getenv("PATH").
* Output:
* return int 0 - OK.
* 1 - An error occurred. A description of
* the error can be acquired by calling
* pca_last_error(pc).
*/
{
const char *pptr; /* A pointer to the next unprocessed character in path[] */
char **fptr; /* A pointer into pc->abs_mem->files[] */
/*
* Check the arguments.
*/
if(!pc)
return 1;
/*
* Clear the outdated contents of the cache.
*/
/*
* If no path list was provided, there is nothing to be added to the
* cache.
*/
if(!path)
return 0;
/*
* Extract directories from the path list, expanding tilde expressions
* on the fly into pc->pathname, then add them to the list of path
* nodes, along with a sorted list of the filenames of interest that
* the directories hold.
*/
while(*pptr) {
/*
* Extract the next pathname component into pc->path->name.
*/
return 1;
/*
* Add a new node to the list of paths, containing both the
* directory name and, if not a relative pathname, the list of
* files of interest in the directory.
*/
return 1;
};
/*
* The file arrays in each absolute directory node are sections of
* pc->abs_mem->files[]. Record pointers to the starts of each
* of these sections in each directory node. Note that this couldn't
* be done in add_PathNode(), because pc->abs_mem->files[] may
* get reallocated in subsequent calls to add_PathNode(), thus
* invalidating any pointers to it.
*/
};
return 0;
}
/*.......................................................................
* Extract the next directory path from a colon-separated list of
* directories, expanding tilde home-directory expressions where needed.
*
* Input:
* pc PathCache * The cache of filenames.
* path const char * A pointer to the start of the next component
* in the path list.
* nextp const char ** A pointer to the next unprocessed character
* in path[] will be assigned to *nextp.
* Output:
* return int 0 - OK. The extracted path is in pc->path->name.
* 1 - Error. A description of the error will
* have been left in pc->err.
*/
{
const char *pptr; /* A pointer into path[] */
const char *sptr; /* The path following tilde expansion */
int escaped = 0; /* True if the last character was a backslash */
/*
* 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.
*/
return 1;
/*
* Keep a record of the current location in the path.
*/
/*
* Locate the end of the directory name in the pathname string, stopping
* when either the end of the string is reached, or an un-escaped colon
* separator is seen.
*/
/*
* Append the rest of the directory path to the pathname buffer.
*/
return 1;
};
/*
* To facilitate subsequently appending filenames to the directory
* path name, make sure that the recorded directory name ends in a
* directory separator.
*/
{
if(dirlen < FS_DIR_SEP_LEN ||
FS_DIR_SEP_LEN) != 0) {
return 1;
};
};
};
/*
* Skip the separator unless we have reached the end of the path.
*/
if(*pptr==':')
pptr++;
/*
* Return the unprocessed tail of the path-list string.
*/
return 0;
}
/*.......................................................................
* Read a username, stopping when a directory separator is seen, a colon
* separator is seen, the end of the string is reached, or the username
* buffer overflows.
*
* Input:
* pc PathCache * The cache of filenames.
* string char * The string who's prefix contains the name.
* slen int The max number of characters to read from string[].
* literal int If true, treat backslashes as literal characters
* instead of escapes.
* nextp char ** A pointer to the next unprocessed character
* in string[] will be assigned to *nextp.
* Output:
* return int 0 - OK. The username can be found in pc->usrnam.
* 1 - Error. A description of the error message
* can be found in pc->err.
*/
{
int usrlen; /* The number of characters in pc->usrnam[] */
const char *sptr; /* A pointer into string[] */
int escaped = 0; /* True if the last character was a backslash */
/*
* Extract the username.
*/
/*
* Stop if the end of the string is reached, or a directory separator
* or un-escaped colon separator is seen.
*/
break;
/*
* Escape the next character?
*/
escaped = 1;
} else {
escaped = 0;
};
};
/*
* Did the username overflow the buffer?
*/
return 1;
};
/*
* Terminate the string.
*/
/*
* Indicate where processing of the input string should continue.
*/
return 0;
}
/*.......................................................................
* Create a new CacheMem object.
*
* Output:
* return CacheMem * The new object, or NULL on error.
*/
static CacheMem *new_CacheMem(void)
{
/*
* Allocate the container.
*/
if(!cm) {
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_CacheMem().
*/
/*
* Allocate a list of string segments for storing filenames.
*/
return del_CacheMem(cm);
/*
* Allocate an array of pointers to filenames.
* This will be extended later if needed.
*/
return del_CacheMem(cm);
};
return cm;
}
/*.......................................................................
* Delete a CacheMem object.
*
* Input:
* cm CacheMem * The object to be deleted.
* Output:
* return CacheMem * The deleted object (always NULL).
*/
{
if(cm) {
/*
* Delete the memory that was used to record filename strings.
*/
/*
* Delete the array of pointers to filenames.
*/
};
/*
* Delete the container.
*/
};
return NULL;
}
/*.......................................................................
* Re-initialize the memory used to allocate filename strings.
*
* Input:
* cm CacheMem * The memory cache to be cleared.
*/
{
return;
}
/*.......................................................................
* Append a new directory node to the list of directories read from the
* path.
*
* Input:
* pc PathCache * The filename cache.
* dirname const char * The name of the new directory.
* Output:
* return int 0 - OK.
* 1 - Error.
*/
{
int relative; /* True if dirname[] is a relative pathname */
/*
* Have we been passed a relative pathname or an absolute pathname?
*/
/*
* If it's an absolute pathname, ignore it if the corresponding
* directory doesn't exist.
*/
return 0;
/*
* Allocate a new list node to record the specifics of the new directory.
*/
if(!node) {
return 1;
};
/*
* Initialize the node.
*/
/*
* Make a copy of the directory pathname.
*/
return 1;
};
/*
* Scan absolute directories for files of interest, recording their names
* in node->mem->sg and appending pointers to these names to the
* node->mem->files[] array.
*/
return nfile < 0;
};
};
/*
* Append the new node to the list.
*/
} else {
};
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.
*/
{
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.
*/
return 0;
/*
* Record the names of all files in the directory in the cache.
*/
char *copy; /* A copy of the filename */
/*
* Make a temporary copy of the filename with an extra byte prepended.
*/
return -1;
};
/*
* Store the filename.
*/
if(!copy) {
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(!files) {
"Insufficient memory to extend filename cache.",
return 1;
};
};
/*
* Record a pointer to the copy of the filename at the end of the files[]
* array.
*/
/*
* Keep a record of the number of files matched so far.
*/
nfile++;
};
/*
* Sort the list of files into lexical order.
*/
/*
* 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
*/
{
}
/*.......................................................................
* 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.
*/
int literal)
{
char **match; /* A pointer to a matching filename string in the cache */
/*
* Check the arguments.
*/
return NULL;
/*
* If no length was specified, determine the length of the string to
* be looked up.
*/
if(name_len < 0)
/*
* 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.
*/
const char *nptr;
return NULL;
};
/*
* 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.
*/
/*
* If the directory of the latest node is a relative pathname,
* scan it for files of interest.
*/
continue;
};
/*
* Copy the filename into a temporary buffer, while interpretting
* escape characters if needed.
*/
return NULL;
/*
* Perform a binary search for the requested filename.
*/
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.
*/
return NULL;
/*
* Return the matching pathname unless it is rejected by the application.
*/
(*match)[0] = PCA_F_WANTED;
} 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
*/
{
const char **cache_name = (const char **) v2;
}
/*.......................................................................
* 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).
*/
{
/*
* Check the arguments.
*/
if(!pc)
return NULL;
/*
* Allocate the container.
*/
if(!ppc) {
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().
*/
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).
*/
{
/*
* Check the arguments.
*/
if(!pc)
return 1;
/*
* Set the default options.
*/
return 0;
}
/*.......................................................................
* Delete a PcaPathConf object.
*
* Input:
* ppc PcaPathConf * The object to be deleted.
* Output:
* return PcaPathConf * The deleted object (always NULL).
*/
{
if(ppc) {
/*
* Delete the container.
*/
};
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.
*/
{
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 */
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;
return 1;
};
/*
* Get the configuration arguments.
*/
/*
* Check that the callback data is a PcaPathConf structure returned
* by new_PcaPathConf().
*/
"Invalid callback data passed to pca_path_completions()");
return 1;
};
/*
* Get the filename cache.
*/
/*
* 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) {
if(!start_path) {
return 1;
};
} else {
};
/*
* Get the index of the start of the word being completed.
*/
/*
* Work out the length of the prefix that is bein completed.
*/
/*
* 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().
*/
};
/*
* 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.
*/
/*
* If the directory of the latest node is a relative pathname,
* scan it for files of interest.
*/
continue;
};
/*
* 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.
*/
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;
if(test > 0)
else if(test < 0)
else {
break;
};
};
/*
* If we found a match, look to see if any of its neigbors also match.
*/
;
;
/*
* 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.
*/
/*
* Form the full pathname of the file.
*/
return 1;
};
/*
* Should the file be included in the list of completions?
*/
match[0] = PCA_F_WANTED;
/*
* Copy the completion suffix into the work pathname pc->path->name,
* adding backslash escapes if needed.
*/
return 1;
/*
* Record the completion.
*/
"", " "))
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.
*/
if(!prefix)
return 1;
/*
* Now open the current directory.
*/
return 0;
/*
* Scan the current directory for sub-directories whos names start with
* the prefix that we are completing.
*/
/*
* Does the latest filename match the prefix, and is it a directory?
*/
/*
* Record the completion.
*/
return 1;
/*
* The prefix in pc->path->name will have been overwritten by
* pca_prepare_suffix(). Restore it here.
*/
if(!prefix)
return 1;
};
};
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.
*/
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?
*/
/*
* Clear the work buffer.
*/
/*
* Count the number of backslashes that will have to be added to
* escape spaces, tabs, backslashes and wildcard characters.
*/
nbsl = 0;
if(add_escapes) {
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.
*/
return 1;
};
/*
* If the suffix doesn't need any escapes, copy it directly into the
* work buffer.
*/
if(nbsl==0) {
} else {
/*
* Make a copy with special characters escaped?
*/
if(nbsl > 0) {
for(i=0; i<suffix_len; i++) {
switch(*src) {
case ' ': case '\t': case '\\': case '*': case '?': case '[':
*dst++ = '\\';
};
};
*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.
*/
{
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 &&
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 &&
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->err.
*/
{
/*
* Make a copy with escapes removed?
*/
if(escaped) {
return NULL;
};
};
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.
*/
{
if(ppc)
}
/*.......................................................................
* 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.
*/
{
if(ppc)
}
/*.......................................................................
* 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.
* 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->err).
*/
{
/*
* Clear the pathname buffer.
*/
/*
* 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[].
*/
return 1;
/*
* Attempt to lookup the home directory of the user.
*/
if(!homedir) {
return 1;
};
/*
* Append the home directory to the pathname string.
*/
"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
*/
pptr += FS_DIR_SEP_LEN;
};
/*
* Return a pointer to the next unprocessed character.
*/
return 0;
}
/*.......................................................................
* Clear the filename status codes that are recorded before each filename
* in the cache.
*
* Input:
* pc PathCache * The filename cache.
*/
{
int i;
/*
* Traverse the absolute directories of the path, clearing the
* filename status marks that precede each filename.
*/
};
};
return;
}
#endif /* ifndef WITHOUT_FILE_SYSTEM */