diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-09-28 00:23:22 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-09-28 00:23:22 -0500 |
commit | 07d30629d78474ed810cad73ead71db96079aff0 (patch) | |
tree | 5f43fd8f6a818a590818e0c474f03a1f05076dba /source | |
parent | 63711877057f1f89b4d1774e24fe20907a3af656 (diff) |
Add directory interface.
Diffstat (limited to 'source')
-rw-r--r-- | source/fud_c_file.cpp | 65 | ||||
-rw-r--r-- | source/fud_directory.cpp | 192 | ||||
-rw-r--r-- | source/fud_string.cpp | 38 |
3 files changed, 288 insertions, 7 deletions
diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp index a85fd00..cad4c9d 100644 --- a/source/fud_c_file.cpp +++ b/source/fud_c_file.cpp @@ -29,7 +29,7 @@ CBinaryFile::CBinaryFile(const String& filename, CFileMode mode) CBinaryFile::CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags) : m_filename{filename}, m_extraFlags{extraFlags}, - m_mode{String(CBinaryFileModeFromFlags(mode)).append(extraFlags)}, + m_mode{String(CBinaryFileModeFromFlags(mode)).catenate(extraFlags)}, m_modeFlags{mode} { } @@ -137,8 +137,8 @@ ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t l return result; } - auto* destPtr = static_cast<char*>(destination); - result.bytesRead = fread(destPtr, 1, length, m_file); + auto* destBytes = static_cast<char*>(destination); + result.bytesRead = fread(destBytes, 1, length, m_file); static_cast<void>(reset()); if (result.bytesRead != length) { result.status = FileStatus::PartialSuccess; @@ -149,6 +149,65 @@ ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t l return result; } +WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t length) +{ + auto offsetResult = size(); + if (offsetResult.isError()) { + return WriteResult{0, offsetResult.getError()}; + } + + return write(source, sourceSize, length, offsetResult.getOkay()); +} + +WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t length, size_t offset) +{ + WriteResult result{}; + if (length == 0) { + return result; + } + + if (source == nullptr) { + result.status = FileStatus::NullPointer; + return result; + } + + if (offset > LONG_MAX || SIZE_MAX - offset < length || sourceSize < length) { + result.status = FileStatus::InvalidArgument; + return result; + } + + auto fileSizeResult = size(); + if (fileSizeResult.isError()) { + result.status = fileSizeResult.getError(); + return result; + } + + // TODO: proper way of handling this + auto fileSize = fileSizeResult.getOkay(); + int seekResult; + if (offset > fileSize) { + seekResult = fseek(m_file, 0, SEEK_END); + } else { + seekResult = fseek(m_file, static_cast<long>(offset), SEEK_SET); + } + + if (seekResult != 0) { + result.status = FileStatus::Error; + return result; + } + + auto* sourceBytes = static_cast<const char*>(source); + result.bytesWritten = fwrite(sourceBytes, 1, length, m_file); + static_cast<void>(reset()); + if (result.bytesWritten != length) { + result.status = FileStatus::PartialSuccess; + } else { + result.status = FileStatus::Success; + } + + return result; +} + FileStatus CBinaryFile::reset() const { if (!isOpen()) { return FileStatus::InvalidState; 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 <cerrno> +#include <fcntl.h> +#include <sys/stat.h> + +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<std::optional<DirectoryEntry>, FudStatus> Directory::getNextEntry() +{ + using RetType = Result<std::optional<DirectoryEntry>, 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<decltype(statBuffer.st_size), long>); + 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<size_t>(statBuffer.st_size); + } + + static_assert(std::is_same_v<decltype(statBuffer.st_nlink), unsigned long>); + static_assert(sizeof(decltype(statBuffer.st_nlink)) <= sizeof(size_t)); + + DirectoryEntry entry{ + String{dirEntry->d_name}, + size, + static_cast<size_t>(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 diff --git a/source/fud_string.cpp b/source/fud_string.cpp index 74742e1..52d6b8f 100644 --- a/source/fud_string.cpp +++ b/source/fud_string.cpp @@ -236,7 +236,7 @@ FudStatus String::pushBack(const FudUtf8& letter) return FudStatus::Success; } -FudStatus String::catenate(StringView source) +FudStatus String::append(StringView source) { if (!valid()) { return FudStatus::StringInvalid; @@ -262,13 +262,19 @@ FudStatus String::catenate(StringView source) return status; } -String String::append(const String& rhs) const +String String::catenate(const char* rhs) const +{ + String rhsString{rhs}; + return catenate(rhsString); +} + +String String::catenate(const String& rhs) const { String output{}; output.m_length = 1; output.m_capacity = 0; - if (!valid()) { + if (!valid() || !rhs.valid()) { return output; } @@ -279,7 +285,9 @@ String String::append(const String& rhs) const } auto* destPtr = output.data(); - auto status = copyMem(destPtr, m_capacity, rhs.data(), rhs.length()); + auto status = copyMem(destPtr, m_capacity, data(), length()); + fudAssert(status == FudStatus::Success); + status = copyMem(destPtr + length(), m_capacity, rhs.data(), rhs.length()); fudAssert(status == FudStatus::Success); static_cast<void>(status); fudAssert(output.nullTerminate() == FudStatus::Success); @@ -287,6 +295,28 @@ String String::append(const String& rhs) const return output; } +bool String::compare(const String& rhs) const { + if (!valid() || !rhs.valid()) { + return false; + } + + if (length() != rhs.length()) { + return false; + } + + if (isLarge() && data() == rhs.data()) + { + return true; + } + + auto diffResult = compareMem(data(), length(), rhs.data(), rhs.length()); + if (diffResult.isError()) { + return false; + } + + return diffResult.getOkay() == 0; +} + const utf8* String::begin() const { return data(); |