/* * 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_c_file.hpp" namespace fud { CBinaryFile::CBinaryFile(const String& filename, CFileMode mode) : m_filename{filename}, m_mode{CBinaryFileModeFromFlags(mode)}, m_modeFlags{mode} { } CBinaryFile::CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags) : m_filename{filename}, m_extraFlags{extraFlags}, m_mode{String(CBinaryFileModeFromFlags(mode)).catenate(extraFlags)}, m_modeFlags{mode} { } CBinaryFile::~CBinaryFile() { close(); } FudStatus CBinaryFile::open() { if (!m_filename.valid()) { return FudStatus::ObjectInvalid; } m_file = fopen(m_filename.c_str(), m_mode.c_str()); return m_file != nullptr ? FudStatus::Success : FudStatus::Failure; } void CBinaryFile::close() { if (m_file != nullptr) { fclose(m_file); m_file = nullptr; } } const FILE* CBinaryFile::file() const { return m_file; } FILE* CBinaryFile::file() { return m_file; } bool CBinaryFile::isOpen() const { return m_file != nullptr; } Result CBinaryFile::size() const { using RetType = Result; if (!isOpen()) { return RetType::error(FudStatus::OperationInvalid); } auto seekStatus = fseek(m_file, 0, SEEK_END); if (seekStatus != 0) { return RetType::error(FudStatus::Failure); } auto fileSizeResult = ftell(m_file); if (fileSizeResult < 0) { return RetType::error(FudStatus::Failure); } auto fileSize = static_cast(fileSizeResult); auto resetStatus = reset(); if (resetStatus != FudStatus::Success) { return RetType::error(resetStatus); } return RetType::okay(fileSize); } ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t length) { return read(destination, destinationSize, length, 0); } ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t length, size_t offset) { ReadResult result{}; if (length == 0) { return result; } if (destination == nullptr) { result.status = FudStatus::NullPointer; return result; } if (offset > LONG_MAX || SIZE_MAX - offset < length || destinationSize < length) { result.status = FudStatus::InvalidInput; return result; } auto fileSizeResult = size(); if (fileSizeResult.isError()) { result.status = fileSizeResult.getError(); return result; } auto fileSize = fileSizeResult.getOkay(); if (offset + length > fileSize) { result.status = FudStatus::InvalidInput; return result; } auto seekResult = fseek(m_file, static_cast(offset), SEEK_SET); if (seekResult != 0) { result.status = FudStatus::Failure; return result; } auto* destBytes = static_cast(destination); result.bytesRead = fread(destBytes, 1, length, m_file); static_cast(reset()); if (result.bytesRead != length) { result.status = FudStatus::Partial; } else { result.status = FudStatus::Success; } 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 = FudStatus::NullPointer; return result; } if (offset > LONG_MAX || SIZE_MAX - offset < length || sourceSize < length) { result.status = FudStatus::InvalidInput; 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(offset), SEEK_SET); } if (seekResult != 0) { result.status = FudStatus::Failure; return result; } auto* sourceBytes = static_cast(source); result.bytesWritten = fwrite(sourceBytes, 1, length, m_file); static_cast(reset()); if (result.bytesWritten != length) { result.status = FudStatus::Partial; } else { result.status = FudStatus::Success; } return result; } FudStatus CBinaryFile::reset() const { if (!isOpen()) { return FudStatus::OperationInvalid; } auto result = fseek(m_file, 0, SEEK_SET); return result == 0 ? FudStatus::Success : FudStatus::Failure; } } // namespace fud