diff --git a/util-unix.cpp b/util-unix.cpp index 90e215c..4eed56d 100644 --- a/util-unix.cpp +++ b/util-unix.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include std::string System_error::message () const { @@ -310,25 +312,47 @@ int util_rename (const char* from, const char* to) return rename(from, to); } -static int dirfilter (const struct dirent* ent) +static size_t sizeof_dirent_for (DIR* p) { - // filter out . and .. - return std::strcmp(ent->d_name, ".") != 0 && std::strcmp(ent->d_name, "..") != 0; + long name_max = fpathconf(dirfd(p), _PC_NAME_MAX); + if (name_max == -1) { + #ifdef NAME_MAX + name_max = NAME_MAX; + #else + name_max = 255; + #endif + } + return offsetof(struct dirent, d_name) + name_max + 1; // final +1 is for d_name's null terminator } std::vector get_directory_contents (const char* path) { - struct dirent** namelist; - int n = scandir(path, &namelist, dirfilter, alphasort); - if (n == -1) { - throw System_error("scandir", path, errno); - } - std::vector contents(n); - for (int i = 0; i < n; ++i) { - contents[i] = namelist[i]->d_name; - free(namelist[i]); - } - free(namelist); + std::vector contents; + DIR* dir = opendir(path); + if (!dir) { + throw System_error("opendir", path, errno); + } + try { + std::vector buffer(sizeof_dirent_for(dir)); + struct dirent* dirent_buffer = reinterpret_cast(&buffer[0]); + struct dirent* ent = NULL; + int err = 0; + while ((err = readdir_r(dir, dirent_buffer, &ent)) == 0 && ent != NULL) { + if (std::strcmp(ent->d_name, ".") == 0 || std::strcmp(ent->d_name, "..") == 0) { + continue; + } + contents.push_back(ent->d_name); + } + if (err != 0) { + throw System_error("readdir_r", path, errno); + } + } catch (...) { + closedir(dir); + throw; + } + closedir(dir); + + std::sort(contents.begin(), contents.end()); return contents; }