mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/scandir' into 'master'
feat(newlib): implement scandir and alphasort Closes IDF-2649 See merge request espressif/esp-idf!28068
This commit is contained in:
commit
a68013ceec
@ -26,7 +26,9 @@ set(srcs
|
||||
"stdatomic.c"
|
||||
"time.c"
|
||||
"sysconf.c"
|
||||
"realpath.c")
|
||||
"realpath.c"
|
||||
"scandir.c"
|
||||
)
|
||||
set(include_dirs platform_include)
|
||||
|
||||
if(CONFIG_SPIRAM_CACHE_WORKAROUND)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -55,6 +55,10 @@ void seekdir(DIR* pdir, long loc);
|
||||
void rewinddir(DIR* pdir);
|
||||
int closedir(DIR* pdir);
|
||||
int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
int scandir(const char *dirname, struct dirent ***out_dirlist,
|
||||
int (*select_func)(const struct dirent *),
|
||||
int (*cmp_func)(const struct dirent **, const struct dirent **));
|
||||
int alphasort(const struct dirent **d1, const struct dirent **d2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
78
components/newlib/scandir.c
Normal file
78
components/newlib/scandir.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "scandir";
|
||||
|
||||
int alphasort(const struct dirent **lhs, const struct dirent **rhs)
|
||||
{
|
||||
return strcoll((*lhs)->d_name, (*rhs)->d_name);
|
||||
}
|
||||
|
||||
int scandir(const char *dirname, struct dirent ***out_dirlist,
|
||||
int (*select_func)(const struct dirent *),
|
||||
int (*cmp_func)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
DIR *dir_ptr = NULL;
|
||||
struct dirent *entry;
|
||||
size_t num_entries = 0;
|
||||
size_t array_size = 8; /* initial estimate */
|
||||
struct dirent **entries = NULL;
|
||||
int ret = -1;
|
||||
|
||||
entries = malloc(array_size * sizeof(struct dirent *));
|
||||
ESP_RETURN_ON_FALSE(entries, -1, TAG, "Malloc failed for entries");
|
||||
|
||||
dir_ptr = opendir(dirname);
|
||||
ESP_GOTO_ON_FALSE(dir_ptr, -1, out, TAG, "Failed to open directory: %s", dirname);
|
||||
|
||||
while ((entry = readdir(dir_ptr)) != NULL) {
|
||||
/* skip entries that don't match the filter function */
|
||||
if (select_func != NULL && !select_func(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent *entry_copy = malloc(sizeof(struct dirent));
|
||||
ESP_GOTO_ON_FALSE(entry_copy, -1, out, TAG, "Malloc failed for entry_copy");
|
||||
|
||||
*entry_copy = *entry;
|
||||
entries[num_entries++] = entry_copy;
|
||||
|
||||
/* grow the array size if it's full */
|
||||
if (num_entries >= array_size) {
|
||||
array_size *= 2;
|
||||
struct dirent **new_entries = realloc(entries, array_size * sizeof(struct dirent *));
|
||||
ESP_GOTO_ON_FALSE(new_entries, -1, out, TAG, "Realloc failed for entries");
|
||||
entries = new_entries;
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the entries if a comparison function is provided */
|
||||
if (num_entries && cmp_func) {
|
||||
qsort(entries, num_entries, sizeof(struct dirent *),
|
||||
(int (*)(const void *, const void *))cmp_func);
|
||||
}
|
||||
|
||||
*out_dirlist = entries;
|
||||
ret = num_entries;
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
while (num_entries > 0) {
|
||||
free(entries[--num_entries]);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
if (dir_ptr) {
|
||||
closedir(dir_ptr);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -12,8 +12,11 @@
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include "sys/dirent.h"
|
||||
#include "unity.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_vfs.h"
|
||||
|
||||
|
||||
TEST_CASE("misc - posix_memalign", "[newlib_misc]")
|
||||
@ -91,3 +94,90 @@ TEST_CASE("misc - realpath", "[newlib_misc]")
|
||||
TEST_ASSERT_EQUAL_STRING("/abc/def", out_new);
|
||||
free(out_new);
|
||||
}
|
||||
|
||||
static int s_select_calls = 0;
|
||||
|
||||
typedef struct {
|
||||
const char **filenames;
|
||||
size_t num_files;
|
||||
size_t current_index;
|
||||
} scandir_test_dir_context_t;
|
||||
|
||||
static DIR *scandir_test_opendir(void* ctx, const char* name)
|
||||
{
|
||||
scandir_test_dir_context_t *dir_ctx = (scandir_test_dir_context_t *)ctx;
|
||||
dir_ctx->current_index = 0;
|
||||
static DIR dir = {};
|
||||
return &dir;
|
||||
}
|
||||
|
||||
static struct dirent *scandir_test_readdir(void* ctx, DIR* pdir)
|
||||
{
|
||||
scandir_test_dir_context_t *dir_ctx = (scandir_test_dir_context_t *)ctx;
|
||||
if (dir_ctx->current_index >= dir_ctx->num_files) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dirent entry;
|
||||
snprintf(entry.d_name, sizeof(entry.d_name), "%s", dir_ctx->filenames[dir_ctx->current_index]);
|
||||
dir_ctx->current_index++;
|
||||
return &entry;
|
||||
}
|
||||
|
||||
static int scandir_test_closedir(void* ctx, DIR* pdir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scandir_test_select(const struct dirent *entry)
|
||||
{
|
||||
s_select_calls++;
|
||||
return strstr(entry->d_name, "test") != NULL;
|
||||
}
|
||||
|
||||
TEST_CASE("file - scandir", "[newlib_misc]")
|
||||
{
|
||||
const char *test_filenames[] = {
|
||||
".",
|
||||
"..",
|
||||
"test_file1.txt",
|
||||
"file2.txt",
|
||||
"test_file3.txt",
|
||||
"test_file4.txt",
|
||||
"test_file5.txt",
|
||||
"test_file6.txt",
|
||||
"test_file7.txt",
|
||||
"test_file8.txt",
|
||||
"test_file9.txt",
|
||||
"test_file10.txt",
|
||||
};
|
||||
size_t num_test_files = sizeof(test_filenames) / sizeof(test_filenames[0]);
|
||||
|
||||
scandir_test_dir_context_t scandir_test_dir_ctx = { .filenames = test_filenames, .num_files = num_test_files };
|
||||
|
||||
const esp_vfs_t scandir_test_vfs = {
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.opendir_p = scandir_test_opendir,
|
||||
.readdir_p = scandir_test_readdir,
|
||||
.closedir_p = scandir_test_closedir
|
||||
};
|
||||
|
||||
TEST_ESP_OK(esp_vfs_register("/data", &scandir_test_vfs, &scandir_test_dir_ctx));
|
||||
|
||||
struct dirent **namelist;
|
||||
s_select_calls = 0;
|
||||
int n = scandir("/data", &namelist, scandir_test_select, alphasort);
|
||||
TEST_ASSERT_NOT_NULL(namelist);
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
|
||||
TEST_ASSERT_EQUAL(num_test_files, s_select_calls);
|
||||
TEST_ASSERT_EQUAL(9, n);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
TEST_ASSERT_NOT_NULL(strstr(namelist[i]->d_name, "test"));
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
|
||||
esp_vfs_unregister("/data");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user