summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/master_lexer_inputsource.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/master_lexer_inputsource.cc')
-rw-r--r--src/lib/dns/master_lexer_inputsource.cc221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/lib/dns/master_lexer_inputsource.cc b/src/lib/dns/master_lexer_inputsource.cc
new file mode 100644
index 0000000..841fc6c
--- /dev/null
+++ b/src/lib/dns/master_lexer_inputsource.cc
@@ -0,0 +1,221 @@
+// Copyright (C) 2012-2015 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>
+
+#include <dns/master_lexer_inputsource.h>
+#include <dns/master_lexer.h>
+
+#include <istream>
+#include <iostream>
+#include <cassert>
+#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
+ // 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,
+ // 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");
+ }
+ 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),
+ 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)),
+ input_size_(getStreamSize(input_))
+{}
+
+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() {
+ 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