summaryrefslogtreecommitdiff
path: root/source/fud_directory.cpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-09-28 00:23:22 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-09-28 00:23:22 -0500
commit07d30629d78474ed810cad73ead71db96079aff0 (patch)
tree5f43fd8f6a818a590818e0c474f03a1f05076dba /source/fud_directory.cpp
parent63711877057f1f89b4d1774e24fe20907a3af656 (diff)
Add directory interface.
Diffstat (limited to 'source/fud_directory.cpp')
-rw-r--r--source/fud_directory.cpp192
1 files changed, 192 insertions, 0 deletions
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