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);