blob: d00a04be3fe07cfc31671b4e92dcd9d23daec798 [file]
//===----------------------------------------------------------------------===//
//
// 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