pca_lookup_file(3)
π pca_lookup_file β Library Functions Manual π
okay bestie, gather round, because this is the pca_lookup_file(3) page and it is a lot π
but don't panic, we're doing this together. It's the tecla library's file-finding, path-scanning, tab-completing little superstar, and yes it's long, so this is part 1 of 2 β hold that thought, more is coming.
β¨ 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 (go read the libtecla(3) man page, it's the whole vibe) β¨
So like, PathCache objects let an application search for files inside any colon-separated list of directories β think the unix execution PATH environment variable, iconic behavior honestly. Here's the tea: files in absolute directories get cached inside the PathCache object right away, whereas relative directories get scanned as needed, on the fly, whenever somebody actually needs them. π§
With a PathCache object you can either look up the full pathname of a simple filename, or you can get a list of the possible completions of a given filename prefix. By default every file in the list of directories is a target for lookup and completion β but there's a versatile mechanism for only picking specific types of files, and it's lowkey the best part. The obvious application of all this is providing Tab-completion and lookup of executable commands in the unix PATH, so yes, there's an optional callback that rejects everything except executable files. Obsessed. π
π AN EXAMPLE π
Under UNIX, this 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;
}
And here's what it does on my laptop under linux, no cap:
$ ./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
$
blob said unknown π she's just not in the PATH bestie, it's not personal.
π FUNCTION DESCRIPTIONS π
To use any of this module's facilities, you first gotta allocate a PathCache object by calling the new_PathCache() constructor function. Periodt.
PathCache *new_PathCache(void)
This one creates all the resources needed to cache and lookup files in a list of directories. It returns NULL on error. (Rude, but okay.) π
π POPULATING THE CACHE π
Once you've created a cache, she needs files in her. To do that, call the pca_scan_path() function.
int pca_scan_path(PathCache *pc, const char *path);
Every single time you call this function, it discards the current contents of the cache, and 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, like "/usr/bin:/home/mcs/bin:.". This can include directories given by absolute pathnames such as "/usr/bin", and also sub-directories given 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. Slay. β¨
On success this function returns 0. On error it returns 1, and you can get a description of the error 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 just by handing its filename to pca_lookup_file().
char *pca_lookup_file(PathCache *pc, const char *name,
int name_len, int literal);
So the way that name_len works: to make it possible to pass this function a filename that's actually part of a longer string, name_len lets you 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 gets figured out with strlen(). And 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. Easy. π
π 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, she's the bestie next door).
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 that let you 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 want 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 a special character that removes any special significance of the character following it β like a space that should be taken as part of the filename rather than delimiting the start of the filename. These backslashes are therefore 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're done with a PcaPathConf variable, you can pass it to the del_PcaPathConf() destructor function to reclaim its memory. Clean up after yourself, queen. π§Ή
PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);
π BEING SELECTIVE π
If you're only interested in certain types of files β executable files, say, or files whose names end in a particular suffix β you can arrange for the file completion and lookup functions to be selective about the filenames they return. Here's how: register a callback function with your PathCache object. After that, whenever a filename is found that either matches a filename being looked up, or matches a prefix that's being completed, your callback function gets called with the full pathname of the file, plus any application-specific data you provide. If the callback returns 1, the filename is reported as a match; if it returns 0, it gets ignored. π
Suitable callback functions and their prototypes should be declared with the following macro. The CplCheckFn typedef is also provided, in case you want 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 means calling the pca_set_check_fn() function. Besides the callback function, passed via the check_fn argument, you can pass a pointer to anything via the data argument. That pointer gets passed on to your callback function through its own data argument every time it's called, so this is the way to pass application-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 interesting 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 on the file. This callback function can therefore be used to look up or complete command names found in the directories listed in the user's PATH environment variable. The example program earlier in this man page demonstrates exactly that. π
β οΈ Beware: if somebody tries to complete an empty string, your callback gets called once for every file in the cache, which could number in the thousands. If your callback does anything time consuming, this could cause an unacceptable delay for the user, so keep those callbacks short, bestie. π
To improve performance, whenever one of these callbacks is called, the choice 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. So 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 operates entirely from the cached dispositions of the files. These cached dispositions are discarded whenever pca_scan_path() is called, and whenever pca_set_check_fn() is called with changed callback function or data arguments. β¨
okay that's part 1, we're not done yet β part 2 is coming, don't go anywhere π
π ERROR HANDLING π
So if pca_scan_path() is like "bestie something went wrong" by returning 1, you can go get the tea by calling pca_last_error(pc). This hands you back an internal string with an error message in it. Internal, okay? Like, it's hers, don't go freeing it or editing it. π
const char *pca_last_error(PathCache *pc);
π§Ό CLEANING UP π§Ό
Once you're done with your PathCache object, you can give it back to the universe by passing it to the del_PathCache() destructor function. It takes a pointer to one of these objects, and it always returns NULL. Every time. No cap. So like, pc = del_PathCache(pc); and now pc is NULL and you can't accidentally touch a ghost cache. β¨
PathCache *del_PathCache(PathCache *pc);
π§΅ THREAD SAFETY π§΅
If your program is multi-threaded, you gotta use the libtecla_r.a version of the library. That's the reentrant one β it uses POSIX reentrant functions wherever they're available (hence the _r suffix, iykyk), and it disables the features that lean on non-reentrant system functions.
For this module specifically, the only disabled feature is username completion in ~username/ expressions, inside cpl_path_completions(). So like, that one little thing gets benched. π
With the libtecla_r.a version, it is safe to use the facilities of this module across multiple threads β provided each thread uses its own separately allocated PathCache object. In other words: if two threads wanna do path searching, they each call new_PathCache() and allocate their own caches. No sharing, no drama, no cursed race conditions. Main character energy only for your own cache. π
Sharing a single PathCache between threads? Not the vibe. Don't.
π FILES π
libtecla.aβ The tecla librarylibtecla.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)
And that's a wrap on pca_lookup_file(3), bestie!! π This whole thing comes straight out of the pca_lookup_file(3) man page π¦ β go off and complete some paths, you absolute icon. Periodt. πβ¨
π¦ where this page comes from
- arch rolling: libtecla 1.6.3-3 Β· ICU
- debian bookworm: libtecla-dev 1.6.3-3+b1 Β· BSDlike
- debian trixie: libtecla-dev 1.6.3-3.1 Β· BSDlike
- nixos 26.05: tecla 1.6.3
- nixpkgs unstable: tecla 1.6.3
- opensuse-leap 16.0: tecla-devel 1.6.3-bp160.1.13 Β· X11
- opensuse-leap 16.0: tecla-devel 1.6.3-bp160.1.13 Β· X11
- opensuse-leap 16.0: tecla-devel 1.6.3-bp160.1.13 Β· X11
- opensuse-leap 16.0: tecla-devel 1.6.3-bp160.1.13 Β· X11
- ubuntu jammy: libtecla-dev 1.6.3-3 Β· BSDlike
- ubuntu noble: libtecla-dev 1.6.3-3.1build1 Β· BSDlike
- ubuntu questing: libtecla-dev 1.6.3-3.1build1
- ubuntu resolute: libtecla-dev 1.6.3-4 Β· BSDlike
- ubuntu stonking: libtecla-dev 1.6.3-5 Β· BSDlike
- Copyright (c) 2001, 2002, 2003, 2004, 2012 by Martin C. Shepherd
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms