Nine years ago, this question has been asked : Find a directory in shared library search path (Find a directory in shared library search path).
An answer has been given using : opendir()
then readdir()
then dlopen()
…
Nowadays, is there a simpler way to do it or should I still follow this SMOP ?
Advertisement
Answer
No, using scandir()
or glob()
is much more appropriate.
In fact, opendir()/readdir()/closedir() has basically never been the recommended way for anything in POSIXy systems like Linux that have glob()
, scandir()
, and nftw()
, because home-spun opendir()/readdir()/closedir() almost never handle the situation where files or directories are renamed, deleted, created, or moved during scanning; whereas the POSIX C library functions are supposed to handle those gracefully.
The only reason opendir()/readdir()/closedir() are pushed so hard, is that they are defined in the C standard (as opposed to POSIX), and therefore can be found in non-POSIXy systems too. But, in my opinion, just because some systems’ C libraries are crippled, is not a good reason to reinvent a bad wheel again and again; we have better tools available already.
For example, let’s say you have constructed an array of glob patterns (say, "/usr/lib/myapp/plugins/*.so", "/home/username/.config/myapp/plugins/*.so", NULL
), and you want to find the files that match those patterns. You use glob()
for this. For example:
#define _POSIX_C_SOURCE 200809L #include <stdlib.h> #include <glob.h> #include <stdio.h> #include <string.h> #include <errno.h> int report_error(const char *path, int errnum) { fprintf(stderr, "%s: %s.n", path, strerror(errnum)); return -1; } void report_glob_error(int result) { switch (result) { case GLOB_NOSPACE: fprintf(stderr, "%s.n", strerror(ENOMEM)); return; case GLOB_ABORTED: fprintf(stderr, "%s.n", strerror(EIO)); return; case GLOB_NOMATCH: fprintf(stderr, "%s.n", strerror(ENOENT)); return; } } int main(int argc, char *argv[]) { glob_t matches; size_t i; int arg, result; if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { const char *argv0 = (argc > 0) ? argv[0] : "(this)"; fprintf(stderr, "n"); fprintf(stderr, "Usage: %s [ -h | --help ]n", argv0); fprintf(stderr, " %s PATTERN [ PATTERN ... ]n", argv0); fprintf(stderr, "n"); return EXIT_FAILURE; } result = glob(argv[1], GLOB_ERR | GLOB_MARK, report_error, &matches); if (result) { report_glob_error(result); return EXIT_FAILURE; } for (arg = 2; arg < argc; arg++) { result = glob(argv[arg], GLOB_ERR | GLOB_MARK | GLOB_APPEND, report_error, &matches); if (result) { report_glob_error(result); return EXIT_FAILURE; } } printf("Found %zu matches:n", matches.gl_pathc); for (i = 0; i < matches.gl_pathc; i++) { printf(" %sn", matches.gl_pathv[i]); } globfree(&matches); return EXIT_SUCCESS; }
Compile and run the above with e.g. '/lib*/*/*.so'
as a parameter. Remember to put the pattern(s) in single quotes, as otherwise the shell will expand them.
If you want all files in certain directories, or need a more complicated filter for the file names (say, alternative glob patterns; you can check a name against a glob pattern using fnmatch()
), you use scandir()
.
Note that if glob patterns are not sufficient, you can use the POSIX regular expressions instead, via regcomp()
/regexec()
/regfree()
.
For an example of using scandir()
, I just posted one here, to answer a similar question.