Этот код не совсем то, что вы хотите, так как он просто выполняет вслепую в первую очередь. Но вы можете изменить код поиска так, чтобы вместо вызова execve
вы вызывали access
, а затем stat
, чтобы узнать, не является ли это каталогом. Я думаю, что только последняя функция, execvepath
, должна быть заменена.
В лучших традициях Unix код является «самодокументируемым» (т.е. недокументированным).
#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);
}
}
}