388 lines
7.6 KiB
C++
388 lines
7.6 KiB
C++
#include "File.h"
|
|
|
|
//#include "FileLogger.h"
|
|
#include "ByteUtils.h"
|
|
#include "Directory.h"
|
|
#include "Result.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
class FileImpl
|
|
{
|
|
public:
|
|
Status do_open(const FileSystemPath& path, File::AccessMode accessMode)
|
|
{
|
|
int flags{0};
|
|
if (accessMode == File::AccessMode::Read)
|
|
{
|
|
flags |= O_RDONLY;
|
|
}
|
|
else
|
|
{
|
|
flags |= O_WRONLY;
|
|
flags |= O_CREAT;
|
|
}
|
|
errno = 0;
|
|
m_fd = ::open(path.str().raw(), flags);
|
|
if (m_fd < 0)
|
|
{
|
|
Status ret;
|
|
ret.on_errno("Failed to open file with");
|
|
return ret;
|
|
}
|
|
|
|
if (accessMode == File::AccessMode::Read)
|
|
{
|
|
m_open_for_read = true;
|
|
}
|
|
else
|
|
{
|
|
m_open_for_write = true;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Status do_close()
|
|
{
|
|
errno = 0;
|
|
const auto rc = ::close(m_fd);
|
|
if (rc < 0)
|
|
{
|
|
Status ret;
|
|
ret.on_errno("Failed to close file with");
|
|
return ret;
|
|
}
|
|
m_open_for_read = false;
|
|
m_open_for_write = false;
|
|
return {};
|
|
}
|
|
|
|
bool is_ok() const
|
|
{
|
|
return m_valid;
|
|
}
|
|
|
|
bool is_open_for_read() const
|
|
{
|
|
return m_open_for_read;
|
|
}
|
|
|
|
bool is_open_for_write() const
|
|
{
|
|
return m_open_for_write;
|
|
}
|
|
|
|
Result<size_t> do_read(VecBytes& bytes)
|
|
{
|
|
errno = 0;
|
|
const auto rc = ::read(m_fd, bytes.data(), bytes.capacity());
|
|
if (rc < 0)
|
|
{
|
|
const auto msg = _s("Error in read impl | ") + Error::from_errno();
|
|
return Result<size_t>(Error(msg));
|
|
}
|
|
return Result<size_t>(rc);
|
|
}
|
|
|
|
Result<size_t> do_write(const VecBytes& bytes)
|
|
{
|
|
errno = 0;
|
|
const auto rc = ::write(m_fd, bytes.data(), bytes.size());
|
|
if (rc < 0)
|
|
{
|
|
const auto msg = _s("Error in write impl | ") + Error::from_errno();
|
|
return Result<size_t>(Error(msg));
|
|
}
|
|
return Result<size_t>(rc);
|
|
}
|
|
|
|
Result<size_t> do_write(const Vector<char>& bytes, int size = -1)
|
|
{
|
|
errno = 0;
|
|
int rc = 0;
|
|
if (size > -1)
|
|
{
|
|
rc = ::write(m_fd, bytes.data(), size);
|
|
}
|
|
else
|
|
{
|
|
rc = ::write(m_fd, bytes.data(), bytes.size());
|
|
}
|
|
if (rc < 0)
|
|
{
|
|
const auto msg = _s("Error in write impl | ") + Error::from_errno();
|
|
return Result<size_t>(Error(msg));
|
|
}
|
|
return Result<size_t>(rc);
|
|
}
|
|
|
|
Status update_size()
|
|
{
|
|
struct stat buf;
|
|
const auto rc = ::fstat(m_fd, &buf);
|
|
if (rc != 0)
|
|
{
|
|
Status ret;
|
|
ret.on_errno("Failed to get size with fstat");
|
|
return ret;
|
|
}
|
|
m_size = buf.st_size;
|
|
return {};
|
|
}
|
|
|
|
bool m_open_for_write{false};
|
|
bool m_open_for_read{false};
|
|
bool m_valid{false};
|
|
size_t m_size{0};
|
|
bool m_is_open{false};
|
|
int m_fd{-1};
|
|
};
|
|
|
|
File::File(const FileSystemPath& path)
|
|
: m_impl(Ptr<FileImpl>::create()),
|
|
m_path(path)
|
|
{
|
|
}
|
|
|
|
File::~File()
|
|
{
|
|
close();
|
|
}
|
|
|
|
String File::getExtension() const
|
|
{
|
|
return m_path.extension();
|
|
}
|
|
|
|
Status File::readBinary(VecBytes& buffer)
|
|
{
|
|
auto status = open(AccessMode::Read, true);
|
|
if (!status.ok())
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = m_impl->update_size();
|
|
if (!status.ok())
|
|
{
|
|
return status;
|
|
}
|
|
|
|
buffer.resize(m_impl->m_size);
|
|
const auto result = m_impl->do_read(buffer);
|
|
if (!result.ok())
|
|
{
|
|
return Status(result.error());
|
|
}
|
|
buffer.resize(result.value());
|
|
return {};
|
|
}
|
|
|
|
Status File::readText(String& ret)
|
|
{
|
|
auto status = open(AccessMode::Read, true);
|
|
if (!status.ok())
|
|
{
|
|
return status;
|
|
}
|
|
|
|
VecBytes buffer;
|
|
status = m_impl->update_size();
|
|
if (status.ok() && m_impl->m_size > 0)
|
|
{
|
|
buffer.resize(m_impl->m_size);
|
|
const auto result = m_impl->do_read(buffer);
|
|
if (!result.ok())
|
|
{
|
|
return Status(result.error());
|
|
}
|
|
buffer.resize(result.value());
|
|
ret.append(buffer);
|
|
}
|
|
else
|
|
{
|
|
buffer.resize(1024);
|
|
while(true)
|
|
{
|
|
const auto result = m_impl->do_read(buffer);
|
|
if (!result.ok())
|
|
{
|
|
return Status(result.error());
|
|
}
|
|
if (result.value() < 1024)
|
|
{
|
|
if (result.value() > 0)
|
|
{
|
|
buffer.resize(result.value());
|
|
ret.append(buffer);
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ret.append(buffer);
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
String File::dumpBinary()
|
|
{
|
|
open(AccessMode::Read);
|
|
|
|
String sstr;
|
|
sstr << "Count | Binary | Decimal | Hex | ASCII \n";
|
|
|
|
VecBytes buffer;
|
|
readBinary(buffer);
|
|
|
|
unsigned count = 0;
|
|
/*
|
|
for(const auto byte : buffer)
|
|
{
|
|
const auto ascii_val = std::isalpha(byte) ? byte : '.';
|
|
String hex_sstr;
|
|
hex_sstr << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(byte);
|
|
|
|
sstr << count << " | " << ByteUtils::toString(byte) << " | " << static_cast<int>(byte) << " | " << hex_sstr.str() << " | " << ascii_val << '\n';
|
|
if (count % 10 == 0)
|
|
{
|
|
sstr << "\n";
|
|
}
|
|
count++;
|
|
}
|
|
*/
|
|
close();
|
|
return sstr;
|
|
}
|
|
|
|
Optional<Byte> File::readNextByte()
|
|
{
|
|
if (!open(AccessMode::Read).ok())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (m_impl->is_ok())
|
|
{
|
|
VecBytes buffer(1);
|
|
m_impl->do_read(buffer);
|
|
if (buffer[0] == EOF)
|
|
{
|
|
return {};
|
|
}
|
|
else
|
|
{
|
|
return buffer[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
Status File::open(AccessMode accessMode, bool binary)
|
|
{
|
|
if (m_path.is_absolute() && !m_path.parent_path().exists())
|
|
{
|
|
Directory::create(m_path.parent_path(), true);
|
|
}
|
|
|
|
return m_impl->do_open(m_path, accessMode);
|
|
}
|
|
|
|
Status File::close()
|
|
{
|
|
return m_impl->do_close();
|
|
}
|
|
|
|
FileFormat::Format File::inferFormat() const
|
|
{
|
|
//const auto extension = getExtension();
|
|
//return FileFormat::inferFormat(extension);
|
|
return {};
|
|
}
|
|
|
|
Status File::writeText(const String& text)
|
|
{
|
|
bool had_to_open{false};
|
|
Status status;
|
|
if (!m_impl->is_open_for_write())
|
|
{
|
|
had_to_open = true;
|
|
status = m_impl->do_open(m_path, File::AccessMode::Write);
|
|
if (!status.ok())
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
const auto result = m_impl->do_write(text.data(), text.data().size() - 1);
|
|
if (!result.ok())
|
|
{
|
|
return Status(result.error());
|
|
}
|
|
|
|
if (had_to_open)
|
|
{
|
|
status = m_impl->do_close();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
Status File::readLines(Vector<String>& lines)
|
|
{
|
|
/*
|
|
if (!pathExists())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if(!open(AccessMode::Read))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
String str;
|
|
while(std::getline(*mInHandle, str))
|
|
{
|
|
content.push_back(str);
|
|
}
|
|
|
|
close();
|
|
*/
|
|
return {};
|
|
}
|
|
|
|
Status File::read(String& ret)
|
|
{
|
|
/*
|
|
if (!pathExists())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if(!open(AccessMode::Read))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
String buffer;
|
|
buffer << mInHandle->rdbuf();
|
|
|
|
close();
|
|
*/
|
|
return {};
|
|
}
|
|
|
|
bool File::exists() const
|
|
{
|
|
return m_path.exists();
|
|
}
|