Reputation: 1130
In C, what is the best way to find out if foo
is available at the command line to execute on the host? If I were at the bash command line, I'd run type foo
. In C, I could do type foo
as a system call and check the exit status. But it's a good idea to avoid system calls when possible, right? Is there a better way?
My program may have to do a system command that eventually would run foo
inside a shell script. (It's someone else's world and they supposedly use foo
.) But if it can tell at the start that foo
would be unavailable, it can avoid doing a lot of unnecessary computation because no foo
means failure is certain.
Upvotes: 2
Views: 93
Reputation: 12425
type
(and its cousin, which
) are commands provided by most Unix-like OS's and also implemented by most shells as an intrinsic.
You could simply invoke those commands by starting a child process from your program, and then read their output. If you want to rely on the behavior of a shell's version of the command, then you must start a child process that launches that shell, then command it to run type
or which
. If you'd rather not use child processes, then you must re-implement their logic in your program.
Using the FreeBSD implementation of which as a guide, we can see the basic steps for doing this:
$PATH
environment variable.$PATH
into the various directories that it contains.And in code:
Read $PATH
from the env:
if ((p = getenv("PATH")) == NULL)
exit(EXIT_FAILURE);
Call print_matches
with one of the arguments to which
, providing the entire value of the $PATH
:
while (argc > 0) {
memcpy(path, p, pathlen);
if (strlen(*argv) >= FILENAME_MAX ||
print_matches(path, *argv) == -1)
status = EXIT_FAILURE;
...
In print_matches
, break apart the $PATH
variable by splitting on the :
character:
while ((d = strsep(&path, ":")) != NULL) {
And for each directory, concatenate the target program to the directory (checking to make sure the string doesn't become too big):
if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
filename) >= (int)sizeof(candidate))
continue;
And then test to see if that file exists:
if (is_there(candidate)) {
Upvotes: 2