2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 18:08:16 +00:00
kea/src/lib/dns/master_lexer_inputsource.cc

221 lines
6.4 KiB
C++
Raw Normal View History

2024-03-22 15:55:27 +00:00
// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
2024-03-08 20:19:05 +02:00
#include <exceptions/isc_assert.h>
#include <dns/master_lexer_inputsource.h>
#include <dns/master_lexer.h>
#include <istream>
#include <iostream>
#include <cerrno>
#include <cstring>
namespace isc {
namespace dns {
namespace master_lexer_internal {
namespace { // unnamed namespace
std::string
createStreamName(const std::istream& input_stream) {
std::stringstream ss;
ss << "stream-" << &input_stream;
return (ss.str());
}
size_t
getStreamSize(std::istream& is) {
errno = 0; // see below
is.seekg(0, std::ios_base::end);
if (is.bad()) {
// This means the istream has an integrity error. It doesn't make
// sense to continue from this point, so we treat it as a fatal error.
isc_throw(InputSource::OpenError,
"failed to seek end of input source");
} else if (is.fail() || errno != 0) {
// This is an error specific to seekg(). There can be several
// reasons, but the most likely cause in this context is that the
// stream is associated with a special type of file such as a pipe.
// In this case, it's more likely that other main operations of
// the input source work fine, so we continue with just setting
// the stream size to "unknown".
//
// (At least some versions of) Solaris + SunStudio shows deviant
2013-01-15 14:24:43 +05:30
// behavior here: seekg() apparently calls lseek(2) internally, but
// even if it fails it doesn't set the error bits of istream. That will
// confuse the rest of this function, so, as a heuristic workaround
// we check errno and handle any non 0 value as fail().
is.clear(); // clear this error not to confuse later ops.
return (MasterLexer::SOURCE_SIZE_UNKNOWN);
}
const std::streampos len = is.tellg();
size_t ret = len;
if (len == static_cast<std::streampos>(-1)) { // cast for some compilers
if (!is.fail()) {
// tellg() returns -1 if istream::fail() would be true, but it's
// not guaranteed that it shouldn't be returned in other cases.
// In fact, with the combination of SunStudio and stlport,
2013-01-15 14:24:43 +05:30
// a stringstream created by the default constructor showed that
// behavior. We treat such cases as an unknown size.
ret = MasterLexer::SOURCE_SIZE_UNKNOWN;
} else {
isc_throw(InputSource::OpenError, "failed to get input size");
}
}
is.seekg(0, std::ios::beg);
if (is.fail()) {
isc_throw(InputSource::OpenError,
"failed to seek beginning of input source");
}
2024-03-08 20:19:05 +02:00
isc_throw_assert(len >= 0 || ret == MasterLexer::SOURCE_SIZE_UNKNOWN);
return (ret);
}
} // end of unnamed namespace
// Explicit definition of class static constant. The value is given in the
// declaration so it's not needed here.
const int InputSource::END_OF_STREAM;
InputSource::InputSource(std::istream& input_stream) :
at_eof_(false),
line_(1),
saved_line_(line_),
buffer_pos_(0),
total_pos_(0),
name_(createStreamName(input_stream)),
input_(input_stream),
2024-03-01 20:37:15 +02:00
input_size_(getStreamSize(input_)) {
}
namespace {
// A helper to initialize InputSource::input_ in the member initialization
// list.
std::istream&
openFileStream(std::ifstream& file_stream, const char* filename) {
errno = 0;
file_stream.open(filename);
if (file_stream.fail()) {
std::string error_txt("Error opening the input source file: ");
error_txt += filename;
if (errno != 0) {
error_txt += "; possible cause: ";
error_txt += std::strerror(errno);
}
isc_throw(InputSource::OpenError, error_txt);
}
return (file_stream);
}
}
InputSource::InputSource(const char* filename) :
at_eof_(false),
line_(1),
saved_line_(line_),
buffer_pos_(0),
total_pos_(0),
name_(filename),
input_(openFileStream(file_stream_, filename)),
2024-03-01 20:37:15 +02:00
input_size_(getStreamSize(input_)) {
}
2024-03-01 20:37:15 +02:00
InputSource::~InputSource() {
if (file_stream_.is_open()) {
file_stream_.close();
}
}
int
InputSource::getChar() {
if (buffer_pos_ == buffer_.size()) {
// We may have reached EOF at the last call to
// getChar(). at_eof_ will be set then. We then simply return
// early.
if (at_eof_) {
return (END_OF_STREAM);
}
// We are not yet at EOF. Read from the stream.
const int c = input_.get();
// Have we reached EOF now? If so, set at_eof_ and return early,
// but don't modify buffer_pos_ (which should still be equal to
// the size of buffer_).
if (input_.eof()) {
at_eof_ = true;
return (END_OF_STREAM);
}
// This has to come after the .eof() check as some
// implementations seem to check the eofbit also in .fail().
if (input_.fail()) {
isc_throw(MasterLexer::ReadError,
"Error reading from the input stream: " << getName());
}
buffer_.push_back(c);
}
const int c = buffer_[buffer_pos_];
++buffer_pos_;
++total_pos_;
if (c == '\n') {
++line_;
}
return (c);
}
void
InputSource::ungetChar() {
if (at_eof_) {
at_eof_ = false;
} else if (buffer_pos_ == 0) {
isc_throw(UngetBeforeBeginning,
"Cannot skip before the start of buffer");
} else {
--buffer_pos_;
--total_pos_;
if (buffer_[buffer_pos_] == '\n') {
--line_;
}
}
}
void
InputSource::ungetAll() {
2024-03-08 20:19:05 +02:00
isc_throw_assert(total_pos_ >= buffer_pos_);
total_pos_ -= buffer_pos_;
buffer_pos_ = 0;
line_ = saved_line_;
at_eof_ = false;
}
void
InputSource::saveLine() {
saved_line_ = line_;
}
void
InputSource::compact() {
if (buffer_pos_ == buffer_.size()) {
buffer_.clear();
} else {
buffer_.erase(buffer_.begin(), buffer_.begin() + buffer_pos_);
}
buffer_pos_ = 0;
}
void
InputSource::mark() {
saveLine();
compact();
}
} // namespace master_lexer_internal
} // namespace dns
} // namespace isc