4
votes

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.

1
What happens when you do what you show in your code? Do you get compilation errors? Runtime errors? It doesn't work as expected (in which case, what's the actual and expected results?) - Some programmer dude
my_pattern or pattern? - 0x90
my_pattern, I'm sorry I have corrected the code - famedoro

1 Answers

5
votes

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.