From 07d30629d78474ed810cad73ead71db96079aff0 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Sat, 28 Sep 2024 00:23:22 -0500 Subject: Add directory interface. --- source/fud_directory.cpp | 192 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 source/fud_directory.cpp (limited to 'source/fud_directory.cpp') diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp new file mode 100644 index 0000000..39f2e10 --- /dev/null +++ b/source/fud_directory.cpp @@ -0,0 +1,192 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_directory.hpp" + +#include +#include +#include + +namespace fud { + +Directory::Directory(String name) : m_name{name} +{ + if (!m_name.valid()) { + return; + } + + if (!m_name.utf8Valid()) { + m_status = FudStatus::Utf8Invalid; + return; + } + + m_directory = opendir(m_name.c_str()); + if (m_directory == nullptr) { + m_status = FudStatus::Failure; + m_errorCode = errno; + return; + } + + m_dirFd = dirfd(m_directory); + if (m_dirFd == -1) { + m_status = FudStatus::Failure; + m_errorCode = errno; + closedir(m_directory); + m_directory = nullptr; + return; + } + + m_errorCode = 0; + m_status = FudStatus::Success; +} + +Directory::Directory(Directory&& rhs) : + m_name{std::move(rhs.m_name)}, m_directory{rhs.m_directory}, m_dirFd{rhs.m_dirFd} +{ + rhs.m_directory = nullptr; + rhs.m_dirFd = -1; +} + +Directory& Directory::operator=(Directory&& rhs) +{ + m_name = std::move(rhs.m_name); + m_directory = rhs.m_directory; + m_dirFd = rhs.m_dirFd; + + rhs.m_directory = nullptr; + rhs.m_dirFd = -1; + + return *this; +} + +Directory::~Directory() +{ + if (m_directory != nullptr) { + closedir(m_directory); + m_directory = nullptr; + } +} + +Result, FudStatus> Directory::getNextEntry() +{ + using RetType = Result, FudStatus>; + + if (m_directory == nullptr || m_dirFd == -1) { + m_status = FudStatus::ObjectInvalid; + return RetType::error(m_status); + } + + errno = 0; + auto* dirEntry = readdir(m_directory); + if (dirEntry == nullptr) { + if (errno != 0) { + m_errorCode = errno; + m_status = FudStatus::Failure; + return RetType::error(m_status); + } else { + return RetType::okay(std::nullopt); + } + } + + const char* entryName = dirEntry->d_name; + if (entryName == nullptr) { + m_errorCode = -1; + m_status = FudStatus::NullPointer; + return RetType::error(m_status); + } + + m_errorCode = 0; + + using Stat = struct stat; + Stat statBuffer{}; + auto flags = 0; + auto status = fstatat(m_dirFd, entryName, &statBuffer, flags); + if (status == -1) { + m_errorCode = errno; + m_status = FudStatus::Failure; + return RetType::error(m_status); + } + + DirectoryEntryType entryType; + switch (statBuffer.st_mode & S_IFMT) { + case S_IFBLK: + entryType = DirectoryEntryType::Block; + break; + case S_IFCHR: + entryType = DirectoryEntryType::Character; + break; + case S_IFDIR: + entryType = DirectoryEntryType::Directory; + break; + case S_IFIFO: + entryType = DirectoryEntryType::NamedPipe; + break; + case S_IFLNK: + entryType = DirectoryEntryType::SymbolicLink; + break; + case S_IFREG: + entryType = DirectoryEntryType::RegularFile; + break; + case S_IFSOCK: + entryType = DirectoryEntryType::UnixSocket; + break; + default: + entryType = DirectoryEntryType::Unknown; + break; + } + + static_assert(std::is_same_v); + static_assert(sizeof(decltype(statBuffer.st_size)) <= sizeof(size_t)); + + size_t size{0}; + if (statBuffer.st_size < 0) { + size = SIZE_MAX; + } else { + size = static_cast(statBuffer.st_size); + } + + static_assert(std::is_same_v); + static_assert(sizeof(decltype(statBuffer.st_nlink)) <= sizeof(size_t)); + + DirectoryEntry entry{ + String{dirEntry->d_name}, + size, + static_cast(statBuffer.st_nlink), + statBuffer.st_mtime, + entryType}; + + if (!entry.name.valid()) { + m_status = FudStatus::StringInvalid; + return RetType::error(m_status); + } + + m_status = FudStatus::Success; + return RetType::okay(std::move(entry)); +} + +FudStatus Directory::reset() +{ + if (m_directory != nullptr) { + rewinddir(m_directory); + } else { + m_status = FudStatus::ObjectInvalid; + return m_status; + } + return FudStatus::NotImplemented; +} + +} // namespace fud -- cgit v1.2.3