Kea: master_lexer_inputsource.cc Source File

| Kea 3.0.0

master_lexer_inputsource.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <exceptions/isc_assert.h>
10#include <dns/master_lexer_inputsource.h>
11#include <dns/master_lexer.h>
12
13#include <istream>
14#include <iostream>
15#include <cerrno>
16#include <cstring>
17
18namespace isc {
19namespace dns {
20namespace master_lexer_internal {
21
22namespace { // unnamed namespace
23
24std::string
25createStreamName(const std::istream& input_stream) {
26 std::stringstream ss;
27 ss << "stream-" << &input_stream;
28 return (ss.str());
29}
30
31size_t
32getStreamSize(std::istream& is) {
33 errno = 0; // see below
34 is.seekg(0, std::ios_base::end);
35 if (is.bad()) {
36 // This means the istream has an integrity error. It doesn't make
37 // sense to continue from this point, so we treat it as a fatal error.
38 isc_throw(InputSource::OpenError,
39 "failed to seek end of input source");
40 } else if (is.fail() || errno != 0) {
41 // This is an error specific to seekg(). There can be several
42 // reasons, but the most likely cause in this context is that the
43 // stream is associated with a special type of file such as a pipe.
44 // In this case, it's more likely that other main operations of
45 // the input source work fine, so we continue with just setting
46 // the stream size to "unknown".
47 //
48 // (At least some versions of) Solaris + SunStudio shows deviant
49 // behavior here: seekg() apparently calls lseek(2) internally, but
50 // even if it fails it doesn't set the error bits of istream. That will
51 // confuse the rest of this function, so, as a heuristic workaround
52 // we check errno and handle any non 0 value as fail().
53 is.clear(); // clear this error not to confuse later ops.
54 return (MasterLexer::SOURCE_SIZE_UNKNOWN);
55 }
56 const std::streampos len = is.tellg();
57 size_t ret = len;
58 if (len == static_cast<std::streampos>(-1)) { // cast for some compilers
59 if (!is.fail()) {
60 // tellg() returns -1 if istream::fail() would be true, but it's
61 // not guaranteed that it shouldn't be returned in other cases.
62 // In fact, with the combination of SunStudio and stlport,
63 // a stringstream created by the default constructor showed that
64 // behavior. We treat such cases as an unknown size.
65 ret = MasterLexer::SOURCE_SIZE_UNKNOWN;
66 } else {
67 isc_throw(InputSource::OpenError, "failed to get input size");
68 }
69 }
70 is.seekg(0, std::ios::beg);
71 if (is.fail()) {
72 isc_throw(InputSource::OpenError,
73 "failed to seek beginning of input source");
74 }
75 isc_throw_assert(len >= 0 || ret == MasterLexer::SOURCE_SIZE_UNKNOWN);
76 return (ret);
77}
78
79} // end of unnamed namespace
80
81// Explicit definition of class static constant. The value is given in the
82// declaration so it's not needed here.
83const int InputSource::END_OF_STREAM;
84
85InputSource::InputSource(std::istream& input_stream) :
86 at_eof_(false),
87 line_(1),
88 saved_line_(line_),
89 buffer_pos_(0),
90 total_pos_(0),
91 name_(createStreamName(input_stream)),
92 input_(input_stream),
93 input_size_(getStreamSize(input_)) {
94}
95
96namespace {
97// A helper to initialize InputSource::input_ in the member initialization
98// list.
99std::istream&
100openFileStream(std::ifstream& file_stream, const char* filename) {
101 errno = 0;
102 file_stream.open(filename);
103 if (file_stream.fail()) {
104 std::string error_txt("Error opening the input source file: ");
105 error_txt += filename;
106 if (errno != 0) {
107 error_txt += "; possible cause: ";
108 error_txt += std::strerror(errno);
109 }
110 isc_throw(InputSource::OpenError, error_txt);
111 }
112
113 return (file_stream);
114}
115}
116
117 InputSource::InputSource(const char* filename) :
118 at_eof_(false),
119 line_(1),
120 saved_line_(line_),
121 buffer_pos_(0),
122 total_pos_(0),
123 name_(filename),
124 input_(openFileStream(file_stream_, filename)),
125 input_size_(getStreamSize(input_)) {
126}
127
128InputSource::~InputSource() {
129 if (file_stream_.is_open()) {
130 file_stream_.close();
131 }
132}
133
134int
135InputSource::getChar() {
136 if (buffer_pos_ == buffer_.size()) {
137 // We may have reached EOF at the last call to
138 // getChar(). at_eof_ will be set then. We then simply return
139 // early.
140 if (at_eof_) {
141 return (END_OF_STREAM);
142 }
143 // We are not yet at EOF. Read from the stream.
144 const int c = input_.get();
145 // Have we reached EOF now? If so, set at_eof_ and return early,
146 // but don't modify buffer_pos_ (which should still be equal to
147 // the size of buffer_).
148 if (input_.eof()) {
149 at_eof_ = true;
150 return (END_OF_STREAM);
151 }
152 // This has to come after the .eof() check as some
153 // implementations seem to check the eofbit also in .fail().
154 if (input_.fail()) {
155 isc_throw(MasterLexer::ReadError,
156 "Error reading from the input stream: " << getName());
157 }
158 buffer_.push_back(c);
159 }
160
161 const int c = buffer_[buffer_pos_];
162 ++buffer_pos_;
163 ++total_pos_;
164 if (c == '\n') {
165 ++line_;
166 }
167
168 return (c);
169}
170
171void
172InputSource::ungetChar() {
173 if (at_eof_) {
174 at_eof_ = false;
175 } else if (buffer_pos_ == 0) {
176 isc_throw(UngetBeforeBeginning,
177 "Cannot skip before the start of buffer");
178 } else {
179 --buffer_pos_;
180 --total_pos_;
181 if (buffer_[buffer_pos_] == '\n') {
182 --line_;
183 }
184 }
185}
186
187void
188InputSource::ungetAll() {
189 isc_throw_assert(total_pos_ >= buffer_pos_);
190 total_pos_ -= buffer_pos_;
191 buffer_pos_ = 0;
192 line_ = saved_line_;
193 at_eof_ = false;
194}
195
196void
197InputSource::saveLine() {
198 saved_line_ = line_;
199}
200
201void
202InputSource::compact() {
203 if (buffer_pos_ == buffer_.size()) {
204 buffer_.clear();
205 } else {
206 buffer_.erase(buffer_.begin(), buffer_.begin() + buffer_pos_);
207 }
208
209 buffer_pos_ = 0;
210}
211
212void
213InputSource::mark() {
214 saveLine();
215 compact();
216}
217
218} // namespace master_lexer_internal
219} // namespace dns
220} // namespace isc
isc::dns::MasterLexer::ReadError
Exception thrown when we fail to read from the input stream or file.
Definition master_lexer.h:308
isc::dns::MasterLexer::SOURCE_SIZE_UNKNOWN
static const size_t SOURCE_SIZE_UNKNOWN
Special value for input source size meaning "unknown".
Definition master_lexer.h:339
isc::dns::master_lexer_internal::InputSource::ungetAll
void ungetAll()
Forgets what was read, and skips back to the position where compact() was last called.
Definition master_lexer_inputsource.cc:188
isc::dns::master_lexer_internal::InputSource::ungetChar
void ungetChar()
Skips backward a single character in the input source.
Definition master_lexer_inputsource.cc:172
isc::dns::master_lexer_internal::InputSource::compact
void compact()
Removes buffered content before the current location in the InputSource.
Definition master_lexer_inputsource.cc:202
isc::dns::master_lexer_internal::InputSource::~InputSource
~InputSource()
Destructor.
Definition master_lexer_inputsource.cc:128
isc::dns::master_lexer_internal::InputSource::mark
void mark()
Calls saveLine() and compact() in sequence.
Definition master_lexer_inputsource.cc:213
isc::dns::master_lexer_internal::InputSource::END_OF_STREAM
static const int END_OF_STREAM
Returned by getChar() when end of stream is reached.
Definition master_lexer_inputsource.h:41
isc::dns::master_lexer_internal::InputSource::getChar
int getChar()
Returns a single character from the input source.
Definition master_lexer_inputsource.cc:135
isc::dns::master_lexer_internal::InputSource::getName
const std::string & getName() const
Returns a name for the InputSource.
Definition master_lexer_inputsource.h:78
isc::dns::master_lexer_internal::InputSource::saveLine
void saveLine()
Saves the current line being read.
Definition master_lexer_inputsource.cc:197
isc::dns::master_lexer_internal::InputSource::InputSource
InputSource(std::istream &input_stream)
Constructor which takes an input stream.
Definition master_lexer_inputsource.cc:85
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
isc_assert.h
isc_throw_assert
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition isc_assert.h:18
master_lexer.h
master_lexer_inputsource.h
isc::dns::master_lexer_internal
Definition master_lexer.h:22
isc::dns
Definition char_string.cc:26
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148
isc::dns::master_lexer_internal::InputSource::OpenError
Exception thrown when we fail to open the input file.
Definition master_lexer_inputsource.h:52
isc::dns::master_lexer_internal::InputSource::UngetBeforeBeginning
Exception thrown when ungetChar() is made to go before the start of buffer.
Definition master_lexer_inputsource.h:45