1// 2// Automated Testing Framework (atf) 3// 4// Copyright (c) 2007, 2008, 2010 The NetBSD Foundation, Inc. 5// All rights reserved. 6// 7// Redistribution and use in source and binary forms, with or without 8// modification, are permitted provided that the following conditions 9// are met: 10// 1. Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// 2. Redistributions in binary form must reproduce the above copyright 13// notice, this list of conditions and the following disclaimer in the 14// documentation and/or other materials provided with the distribution. 15// 16// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 17// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 21// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28// 29 30#include <sstream> 31 32#include "parser.hpp" 33#include "sanity.hpp" 34#include "text.hpp" 35 36namespace impl = atf::parser; 37#define IMPL_NAME "atf::parser" 38 39// ------------------------------------------------------------------------ 40// The "parse_error" class. 41// ------------------------------------------------------------------------ 42 43impl::parse_error::parse_error(size_t line, std::string msg) : 44 std::runtime_error(msg), 45 std::pair< size_t, std::string >(line, msg) 46{ 47} 48 49impl::parse_error::~parse_error(void) 50 throw() 51{ 52} 53 54const char* 55impl::parse_error::what(void) 56 const throw() 57{ 58 try { 59 std::ostringstream oss; 60 oss << "LONELY PARSE ERROR: " << first << ": " << second; 61 m_msg = oss.str(); 62 return m_msg.c_str(); 63 } catch (...) { 64 return "Could not format message for parsing error."; 65 } 66} 67 68impl::parse_error::operator std::string(void) 69 const 70{ 71 return atf::text::to_string(first) + ": " + second; 72} 73 74// ------------------------------------------------------------------------ 75// The "parse_errors" class. 76// ------------------------------------------------------------------------ 77 78impl::parse_errors::parse_errors(void) : 79 std::runtime_error("No parsing errors yet") 80{ 81 m_msg.clear(); 82} 83 84impl::parse_errors::~parse_errors(void) 85 throw() 86{ 87} 88 89const char* 90impl::parse_errors::what(void) 91 const throw() 92{ 93 try { 94 m_msg = atf::text::join(*this, "\n"); 95 return m_msg.c_str(); 96 } catch (...) { 97 return "Could not format messages for parsing errors."; 98 } 99} 100 101// ------------------------------------------------------------------------ 102// The "format_error" class. 103// ------------------------------------------------------------------------ 104 105impl::format_error::format_error(const std::string& w) : 106 std::runtime_error(w.c_str()) 107{ 108} 109 110// ------------------------------------------------------------------------ 111// The "token" class. 112// ------------------------------------------------------------------------ 113 114impl::token::token(void) : 115 m_inited(false) 116{ 117} 118 119impl::token::token(size_t p_line, 120 const token_type& p_type, 121 const std::string& p_text) : 122 m_inited(true), 123 m_line(p_line), 124 m_type(p_type), 125 m_text(p_text) 126{ 127} 128 129size_t 130impl::token::lineno(void) 131 const 132{ 133 return m_line; 134} 135 136const impl::token_type& 137impl::token::type(void) 138 const 139{ 140 return m_type; 141} 142 143const std::string& 144impl::token::text(void) 145 const 146{ 147 return m_text; 148} 149 150impl::token::operator bool(void) 151 const 152{ 153 return m_inited; 154} 155 156bool 157impl::token::operator!(void) 158 const 159{ 160 return !m_inited; 161} 162 163// ------------------------------------------------------------------------ 164// The "header_entry" class. 165// ------------------------------------------------------------------------ 166 167impl::header_entry::header_entry(void) 168{ 169} 170 171impl::header_entry::header_entry(const std::string& n, const std::string& v, 172 attrs_map as) : 173 m_name(n), 174 m_value(v), 175 m_attrs(as) 176{ 177} 178 179const std::string& 180impl::header_entry::name(void) const 181{ 182 return m_name; 183} 184 185const std::string& 186impl::header_entry::value(void) const 187{ 188 return m_value; 189} 190 191const impl::attrs_map& 192impl::header_entry::attrs(void) const 193{ 194 return m_attrs; 195} 196 197bool 198impl::header_entry::has_attr(const std::string& n) const 199{ 200 return m_attrs.find(n) != m_attrs.end(); 201} 202 203const std::string& 204impl::header_entry::get_attr(const std::string& n) const 205{ 206 attrs_map::const_iterator iter = m_attrs.find(n); 207 PRE(iter != m_attrs.end()); 208 return (*iter).second; 209} 210 211// ------------------------------------------------------------------------ 212// The header tokenizer. 213// ------------------------------------------------------------------------ 214 215namespace header { 216 217static const impl::token_type eof_type = 0; 218static const impl::token_type nl_type = 1; 219static const impl::token_type text_type = 2; 220static const impl::token_type colon_type = 3; 221static const impl::token_type semicolon_type = 4; 222static const impl::token_type dblquote_type = 5; 223static const impl::token_type equal_type = 6; 224 225class tokenizer : public impl::tokenizer< std::istream > { 226public: 227 tokenizer(std::istream& is, size_t curline) : 228 impl::tokenizer< std::istream > 229 (is, true, eof_type, nl_type, text_type, curline) 230 { 231 add_delim(';', semicolon_type); 232 add_delim(':', colon_type); 233 add_delim('=', equal_type); 234 add_quote('"', dblquote_type); 235 } 236}; 237 238static 239impl::parser< header::tokenizer >& 240read(impl::parser< header::tokenizer >& p, impl::header_entry& he) 241{ 242 using namespace header; 243 244 impl::token t = p.expect(text_type, nl_type, "a header name"); 245 if (t.type() == nl_type) { 246 he = impl::header_entry(); 247 return p; 248 } 249 std::string hdr_name = t.text(); 250 251 t = p.expect(colon_type, "`:'"); 252 253 t = p.expect(text_type, "a textual value"); 254 std::string hdr_value = t.text(); 255 256 impl::attrs_map attrs; 257 258 for (;;) { 259 t = p.expect(eof_type, semicolon_type, nl_type, 260 "eof, `;' or new line"); 261 if (t.type() == eof_type || t.type() == nl_type) 262 break; 263 264 t = p.expect(text_type, "an attribute name"); 265 std::string attr_name = t.text(); 266 267 t = p.expect(equal_type, "`='"); 268 269 t = p.expect(text_type, "word or quoted string"); 270 std::string attr_value = t.text(); 271 attrs[attr_name] = attr_value; 272 } 273 274 he = impl::header_entry(hdr_name, hdr_value, attrs); 275 276 return p; 277} 278 279static 280std::ostream& 281write(std::ostream& os, const impl::header_entry& he) 282{ 283 std::string line = he.name() + ": " + he.value(); 284 impl::attrs_map as = he.attrs(); 285 for (impl::attrs_map::const_iterator iter = as.begin(); iter != as.end(); 286 iter++) { 287 PRE((*iter).second.find('\"') == std::string::npos); 288 line += "; " + (*iter).first + "=\"" + (*iter).second + "\""; 289 } 290 291 os << line << "\n"; 292 293 return os; 294} 295 296} // namespace header 297 298// ------------------------------------------------------------------------ 299// Free functions. 300// ------------------------------------------------------------------------ 301 302std::pair< size_t, impl::headers_map > 303impl::read_headers(std::istream& is, size_t curline) 304{ 305 using impl::format_error; 306 307 headers_map hm; 308 309 // 310 // Grammar 311 // 312 // header = entry+ nl 313 // entry = line nl 314 // line = text colon text 315 // (semicolon (text equal (text | dblquote string dblquote)))* 316 // string = quoted_string 317 // 318 319 header::tokenizer tkz(is, curline); 320 impl::parser< header::tokenizer > p(tkz); 321 322 bool first = true; 323 for (;;) { 324 try { 325 header_entry he; 326 if (!header::read(p, he).good() || he.name().empty()) 327 break; 328 329 if (first && he.name() != "Content-Type") 330 throw format_error("Could not determine content type"); 331 else 332 first = false; 333 334 hm[he.name()] = he; 335 } catch (const impl::parse_error& pe) { 336 p.add_error(pe); 337 p.reset(header::nl_type); 338 } 339 } 340 341 if (!is.good()) 342 throw format_error("Unexpected end of stream"); 343 344 return std::pair< size_t, headers_map >(tkz.lineno(), hm); 345} 346 347void 348impl::write_headers(const impl::headers_map& hm, std::ostream& os) 349{ 350 PRE(!hm.empty()); 351 headers_map::const_iterator ct = hm.find("Content-Type"); 352 PRE(ct != hm.end()); 353 header::write(os, (*ct).second); 354 for (headers_map::const_iterator iter = hm.begin(); iter != hm.end(); 355 iter++) { 356 if ((*iter).first != "Content-Type") 357 header::write(os, (*iter).second); 358 } 359 os << "\n"; 360} 361 362void 363impl::validate_content_type(const impl::headers_map& hm, const std::string& fmt, 364 int version) 365{ 366 using impl::format_error; 367 368 headers_map::const_iterator iter = hm.find("Content-Type"); 369 if (iter == hm.end()) 370 throw format_error("Could not determine content type"); 371 372 const header_entry& he = (*iter).second; 373 if (he.value() != fmt) 374 throw format_error("Mismatched content type: expected `" + fmt + 375 "' but got `" + he.value() + "'"); 376 377 if (!he.has_attr("version")) 378 throw format_error("Could not determine version"); 379 const std::string& vstr = atf::text::to_string(version); 380 if (he.get_attr("version") != vstr) 381 throw format_error("Mismatched version: expected `" + 382 vstr + "' but got `" + 383 he.get_attr("version") + "'"); 384} 385