master_lexer_inputsource.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dns/master_lexer_inputsource.h>
  15. #include <dns/master_lexer.h>
  16. #include <istream>
  17. #include <iostream>
  18. #include <cassert>
  19. #include <cerrno>
  20. #include <cstring>
  21. namespace isc {
  22. namespace dns {
  23. namespace master_lexer_internal {
  24. namespace { // unnamed namespace
  25. std::string
  26. createStreamName(const std::istream& input_stream) {
  27. std::stringstream ss;
  28. ss << "stream-" << &input_stream;
  29. return (ss.str());
  30. }
  31. size_t
  32. getStreamSize(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. assert(len >= 0 || ret == MasterLexer::SOURCE_SIZE_UNKNOWN);
  76. return (ret);
  77. }
  78. } // end of unnamed namespace
  79. // Explicit definition of class static constant. The value is given in the
  80. // declaration so it's not needed here.
  81. const int InputSource::END_OF_STREAM;
  82. InputSource::InputSource(std::istream& input_stream) :
  83. at_eof_(false),
  84. line_(1),
  85. saved_line_(line_),
  86. buffer_pos_(0),
  87. total_pos_(0),
  88. name_(createStreamName(input_stream)),
  89. input_(input_stream),
  90. input_size_(getStreamSize(input_))
  91. {}
  92. namespace {
  93. // A helper to initialize InputSource::input_ in the member initialization
  94. // list.
  95. std::istream&
  96. openFileStream(std::ifstream& file_stream, const char* filename) {
  97. errno = 0;
  98. file_stream.open(filename);
  99. if (file_stream.fail()) {
  100. std::string error_txt("Error opening the input source file: ");
  101. error_txt += filename;
  102. if (errno != 0) {
  103. error_txt += "; possible cause: ";
  104. error_txt += std::strerror(errno);
  105. }
  106. isc_throw(InputSource::OpenError, error_txt);
  107. }
  108. return (file_stream);
  109. }
  110. }
  111. InputSource::InputSource(const char* filename) :
  112. at_eof_(false),
  113. line_(1),
  114. saved_line_(line_),
  115. buffer_pos_(0),
  116. total_pos_(0),
  117. name_(filename),
  118. input_(openFileStream(file_stream_, filename)),
  119. input_size_(getStreamSize(input_))
  120. {}
  121. InputSource::~InputSource()
  122. {
  123. if (file_stream_.is_open()) {
  124. file_stream_.close();
  125. }
  126. }
  127. int
  128. InputSource::getChar() {
  129. if (buffer_pos_ == buffer_.size()) {
  130. // We may have reached EOF at the last call to
  131. // getChar(). at_eof_ will be set then. We then simply return
  132. // early.
  133. if (at_eof_) {
  134. return (END_OF_STREAM);
  135. }
  136. // We are not yet at EOF. Read from the stream.
  137. const int c = input_.get();
  138. // Have we reached EOF now? If so, set at_eof_ and return early,
  139. // but don't modify buffer_pos_ (which should still be equal to
  140. // the size of buffer_).
  141. if (input_.eof()) {
  142. at_eof_ = true;
  143. return (END_OF_STREAM);
  144. }
  145. // This has to come after the .eof() check as some
  146. // implementations seem to check the eofbit also in .fail().
  147. if (input_.fail()) {
  148. isc_throw(MasterLexer::ReadError,
  149. "Error reading from the input stream: " << getName());
  150. }
  151. buffer_.push_back(c);
  152. }
  153. const int c = buffer_[buffer_pos_];
  154. ++buffer_pos_;
  155. ++total_pos_;
  156. if (c == '\n') {
  157. ++line_;
  158. }
  159. return (c);
  160. }
  161. void
  162. InputSource::ungetChar() {
  163. if (at_eof_) {
  164. at_eof_ = false;
  165. } else if (buffer_pos_ == 0) {
  166. isc_throw(UngetBeforeBeginning,
  167. "Cannot skip before the start of buffer");
  168. } else {
  169. --buffer_pos_;
  170. --total_pos_;
  171. if (buffer_[buffer_pos_] == '\n') {
  172. --line_;
  173. }
  174. }
  175. }
  176. void
  177. InputSource::ungetAll() {
  178. assert(total_pos_ >= buffer_pos_);
  179. total_pos_ -= buffer_pos_;
  180. buffer_pos_ = 0;
  181. line_ = saved_line_;
  182. at_eof_ = false;
  183. }
  184. void
  185. InputSource::saveLine() {
  186. saved_line_ = line_;
  187. }
  188. void
  189. InputSource::compact() {
  190. if (buffer_pos_ == buffer_.size()) {
  191. buffer_.clear();
  192. } else {
  193. buffer_.erase(buffer_.begin(), buffer_.begin() + buffer_pos_);
  194. }
  195. buffer_pos_ = 0;
  196. }
  197. void
  198. InputSource::mark() {
  199. saveLine();
  200. compact();
  201. }
  202. } // namespace master_lexer_internal
  203. } // namespace dns
  204. } // namespace isc