| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Templated implementation of the scandir logic for dependency injection. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIBC_SRC___SUPPORT_FILE_SCAN_IMPL_H | |
| #define LLVM_LIBC_SRC___SUPPORT_FILE_SCAN_IMPL_H | |
| #include "hdr/errno_macros.h" | |
| #include "hdr/func/free.h" | |
| #include "hdr/func/malloc.h" | |
| #include "hdr/func/realloc.h" | |
| #include "hdr/types/struct_dirent.h" | |
| #include "src/__support/CPP/limits.h" | |
| #include "src/__support/error_or.h" | |
| #include "src/stdlib/qsort_util.h" | |
| #include "src/string/memory_utils/inline_memcpy.h" | |
| namespace LIBC_NAMESPACE_DECL { | |
| namespace internal { | |
| template <typename DirType> | |
| ErrorOr<int> scan_impl(const char *name, struct dirent ***namelist, | |
| int (*filter)(const struct dirent *), | |
| int (*compare)(const struct dirent **, | |
| const struct dirent **)) { | |
| auto res_open = DirType::open(name); | |
| if (!res_open) | |
| return LIBC_NAMESPACE::Error(res_open.error()); | |
| DirType *dir = res_open.value(); | |
| int error_code = 0; | |
| struct dirent **entries = nullptr; | |
| size_t count = 0; | |
| size_t buffer_capacity = 0; | |
| auto free_entries = [&entries, &count]() { | |
| if (entries == nullptr) | |
| return; | |
| for (size_t i = 0; i < count; ++i) | |
| ::free(entries[i]); | |
| ::free(entries); | |
| }; | |
| while (true) { | |
| auto res_read = dir->read(); | |
| if (!res_read) { | |
| error_code = res_read.error(); | |
| break; | |
| } | |
| struct dirent *entry = res_read.value(); | |
| if (entry == nullptr) | |
| break; | |
| if (filter != nullptr && !filter(entry)) | |
| continue; | |
| // Scandir must return the number of entries as int, anything | |
| // above that is non-representable. | |
| if (count == cpp::numeric_limits<int>::max()) { | |
| error_code = EOVERFLOW; | |
| break; | |
| } | |
| if (count >= buffer_capacity) { | |
| size_t new_capacity = (buffer_capacity == 0) ? 8 : buffer_capacity * 2; | |
| // Overflow check | |
| if (new_capacity > | |
| cpp::numeric_limits<size_t>::max() / sizeof(struct dirent *)) { | |
| error_code = ENOMEM; | |
| break; | |
| } | |
| struct dirent **bigger_buffer = static_cast<struct dirent **>( | |
| ::realloc(entries, new_capacity * sizeof(struct dirent *))); | |
| if (bigger_buffer == nullptr) { | |
| error_code = ENOMEM; | |
| break; | |
| } | |
| buffer_capacity = new_capacity; | |
| entries = bigger_buffer; | |
| } | |
| size_t reclen = DirType::reclen(entry); | |
| struct dirent *new_entry = static_cast<struct dirent *>(::malloc(reclen)); | |
| if (new_entry == nullptr) { | |
| error_code = ENOMEM; | |
| break; | |
| } | |
| inline_memcpy(new_entry, entry, reclen); | |
| entries[count] = new_entry; | |
| count++; | |
| } | |
| dir->close(); | |
| if (error_code != 0) { | |
| free_entries(); | |
| return LIBC_NAMESPACE::Error(error_code); | |
| } | |
| if (compare != nullptr && count > 1) { | |
| auto cmp_fn = [compare](const void *a, const void *b) { | |
| auto left = static_cast<const struct dirent **>(const_cast<void *>(a)); | |
| auto right = static_cast<const struct dirent **>(const_cast<void *>(b)); | |
| return compare(left, right); | |
| }; | |
| internal::unstable_sort(entries, count, sizeof(struct dirent *), cmp_fn); | |
| } | |
| *namelist = entries; | |
| return static_cast<int>(count); | |
| } | |
| } // namespace internal | |
| } // namespace LIBC_NAMESPACE_DECL | |
| #endif // LLVM_LIBC_SRC___SUPPORT_FILE_SCAN_IMPL_H |