1067 lines
22 KiB
C++
1067 lines
22 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <fennec/langproc/filesystem/file.h>
|
|
#include <fennec/langproc/filesystem/path.h>
|
|
|
|
|
|
#ifdef FENNEC_COMPILER_MSVC
|
|
|
|
|
|
|
|
#else
|
|
|
|
#include <sys/file.h>
|
|
|
|
#endif
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
constexpr const cstring fmode_translate(uint8_t mode) {
|
|
if (not file::is_valid(mode)) {
|
|
return "";
|
|
}
|
|
|
|
mode &= ~fmode_wide; // Ignore wide bit
|
|
|
|
switch (mode) {
|
|
case fmode_read: return "r";
|
|
case fmode_write: return "a";
|
|
|
|
// I chose r+ because for most read/write assets in the context of the game engine will be setting files, save files,
|
|
// and editor assets, which require a complete read of the file before any writing is done
|
|
case fmode_read | fmode_write: return "r+";
|
|
|
|
case fmode_write | fmode_trunc: return "w";
|
|
case fmode_read | fmode_write | fmode_trunc: return "w+";
|
|
|
|
case fmode_write | fmode_trunc | fmode_exclusive: return "wx";
|
|
case fmode_read | fmode_write | fmode_trunc | fmode_exclusive: return "wx+";
|
|
|
|
// Binary Files
|
|
case fmode_binary | fmode_read: return "rb";
|
|
case fmode_binary | fmode_write: return "ab";
|
|
|
|
case fmode_binary | fmode_read | fmode_write: return "rb+";
|
|
|
|
case fmode_binary | fmode_write | fmode_trunc: return "wb";
|
|
case fmode_binary | fmode_read | fmode_write | fmode_trunc: return "wb+";
|
|
|
|
case fmode_binary | fmode_write | fmode_trunc | fmode_exclusive: return "wxb";
|
|
case fmode_binary | fmode_read | fmode_write | fmode_trunc | fmode_exclusive: return "wxb+";
|
|
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
file& file::cout() {
|
|
static constexpr char path[] = "stdout";
|
|
static file out = []() -> file {
|
|
file res;
|
|
res._mode = fmode_write;
|
|
res._handle = stdout;
|
|
res._path = string(path);
|
|
return res;
|
|
}();
|
|
return out;
|
|
}
|
|
|
|
file& file::cin() {
|
|
static constexpr char path[] = "stdin";
|
|
static file out = []() -> file {
|
|
file res;
|
|
res._mode = fmode_read;
|
|
res._handle = stdin;
|
|
res._path = string(path);
|
|
return res;
|
|
}();
|
|
return out;
|
|
}
|
|
|
|
file& file::cerr() {
|
|
static constexpr char path[] = "stderr";
|
|
static file out = []() -> file {
|
|
file res;
|
|
res._mode = fmode_write;
|
|
res._handle = stderr;
|
|
res._path = string(path);
|
|
return res;
|
|
}();
|
|
return out;
|
|
}
|
|
|
|
file::file()
|
|
: _handle(nullptr)
|
|
, _path("")
|
|
, _mode(0)
|
|
, _error(nullptr) {
|
|
|
|
}
|
|
|
|
file::~file() {
|
|
close();
|
|
}
|
|
|
|
file::file(file&& file) noexcept
|
|
: _handle(file._handle), _path(file._path), _mode(file._mode), _error(file._error) {
|
|
file._handle = nullptr;
|
|
file._error = nullptr;
|
|
}
|
|
|
|
file& file::operator=(file&& file) noexcept {
|
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
|
|
|
close();
|
|
_handle = file._handle;
|
|
_path = file._path;
|
|
_mode = file._mode;
|
|
_error = file._error;
|
|
|
|
file._handle = nullptr;
|
|
file._path = "";
|
|
file._error = nullptr;
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool file::open(const cstring& p, uint8_t mode) {
|
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
|
|
|
// Ensure validity of the mode
|
|
if (not is_valid(mode)) {
|
|
return true;
|
|
}
|
|
|
|
// Close the file if already open
|
|
if (_handle) {
|
|
close();
|
|
}
|
|
|
|
// Attempt to open the file
|
|
_handle = fopen(p, fmode_translate(mode));
|
|
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Attempt to lock the file
|
|
if (flock(fileno(_handle), LOCK_EX)) {
|
|
_error = strerror(errno);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
return true;
|
|
}
|
|
|
|
_mode = mode;
|
|
_path = p;
|
|
_path = _path.absolute();
|
|
return false;
|
|
}
|
|
|
|
bool file::open(const string& p, uint8_t mode) {
|
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
|
|
|
// Ensure validity of the mode
|
|
if (not is_valid(mode)) {
|
|
return true;
|
|
}
|
|
|
|
// Close the file if already open
|
|
if (_handle) {
|
|
close();
|
|
}
|
|
|
|
// Attempt to open the file
|
|
_handle = fopen(p.cstr(), fmode_translate(mode));
|
|
|
|
// Validate the file
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Attempt to lock the file
|
|
if (flock(fileno(_handle), LOCK_EX)) {
|
|
_error = strerror(errno);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
return true;
|
|
}
|
|
|
|
// Set the orientation
|
|
fwide(_handle, mode & fmode_wide ? 1 : -1);
|
|
|
|
_mode = mode;
|
|
_path = p;
|
|
_path = _path.absolute();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::open(const path& p, uint8_t mode) {
|
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
|
|
|
// Ensure validity of the mode
|
|
if (not is_valid(mode)) {
|
|
return true;
|
|
}
|
|
|
|
// Close the file if already open
|
|
if (_handle) {
|
|
close();
|
|
}
|
|
|
|
// Attempt to open the file
|
|
_handle = fopen(p.cstr(), fmode_translate(mode));
|
|
|
|
// Validate the file
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Attempt to lock the file
|
|
if (flock(fileno(_handle), LOCK_EX)) {
|
|
_error = strerror(errno);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
return true;
|
|
}
|
|
|
|
// Set the orientation
|
|
fwide(_handle, mode & fmode_wide ? 1 : -1);
|
|
|
|
_mode = mode;
|
|
_path = p.absolute();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::close() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to unlock the file
|
|
if (flock(fileno(_handle), LOCK_UN)) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Close the file and reset variables
|
|
fclose(_handle);
|
|
_mode = 0;
|
|
_handle = nullptr;
|
|
_path = "";
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::commit() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to flush
|
|
if (fflush(_handle)) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::erase() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Close the file
|
|
const path path = move(_path);
|
|
if (close()) {
|
|
return true;
|
|
}
|
|
|
|
// Erase the file
|
|
remove(path.cstr());
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::rename(const cstring& str) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Validate path
|
|
path fpath = str;
|
|
fpath = fpath.absolute();
|
|
if (_path == fpath) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return true;
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return true;
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Close old file
|
|
fclose(_handle);
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(fpath.cstr(), fmode_translate(_mode), fnew);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Erase the old file
|
|
remove(_path.cstr());
|
|
|
|
// Set the new path
|
|
_path = fpath;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::rename(const string& str) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Validate path
|
|
path fpath = str;
|
|
fpath = fpath.absolute();
|
|
if (_path == fpath) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return true;
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return true;
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Close old file
|
|
fclose(_handle);
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(_path.cstr(), fmode_translate(_mode), fnew);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Set the new path
|
|
_path = fpath;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::rename(const path& p) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Validate path
|
|
const path fpath = p.absolute();
|
|
if (_path == fpath) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return true;
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return true;
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Close old file
|
|
fclose(_handle);
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(_path.cstr(), fmode_translate(_mode), fnew);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
// Set the new path
|
|
_path = fpath;
|
|
|
|
return false;
|
|
}
|
|
|
|
file file::copy(const cstring& str) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return file();
|
|
}
|
|
|
|
// Validate path
|
|
path fpath = str;
|
|
fpath = fpath.absolute();
|
|
if (_path == fpath) {
|
|
return file();
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return file();
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return file();
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(nullptr, fmode_translate(_mode), _handle);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Set the new path
|
|
file res;
|
|
res._handle = fnew;
|
|
res._path = fpath;
|
|
res._mode = fmode_write;
|
|
|
|
return res;
|
|
}
|
|
|
|
file file::copy(const string& str) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return file();
|
|
}
|
|
|
|
// Validate path
|
|
path fpath = str;
|
|
fpath = fpath.absolute();
|
|
if (_path == fpath) {
|
|
return file();
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return file();
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return file();
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(nullptr, fmode_translate(_mode), _handle);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Set the new path
|
|
file res;
|
|
res._handle = fnew;
|
|
res._path = fpath;
|
|
res._mode = fmode_write;
|
|
|
|
return res;
|
|
}
|
|
|
|
file file::copy(const path& p) {
|
|
static const size_t page_size = pagesize();
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return file();
|
|
}
|
|
|
|
// Validate path
|
|
const path fpath = p.absolute();
|
|
if (_path == fpath) {
|
|
return file();
|
|
}
|
|
|
|
// Attempt to open the new file
|
|
FILE* fnew = fopen(fpath.cstr(), "wx");
|
|
|
|
// Check for open failure
|
|
if (fnew == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Reopen this file as read
|
|
_handle = freopen(nullptr, "r", _handle);
|
|
|
|
// Check if it failed to reopen
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Initialize buffer
|
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
|
|
|
// Copy contents
|
|
size_t read = 0;
|
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle eof
|
|
if (feof(_handle)) {
|
|
fwrite(buffer, 1, read, fnew);
|
|
}
|
|
|
|
// Check new file for errors
|
|
if (ferror(fnew)) {
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
_mode = fmode_read;
|
|
return file();
|
|
}
|
|
|
|
// Check the original file for errors
|
|
if (ferror(_handle)) {
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
_error = strerror(errno);
|
|
fclose(fnew);
|
|
fclose(_handle);
|
|
_handle = nullptr;
|
|
_mode = 0;
|
|
_path = "";
|
|
return file();
|
|
}
|
|
|
|
// Cleanup the buffer
|
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
|
|
|
// Reopen the new file
|
|
_handle = freopen(nullptr, fmode_translate(_mode), _handle);
|
|
|
|
// Check for open failure
|
|
if (_handle == nullptr) {
|
|
_error = strerror(errno);
|
|
return file();
|
|
}
|
|
|
|
// Set the new path
|
|
file res;
|
|
res._handle = fnew;
|
|
res._path = fpath;
|
|
res._mode = fmode_write;
|
|
|
|
return res;
|
|
}
|
|
|
|
size_t file::get_pos() const {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return npos;
|
|
}
|
|
|
|
return ftell(_handle);
|
|
}
|
|
|
|
bool file::set_pos(size_t i) {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (fseek(_handle, i, SEEK_SET)) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::rewind() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
::rewind(_handle);
|
|
if (ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::eof() const {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
return feof(_handle);
|
|
}
|
|
|
|
char file::getc() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(not(_mode & fmode_wide), "Attempted Byte Operation on Wide File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
return fgetc(_handle);
|
|
}
|
|
|
|
wchar_t file::getwc() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
return fgetwc(_handle);
|
|
}
|
|
|
|
size_t file::read(void* data, size_t size, size_t n) {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
const size_t read = fread(data, size, n, _handle);
|
|
if (read != size && ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
}
|
|
|
|
return read;
|
|
}
|
|
|
|
string file::getline() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(not(_mode & fmode_wide), "Attempted Byte Operation on Wide File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
// Read the next line;
|
|
char* line = nullptr;
|
|
size_t size = 0;
|
|
const size_t read = ::getline(&line, &size, _handle);
|
|
if (read == npos && ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
if (line) free(line);
|
|
return string("");
|
|
}
|
|
|
|
string res = string(line, read);
|
|
free(line);
|
|
return res;
|
|
}
|
|
|
|
wstring file::getwline() {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return _wstring{ L"" };
|
|
}
|
|
|
|
// Read the next line;
|
|
wchar_t arr[257] = { L'\0' };
|
|
wcstring buff = arr;
|
|
wstring res{L""};
|
|
|
|
// read until first newline or end of file
|
|
while (fgetws(arr, 257, _handle)) {
|
|
res += wcstring(arr, buff.length());
|
|
if (buff.length() < 256 || buff[256] == L'\n') {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
_error = strerror(errno);
|
|
return wstring(L"");
|
|
}
|
|
|
|
bool file::putc(char c) {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(not(_mode & fmode_wide), "Attempted Byte Operation on Wide File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
int res;
|
|
if ((res = fputc(c, _handle)) != c && res != EOF) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool file::putwc(wchar_t c) {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
int res;
|
|
if ((res = fputc(c, _handle)) != c && res != EOF) {
|
|
_error = strerror(errno);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
size_t file::write(const void* data, size_t size, size_t n) {
|
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
|
|
|
// Check if there is a file
|
|
if (_handle == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
const size_t r = fwrite(data, size, n, _handle);
|
|
|
|
if (r != size && ferror(_handle)) {
|
|
_error = strerror(errno);
|
|
return 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
}
|