Skip to content

Instantly share code, notes, and snippets.

@edsko
Last active August 14, 2018 08:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edsko/6e6ac1f007d35a88611410e547ca2e4c to your computer and use it in GitHub Desktop.
Save edsko/6e6ac1f007d35a88611410e547ca2e4c to your computer and use it in GitHub Desktop.
Utility to find and run an executable in the `dist-newstyle` directory
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int findUp(char* cwd, const char* needle, unsigned char typ);
void findDown(char* cwd, const char* needle, unsigned char typ, char* out);
DIR* opendirOrError(const char* name);
void append(char* x, const char* y);
int main(int argc, char** argv, char** env) {
if(argc < 2) {
printf("Usage: %s prog [args]\n", argv[0]);
exit(-1);
}
char* cwd = calloc(1024, 1);
strcpy(cwd, ".");
int foundDistNewStyle = findUp(cwd, "dist-newstyle", DT_DIR);
if(!foundDistNewStyle) {
fprintf(stderr, "Error: dist-newstyle not found\n");
exit(-1);
}
char* out = calloc(1024, 1);
findDown(cwd, argv[1], DT_REG, out);
if(!out[0]) {
fprintf(stderr, "Error: %s not found\n", argv[1]);
exit(-1);
}
execve(out, argv + 1, env);
}
/**
* Find directory entry needle, starting at cwd and working our way up,
* looking for entries of type typ (DT_DIR; DT_REG; see man readdir).
*
* The result will be written to cwd, so cwd must be large enough to contain
* possibly large paths.
*
* Returns 1 if found, 0 otherwise.
*/
int findUp(char* cwd, const char* needle, unsigned char typ) {
struct dirent* dirent = NULL;
int reachedRoot = 0;
while(!reachedRoot) {
DIR* dirp = opendirOrError(cwd);
while((dirent = readdir(dirp))) {
if(dirent->d_type == typ && !strcmp(dirent->d_name, needle)) {
append(cwd, "/");
append(cwd, needle);
return 1;
}
// inode of the root is 2 on ext4
if(!strcmp(dirent->d_name, ".") && dirent->d_ino == 2) {
reachedRoot = 1;
}
}
closedir(dirp);
append(cwd, "/..");
}
// Not found :-(
return 0;
}
/**
* Like findUp, but searching _down_ a directory tree instead, recursively.
*
* We store the result in out, which must be a suitably large buffer and
* initialized to zero.
*
* If we find more than one match, we return an error.
*/
void findDown(char* cwd, const char* needle, unsigned char typ, char* out) {
struct dirent* dirent = NULL;
// TODO: Right now this opens as many directories at a time as we might
// need to go down the directory tree. This is perhaps not ideal.
DIR* dirp = opendirOrError(cwd);
while((dirent = readdir(dirp))) {
if(dirent->d_type == typ && !strcmp(dirent->d_name, needle)) {
if(!out[0]) {
sprintf(out, "%s/%s", cwd, needle);
// We keep going, to check for other entries
} else {
fprintf(stderr, "Error: more than one match (%s, %s, possibly others)\n", out, cwd);
exit(-1);
}
}
if(dirent->d_type == DT_DIR && dirent->d_name[0] != '.') {
// Recursively go down the tree. We will only _append_ to cwd, never
// overwrite or remove stuff from it, so we can easily restore cwd
// simply by remembering the current length. We use recursion so
// we can rely on the C stack rather than having to maintain our own.
int oldLength = strlen(cwd);
append(cwd, "/");
append(cwd, dirent->d_name);
findDown(cwd, needle, typ, out);
cwd[oldLength] = 0;
}
}
closedir(dirp);
}
/**
* Simple wrapper around opendir that exits on error
*/
DIR* opendirOrError(const char* name) {
DIR* dirp = opendir(name);
if(dirp == NULL) {
fprintf(stderr, "Could not open %s: %s\n", name, strerror(errno));
exit(-1);
}
return dirp;
}
/**
* Append `y` to `x`, in-place
*/
void append(char* x, const char* y) {
strcpy(x + strlen(x), y);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment