0

How do I use scandir, so that it only fills my struct dirent ** namelist, with folders?

John NG
  • 29
  • 1
  • 5

1 Answers1

4

You can filter the entities to list by providing a filter function, which should return a non-zero value if the file is to be included in the list.

Unfortunately, the members of the dirent struct don't tell you whether you have a directory or not (although your system might include a type field), so you must use some other means to find directories, for example stat:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

int filter_dir(const struct dirent *e)
{
    struct stat st;

    stat(e->d_name, &st);
    return (st.st_mode & S_IFDIR);
}

You could also try to call opendir on the name and check wheter that succeeds. (But don't forget to close it if it is successful.)

This works for the current directory, because the names exclude the path. The filter also doesn't provide a slot for passing additional data, so the best you can do is to define a global variable that holds the path and that you must set beforehand.

Here's an implementation with a wrapper function, scandir_dir:

#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

static const char *filterdir;

static int filter_dir(const struct dirent *e)
{
    char buf[NAME_MAX];
    struct stat st;

    if (filterdir) {
        snprintf(buf, sizeof(buf), "%s/%s", filterdir, e->d_name);    
        stat(buf, &st);
    } else {    
        stat(e->d_name, &st);
    }
    return (st.st_mode & S_IFDIR);
}

int scandir_dir(const char *path, struct dirent ***namelist)
{
    int n;

    filterdir = path;
    n = scandir(path, namelist, filter_dir, alphasort);
    filterdir = NULL;

    return n;
}

int main()
{
    struct dirent **namelist;
    int n;

    n = scandir_dir("/some/dir", &namelist);

    if (n < 0) {
        perror("scandir");
    } else {
        int i;

        for (i = 0; i < n; i++) {
            printf("%s\n", namelist[i]->d_name);
            free(namelist[i]);
        }
        free(namelist);
    }

    return 0;
}
Community
  • 1
  • 1
M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Is there a reason why filter_dir is declared to be static? – ruisen Apr 17 '21 at 23:59
  • Yes, @ruisen: `filterdir` is private state that must be accessible from the filtering function and from the function that sets it. Making it `static` will not make it accessible from other compilation units. (It doesn't matter in this small example program, where everything is in one file, though.) – M Oehm Apr 18 '21 at 07:49
  • Thanks for the reply! If this function were implemented as a class member function, why would it need to be made static? I saw a comment in the codebase saying that since it calls scandir both the calling function and filter function has to be static - but I can't seem to find any reason why. – ruisen Apr 18 '21 at 22:00
  • This a an answer in C, where `static` functions are private to the current compilation unit, that is source file. It doesn't have anything to do with `static` class members in C++ or similar languages, which are functions that don't act on a class instance. Also, what codebase are you talking about? I've got a feeling that you use my answer as a pretext to drag me into a completely unrelated discussion. – M Oehm Apr 19 '21 at 06:50