c++cparametersscandir

how can I parameterize select function in scandir


The scandir() function scans the directory dir, calling select() on each directory entry as "int(*filter)(const struct dirent *)" How can I pass pattern value as parameter to fnmatch(const char *pattern, const char *string, int flags) function used in filter ?

Here my sample code

int my_selectgrf(const struct dirent *namelist)
{
   int  r = 0;
   char     my_pattern[] = "*.grf";
   r = fnmatch(my_pattern, namelist->d_name, FNM_PERIOD);

   return (r==0)?1:0;
}
scandir("/pub/data/grf", &namelist, my_selectgrf, alphasort);

my goal is to be able to use my_pattern as input parameter.


Solution

  • The short answer: You can't. This is an atrociously bad API, and it's outright shameful that something like this was added to POSIX as recently as 2008 (based on a bad design in glibc). This kind of API without a way to parameterize it or pass it a context should have been abolished 20+ years ago.

    With that said, there are some workarounds:

    Approach 1: Use a global variable, and if your code needs to be thread-safe, ensure that only one thread can be using scandir with the given scan function at a time, by locking. This of course serializes usage, which is probably not acceptable if you actually want to be calling the function from multiple threads.

    Approach 2: Use thread-local storage, either the GCC __thread keyword (or the C11 _Thread_local keyword, which GCC sadly still does not accept) or POSIX pthread_setspecific and family. This is fairly clean, but unfortunately it may not be correct; if the implementation of scandir internally used multiple threads, the parameter could fail to be available in some calls back to the scan function. At present, I don't believe there are multi-threaded implementations of scandir.

    Now, the better solution:

    Ditch scandir and write your own function to do the same thing, with the proper API. It's only a few lines anyway.