NAME¶
pca_lookup_file, del_PathCache, del_PcaPathConf, new_PathCache, new_PcaPathConf,
pca_last_error, pca_path_completions, pca_scan_path, pca_set_check_fn,
ppc_file_start, ppc_literal_escapes - lookup a file in a list of directories
SYNOPSIS¶
#include <libtecla.h>
PathCache *new_PathCache(void);
PathCache *del_PathCache(PathCache *pc);
int pca_scan_path(PathCache *pc, const char *path);
void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
void *data);
char *pca_lookup_file(PathCache *pc, const char *name,
int name_len, int literal);
const char *pca_last_error(PathCache *pc);
CPL_MATCH_FN(pca_path_completions);
DESCRIPTION¶
The
PathCache object is part of the tecla library (see the
libtecla(3)
man page).
PathCache objects allow an application to search for files in any colon
separated list of directories, such as the unix execution PATH environment
variable. Files in absolute directories are cached in a
PathCache
object, whereas relative directories are scanned as needed. Using a
PathCache object, you can look up the full pathname of a simple
filename, or you can obtain a list of the possible completions of a given
filename prefix. By default all files in the list of directories are targets
for lookup and completion, but a versatile mechanism is provided for only
selecting specific types of files. The obvious application of this facility is
to provide Tab-completion and lookup of executable commands in the unix PATH,
so an optional callback which rejects all but executable files, is provided.
AN EXAMPLE¶
Under UNIX, the following example program looks up and displays the full
pathnames of each of the command names on the command line.
#include <stdio.h>
#include <stdlib.h>
#include <libtecla.h>
int main(int argc, char *argv[])
{
int i;
/*
* Create a cache for executable files.
*/
PathCache *pc = new_PathCache();
if(!pc)
exit(1);
/*
* Scan the user's PATH for executables.
*/
if(pca_scan_path(pc, getenv("PATH"))) {
fprintf(stderr, "%s\n", pca_last_error(pc));
exit(1);
}
/*
* Arrange to only report executable files.
*/
pca_set_check_fn(pc, cpl_check_exe, NULL);
/*
* Lookup and display the full pathname of each of the
* commands listed on the command line.
*/
for(i=1; i<argc; i++) {
char *cmd = pca_lookup_file(pc, argv[i], -1, 0);
printf("The full pathname of '%s' is %s\n", argv[i],
cmd ? cmd : "unknown");
}
pc = del_PathCache(pc); /* Clean up */
return 0;
}
The following is an example of what this does on my laptop under linux:
$ ./example less more blob
The full pathname of 'less' is /usr/bin/less
The full pathname of 'more' is /bin/more
The full pathname of 'blob' is unknown
$
FUNCTION DESCRIPTIONS¶
In order to use the facilities of this module, you must first allocate a
PathCache object by calling the
new_PathCache() constructor
function.
PathCache *new_PathCache(void)
This function creates the resources needed to cache and lookup files in a list
of directories. It returns
NULL on error.
POPULATING THE CACHE¶
Once you have created a cache, it needs to be populated with files. To do this,
call the
pca_scan_path() function.
int pca_scan_path(PathCache *pc, const char *path);
Whenever this function is called, it discards the current contents of the cache,
then scans the list of directories specified in its
path argument for
files. The
path argument must be a string containing a colon-separated
list of directories, such as
"/usr/bin:/home/mcs/bin:.". This
can include directories specified by absolute pathnames such as
"/usr/bin", as well as sub-directories specified by relative
pathnames such as
"." or
"bin". Files in the
absolute directories are immediately cached in the specified
PathCache
object, whereas sub-directories, whose identities obviously change whenever
the current working directory is changed, are marked to be scanned on the fly
whenever a file is looked up.
On success this function return
0. On error it returns
1, and a
description of the error can be obtained by calling
pca_last_error(pc).
LOOKING UP FILES¶
Once the cache has been populated with files, you can look up the full pathname
of a file, simply by specifying its filename to
pca_lookup_file().
char *pca_lookup_file(PathCache *pc, const char *name,
int name_len, int literal);
To make it possible to pass this function a filename which is actually part of a
longer string, the
name_len argument can be used to specify the length
of the filename at the start of the
name[] argument. If you pass
-1 for this length, the length of the string will be determined with
strlen(). If the
name[] string might contain backslashes that
escape the special meanings of spaces and tabs within the filename, give the
literal argument, the value
0. Otherwise, if backslashes should
be treated as normal characters, pass
1 for the value of the
literal argument.
FILENAME COMPLETION¶
Looking up the potential completions of a filename-prefix in the filename cache,
is achieved by passing the provided
pca_path_completions() callback
function to the
cpl_complete_word() function (see the
cpl_complete_word(3) man page).
CPL_MATCH_FN(pca_path_completions);
This callback requires that its
data argument be a pointer to a
PcaPathConf object. Configuration objects of this type are allocated by
calling
new_PcaPathConf().
PcaPathConf *new_PcaPathConf(PathCache *pc);
This function returns an object initialized with default configuration
parameters, which determine how the
cpl_path_completions() callback
function behaves. The functions which allow you to individually change these
parameters are discussed below.
By default, the
pca_path_completions() callback function searches
backwards for the start of the filename being completed, looking for the first
un-escaped space or the start of the input line. If you wish to specify a
different location, call
ppc_file_start() with the index at which the
filename starts in the input line. Passing
start_index=-1 re-enables
the default behavior.
void ppc_file_start(PcaPathConf *ppc, int start_index);
By default, when
pca_path_completions() looks at a filename in the input
line, each lone backslash in the input line is interpreted as being a special
character which removes any special significance of the character which
follows it, such as a space which should be taken as part of the filename
rather than delimiting the start of the filename. These backslashes are thus
ignored while looking for completions, and subsequently added before spaces,
tabs and literal backslashes in the list of completions. To have unescaped
backslashes treated as normal characters, call
ppc_literal_escapes()
with a non-zero value in its
literal argument.
void ppc_literal_escapes(PcaPathConf *ppc, int literal);
When you have finished with a
PcaPathConf variable, you can pass it to
the
del_PcaPathConf() destructor function to reclaim its memory.
PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);
BEING SELECTIVE¶
If you are only interested in certain types or files, such as, for example,
executable files, or files whose names end in a particular suffix, you can
arrange for the file completion and lookup functions to be selective in the
filenames that they return. This is done by registering a callback function
with your
PathCache object. Thereafter, whenever a filename is found
which either matches a filename being looked up, or matches a prefix which is
being completed, your callback function will be called with the full pathname
of the file, plus any application-specific data that you provide, and if the
callback returns
1 the filename will be reported as a match, and if it
returns
0, it will be ignored. Suitable callback functions and their
prototypes should be declared with the following macro. The
CplCheckFn
typedef is also provided in case you wish to declare pointers to such
functions.
#define CPL_CHECK_FN(fn) int (fn)(void *data, \
const char *pathname)
typedef CPL_CHECK_FN(CplCheckFn);
Registering one of these functions involves calling the
pca_set_check_fn() function. In addition to the callback function,
passed via the
check_fn argument, you can pass a pointer to anything
via the
data argument. This pointer will be passed on to your callback
function, via its own
data argument, whenever it is called, so this
provides a way to pass appplication specific data to your callback.
void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
void *data);
Note that these callbacks are passed the full pathname of each matching file, so
the decision about whether a file is of interest can be based on any property
of the file, not just its filename. As an example, the provided
cpl_check_exe() callback function looks at the executable permissions
of the file and the permissions of its parent directories, and only returns
1 if the user has execute permission to the file. This callback
function can thus be used to lookup or complete command names found in the
directories listed in the user's
PATH environment variable. The example
program given earlier in this man page provides a demonstration of this.
Beware that if somebody tries to complete an empty string, your callback will
get called once for every file in the cache, which could number in the
thousands. If your callback does anything time consuming, this could result in
an unacceptable delay for the user, so callbacks should be kept short.
To improve performance, whenever one of these callbacks is called, the choice
that it makes is cached, and the next time the corresponding file is looked
up, instead of calling the callback again, the cached record of whether it was
accepted or rejected is used. Thus if somebody tries to complete an empty
string, and hits tab a second time when nothing appears to happen, there will
only be one long delay, since the second pass will operate entirely from the
cached dispositions of the files. These cached dipositions are discarded
whenever
pca_scan_path() is called, and whenever
pca_set_check_fn() is called with changed callback function or data
arguments.
ERROR HANDLING¶
If
pca_scan_path() reports that an error occurred by returning
1,
you can obtain a terse description of the error by calling
pca_last_error(pc). This returns an internal string containing an error
message.
const char *pca_last_error(PathCache *pc);
CLEANING UP¶
Once you have finished using a
PathCache object, you can reclaim its
resources by passing it to the
del_PathCache() destructor function.
This takes a pointer to one of these objects, and always returns
NULL.
PathCache *del_PathCache(PathCache *pc);
THREAD SAFETY¶
In multi-threaded programs, you should use the
libtecla_r.a version of
the library. This uses POSIX reentrant functions where available (hence the
_r suffix), and disables features that rely on non-reentrant system
functions. In the case of this module, the only disabled feature is username
completion in
~username/ expressions, in
cpl_path_completions().
Using the
libtecla_r.a version of the library, it is safe to use the
facilities of this module in multiple threads, provided that each thread uses
a separately allocated
PathCache object. In other words, if two threads
want to do path searching, they should each call
new_PathCache() to
allocate their own caches.
FILES¶
libtecla.a - The tecla library
libtecla.h - The tecla header file.
SEE ALSO¶
libtecla(3), gl_get_line(3), ef_expand_file(3),
cpl_complete_word(3)
AUTHOR¶
Martin Shepherd (mcs@astro.caltech.edu)