qpdf/libqpdf/FileInputSource.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

143 lines
3.5 KiB
C++
Raw Normal View History

#include <qpdf/FileInputSource.hh>
#include <qpdf/QPDFExc.hh>
#include <qpdf/QUtil.hh>
#include <algorithm>
2023-05-20 11:22:32 +00:00
#include <cstring>
FileInputSource::FileInputSource() :
2022-08-25 11:42:14 +00:00
close_file(false),
file(nullptr)
{
}
2022-05-04 16:02:39 +00:00
FileInputSource::FileInputSource(char const* filename) :
2022-08-25 11:42:14 +00:00
close_file(true),
filename(filename),
file(QUtil::safe_fopen(filename, "rb"))
2022-05-04 16:02:39 +00:00
{
}
FileInputSource::FileInputSource(char const* description, FILE* filep, bool close_file) :
2022-08-25 11:42:14 +00:00
close_file(close_file),
filename(description),
file(filep)
{
}
FileInputSource::~FileInputSource()
2022-05-04 16:02:39 +00:00
{
2022-08-25 11:42:14 +00:00
// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
if (this->file && this->close_file) {
fclose(this->file);
}
2022-05-04 16:02:39 +00:00
}
void
FileInputSource::setFilename(char const* filename)
{
2022-08-25 11:42:14 +00:00
this->close_file = true;
this->filename = filename;
this->file = QUtil::safe_fopen(filename, "rb");
}
void
FileInputSource::setFile(char const* description, FILE* filep, bool close_file)
{
2022-08-25 11:42:14 +00:00
this->filename = description;
this->file = filep;
this->seek(0, SEEK_SET);
}
qpdf_offset_t
FileInputSource::findAndSkipNextEOL()
{
qpdf_offset_t result = 0;
bool done = false;
char buf[10240];
while (!done) {
2022-08-25 11:42:14 +00:00
qpdf_offset_t cur_offset = QUtil::tell(this->file);
size_t len = this->read(buf, sizeof(buf));
if (len == 0) {
done = true;
result = this->tell();
} else {
char* p1 = static_cast<char*>(memchr(buf, '\r', len));
char* p2 = static_cast<char*>(memchr(buf, '\n', len));
char* p = (p1 && p2) ? std::min(p1, p2) : p1 ? p1 : p2;
if (p) {
result = cur_offset + (p - buf);
// We found \r or \n. Keep reading until we get past \r and \n characters.
this->seek(result + 1, SEEK_SET);
char ch;
while (!done) {
if (this->read(&ch, 1) == 0) {
done = true;
} else if (!((ch == '\r') || (ch == '\n'))) {
this->unreadCh(ch);
done = true;
}
}
}
}
}
return result;
}
std::string const&
FileInputSource::getName() const
{
2022-08-25 11:42:14 +00:00
return this->filename;
}
qpdf_offset_t
FileInputSource::tell()
{
2022-08-25 11:42:14 +00:00
return QUtil::tell(this->file);
}
void
FileInputSource::seek(qpdf_offset_t offset, int whence)
{
if (QUtil::seek(this->file, offset, whence) == -1) {
QUtil::throw_system_error(
std::string("seek to ") + this->filename + ", offset " + std::to_string(offset) + " (" +
std::to_string(whence) + ")");
}
}
void
FileInputSource::rewind()
{
2022-08-25 11:42:14 +00:00
::rewind(this->file);
}
size_t
FileInputSource::read(char* buffer, size_t length)
{
this->last_offset = QUtil::tell(this->file);
2022-08-25 11:42:14 +00:00
size_t len = fread(buffer, 1, length, this->file);
if (len == 0) {
2022-08-25 11:42:14 +00:00
if (ferror(this->file)) {
throw QPDFExc(
qpdf_e_system,
2022-08-25 11:42:14 +00:00
this->filename,
"",
this->last_offset,
(std::string("read ") + std::to_string(length) + " bytes"));
} else if (length > 0) {
this->seek(0, SEEK_END);
this->last_offset = this->tell();
}
}
return len;
}
void
FileInputSource::unreadCh(char ch)
{
if (ungetc(static_cast<unsigned char>(ch), this->file) == -1) {
QUtil::throw_system_error(this->filename + ": unread character");
}
}