Check if the file exists including on PATH
Given the name of a file in C, I want to determine if this file exists and has execute permission on it. All I have is this:
if( access( filename, X_OK) != 0 ) {
But that won't search PATH for files, and it will also match directories (which I don't want). Can anyone please help?
Edit:
Alternatively, when I run execvp () on the child process, is there a way to check the return value of execvp () and signal the parent process to die with an error message?
a source to share
This code is not exactly what you want, as it just blindly performs the first thing. But you can change the lookup code so that instead of calling execve
you call access
and then stat
to see if it is a directory. I think only the last function execvepath
should be replaced.
In the best Unix tradition, code is "self-documenting" (ie, undocumented).
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "shellpath.h"
static void *malloc_check(const char *what, size_t n) {
void *p = malloc(n);
if (p == NULL) {
fprintf(stderr, "Cannot allocate %zu bytes to %s\n", n, what);
exit(2);
}
return p;
}
static char *strsave(const char *s, const char *lim) {
if (lim == NULL)
lim = s + strlen(s);
char *p = malloc_check("save string", lim - s + 1);
strncpy(p, s, lim-s);
p[lim-s] = '\0';
return p;
}
char ** shellpath(void) {
const char *path = getenv("PATH");
if (!path)
path = "/bin:/usr/bin:/usr/local/bin";
char **vector = // size is overkill
malloc_check("hold path elements", strlen(path) * sizeof(*vector));
const char *p = path;
int next = 0;
while (p) {
char *q = strchr(p, ':');
vector[next++] = strsave(p, q);
p = q ? q + 1 : NULL;
}
vector[next] = NULL;
return vector;
}
void freeshellpath (char *shellpath[]) {
for (int i = 0; shellpath[i]; i++)
free(shellpath[i]);
free(shellpath);
}
unsigned maxpathlen(char *path[], const char *base) {
unsigned blen = strlen(base);
unsigned n = 0;
for (int i = 0; path[i]; i++) {
unsigned pn = strlen(path[i]);
if (pn > n) n = pn;
}
return blen+n+1;
}
void execvepath(char *path[], const char *base, char *const argv[],
char *const envp[])
{
if (strchr(base, '/'))
execve(base, argv, envp);
else {
size_t maxlen = maxpathlen(path, base)+1;
char *buf = malloc_check("hold path", maxlen);
for (int i = 0; path[i]; i++) {
snprintf(buf, maxlen, "%s/%s", path[i], base);
execve(buf, argv, envp);
}
}
}
a source to share
You can use the following functions. like .. the following codes contain some pseudocode, but they are easy to implement
if the given path contains the current directory path,
like /root/a or ./abc, then
return access( filename, X_OK) == 0;
Else - if the given path only contains the filename,
{
getenv( "PATH" );
while( iterate the directories in PATH )
{
if( search( PATH_directory, filename ) )
{
// create the full path string with strcat, strcpy, and/or etc.
full_path = blabla
if( !is_directory( full_path ) && access( filename, X_OK ) == 0 )
return 1; // Yeah~~ We got it!!!
}
}
return 0; // Nah, I don't think there is any of such a file.
}
int is_directory( const char* path )
{
struct stat file_info;
return ( stat( path, &file_info ) == 0 ) ? S_ISDIR( file_info.st_mode ) : 0;
}
int search( const char* file_name, const char* path )
{
struct dirent* dptr;
DIR* dirp;
if( (dirp = opendir( path )) == NULL )
return 0;
while( dptr = readdir( dirp ) )
{
if( strcmp( file_name, dptr->d_name ) == 0 )
{
closedir( dirp );
return 1;
}
}
closedir( dirp );
return 0;
}
a source to share