diff --git a/libc/Makefile b/libc/Makefile
index 8a139d89..a5060518 100644
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -296,6 +296,7 @@ $(CPUDIR)/fork.o \
$(CPUDIR)/setjmp.o \
$(CPUDIR)/signal.o \
$(CPUDIR)/syscall.o \
+dirent/dscandir_r.o \
dirent/fdopendir.o \
dirent/opendir.o \
dirent/scandir.o \
diff --git a/libc/dirent/dscandir_r.cpp b/libc/dirent/dscandir_r.cpp
new file mode 100644
index 00000000..0dec6bf7
--- /dev/null
+++ b/libc/dirent/dscandir_r.cpp
@@ -0,0 +1,85 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
+
+ This file is part of the Sortix C Library.
+
+ The Sortix C Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ The Sortix C Library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with the Sortix C Library. If not, see .
+
+ dirent/dscandir_r.cpp
+ Filtered and sorted directory reading.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+extern "C"
+int dscandir_r(DIR* dir,
+ struct dirent*** namelist_ptr,
+ int (*filter)(const struct dirent*, void*),
+ void* filter_ctx,
+ int (*compare)(const struct dirent**, const struct dirent**, void*),
+ void* compare_ctx)
+{
+ rewinddir(dir);
+
+ size_t namelist_used = 0;
+ size_t namelist_length = 0;
+ struct dirent** namelist = NULL;
+
+ if ( false )
+ {
+ out_error:
+ for ( size_t i = 0; i < namelist_used; i++ )
+ free(namelist[i]);
+ free(namelist);
+ return errno = EOVERFLOW, -1;
+ }
+
+ while ( struct dirent* entry = readdir(dir) )
+ {
+ if ( filter && !filter(entry, filter_ctx) )
+ continue;
+ if ( (size_t) INT_MAX <= namelist_used )
+ goto out_error;
+ if ( namelist_used == namelist_length )
+ {
+ size_t new_length = namelist_length ? 2 * namelist_length : 8;
+ size_t new_size = new_length * sizeof(struct dirent*);
+ struct dirent** list = (struct dirent**) realloc(namelist, new_size);
+ if ( !list )
+ goto out_error;
+ namelist = list;
+ namelist_length = new_length;
+ }
+ size_t name_length = strlen(entry->d_name);
+ size_t dirent_size = sizeof(struct dirent) + name_length + 1;
+ struct dirent* dirent = (struct dirent*) malloc(dirent_size);
+ if ( !dirent )
+ goto out_error;
+ memcpy(dirent, entry, sizeof(*entry));
+ strcpy(dirent->d_name, entry->d_name);
+ namelist[namelist_used++] = dirent;
+ }
+
+ if ( compare )
+ qsort_r(namelist, namelist_used, sizeof(struct dirent*),
+ (int (*)(const void*, const void*, void*)) compare, compare_ctx);
+
+ return *namelist_ptr = namelist, (int) namelist_used;
+}
diff --git a/libc/dirent/scandir.cpp b/libc/dirent/scandir.cpp
index 428acf49..b9938f83 100644
--- a/libc/dirent/scandir.cpp
+++ b/libc/dirent/scandir.cpp
@@ -1,6 +1,6 @@
/*******************************************************************************
- Copyright(C) Jonas 'Sortie' Termansen 2013.
+ Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This file is part of the Sortix C Library.
@@ -28,6 +28,17 @@
#include
#include
+static int wrap_filter(const struct dirent* dirent, void* function)
+{
+ return ((int (*)(const struct dirent*)) function)(dirent);
+}
+
+static int wrap_compare(const struct dirent** dirent_a,
+ const struct dirent** dirent_b, void* function)
+{
+ return ((int (*)(const struct dirent**, const struct dirent**)) function)(dirent_a, dirent_b);
+}
+
extern "C"
int scandir(const char* path, struct dirent*** namelist_ptr,
int (*filter)(const struct dirent*),
@@ -36,52 +47,14 @@ int scandir(const char* path, struct dirent*** namelist_ptr,
DIR* dir = opendir(path);
if ( !dir )
return -1;
-
- size_t namelist_used = 0;
- size_t namelist_length = 0;
- struct dirent** namelist = NULL;
-
- if ( false )
- {
- out_error:
- for ( size_t i = 0; i < namelist_used; i++ )
- free(namelist[i]);
- free(namelist);
- closedir(dir);
- return errno = EOVERFLOW, -1;
- }
-
- while ( struct dirent* entry = readdir(dir) )
- {
- if ( filter && !filter(entry) )
- continue;
- if ( (size_t) INT_MAX <= namelist_used )
- goto out_error;
- if ( namelist_used == namelist_length )
- {
- size_t new_length = namelist_length ? 2 * namelist_length : 8;
- size_t new_size = new_length * sizeof(struct dirent*);
- struct dirent** list = (struct dirent**) realloc(namelist, new_size);
- if ( !list )
- goto out_error;
- namelist = list;
- namelist_length = new_length;
- }
- size_t name_length = strlen(entry->d_name);
- size_t dirent_size = sizeof(struct dirent) + name_length + 1;
- struct dirent* dirent = (struct dirent*) malloc(dirent_size);
- if ( !dirent )
- goto out_error;
- memcpy(dirent, entry, sizeof(*entry));
- strcpy(dirent->d_name, entry->d_name);
- namelist[namelist_used++] = dirent;
- }
-
- if ( compare )
- qsort(namelist, namelist_used, sizeof(struct dirent*),
- (int (*)(const void*, const void*)) compare);
-
+ int (*used_filter)(const struct dirent*,
+ void*) = filter ? wrap_filter : NULL;
+ int (*used_compare)(const struct dirent**,
+ const struct dirent**,
+ void*) = compare ? wrap_compare : NULL;
+ int result = dscandir_r(dir, namelist_ptr,
+ used_filter, (void*) filter,
+ used_compare, (void*) compare);
closedir(dir);
-
- return *namelist_ptr = namelist, (int) namelist_used;
+ return result;
}
diff --git a/libc/include/dirent.h b/libc/include/dirent.h
index ba2aa326..446ee9a2 100644
--- a/libc/include/dirent.h
+++ b/libc/include/dirent.h
@@ -89,6 +89,11 @@ struct dirent
int alphasort(const struct dirent**, const struct dirent**);
int closedir(DIR* dir);
int dirfd(DIR* dir);
+int dscandir_r(DIR*, struct dirent***,
+ int (*)(const struct dirent*, void*),
+ void*,
+ int (*)(const struct dirent**, const struct dirent**, void*),
+ void*);
DIR* fdopendir(int fd);
DIR* opendir(const char* path);
struct dirent* readdir(DIR* dir);