// // PageReader.cpp // // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "PageReader.h" #include "Page.h" #include "Poco/FileStream.h" #include "Poco/CountingStream.h" #include "Poco/Path.h" #include "Poco/Exception.h" #include "Poco/Ascii.h" const std::string PageReader::MARKUP_BEGIN("\tresponseStream << \""); const std::string PageReader::MARKUP_END("\";\n"); const std::string PageReader::EXPR_BEGIN("\tresponseStream << ("); const std::string PageReader::EXPR_END(");\n"); PageReader::PageReader(Page& page, const std::string& path): _page(page), _pParent(0), _path(path), _line(0), _emitLineDirectives(false) { _attrs.reserve(4096); } PageReader::PageReader(const PageReader& parent, const std::string& path): _page(parent._page), _pParent(&parent), _path(path), _line(0), _emitLineDirectives(false) { _attrs.reserve(4096); } PageReader::~PageReader() { } void PageReader::emitLineDirectives(bool flag) { _emitLineDirectives = flag; } void PageReader::parse(std::istream& pageStream) { ParsingState state = STATE_MARKUP; _page.handler() << MARKUP_BEGIN; Poco::CountingInputStream countingPageStream(pageStream); std::string token; nextToken(countingPageStream, token); while (!token.empty()) { _line = countingPageStream.getCurrentLineNumber(); if (token == "<%") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; generateLineDirective(_page.handler()); state = STATE_BLOCK; } else _page.handler() << token; } else if (token == "<%%") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; generateLineDirective(_page.preHandler()); state = STATE_PREHANDLER; } else _page.handler() << token; } else if (token == "<%!") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; generateLineDirective(_page.implDecls()); state = STATE_IMPLDECL; } else _page.handler() << token; } else if (token == "<%!!") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; generateLineDirective(_page.headerDecls()); state = STATE_HDRDECL; } else _page.handler() << token; } else if (token == "<%--") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; state = STATE_COMMENT; } else _page.handler() << token; } else if (token == "<%@") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; state = STATE_ATTR; _attrs.clear(); } else _page.handler() << token; } else if (token == "<%=") { if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; generateLineDirective(_page.handler()); _page.handler() << EXPR_BEGIN; state = STATE_EXPR; } else _page.handler() << token; } else if (token == "%>") { if (state == STATE_EXPR) { _page.handler() << EXPR_END; _page.handler() << MARKUP_BEGIN; state = STATE_MARKUP; } else if (state == STATE_ATTR) { parseAttributes(); _attrs.clear(); _page.handler() << MARKUP_BEGIN; state = STATE_MARKUP; } else if (state != STATE_MARKUP) { _page.handler() << MARKUP_BEGIN; state = STATE_MARKUP; } else _page.handler() << token; } else { switch (state) { case STATE_MARKUP: if (token == "\n") { _page.handler() << "\\n"; _page.handler() << MARKUP_END; _page.handler() << MARKUP_BEGIN; } else if (token == "\t") { _page.handler() << "\\t"; } else if (token == "\"") { _page.handler() << "\\\""; } else if (token == "\\") { _page.handler() << "\\\\"; } else if (token != "\r") { _page.handler() << token; } break; case STATE_IMPLDECL: _page.implDecls() << token; break; case STATE_HDRDECL: _page.headerDecls() << token; break; case STATE_PREHANDLER: _page.preHandler() << token; break; case STATE_BLOCK: _page.handler() << token; break; case STATE_EXPR: _page.handler() << token; break; case STATE_COMMENT: break; case STATE_ATTR: _attrs += token; break; } } nextToken(countingPageStream, token); } if (state == STATE_MARKUP) { _page.handler() << MARKUP_END; } else throw Poco::SyntaxException("unclosed meta or code block", where()); } void PageReader::parseAttributes() { static const int eof = std::char_traits::eof(); std::string basename; std::istringstream istr(_attrs); int ch = istr.get(); while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get(); while (ch != eof && Poco::Ascii::isAlphaNumeric(ch)) { basename += (char) ch; ch = istr.get(); } while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get(); while (ch != eof) { std::string name(basename + "."); std::string value; while (ch != eof && Poco::Ascii::isAlphaNumeric(ch)) { name += (char) ch; ch = istr.get(); } while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get(); if (ch != '=') throw Poco::SyntaxException("bad attribute syntax: '=' expected", where()); ch = istr.get(); while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get(); if (ch == '"') { ch = istr.get(); while (ch != eof && ch != '"') { value += (char) ch; ch = istr.get(); } if (ch != '"') throw Poco::SyntaxException("bad attribute syntax: '\"' expected", where()); } else if (ch == '\'') { ch = istr.get(); while (ch != eof && ch != '\'') { value += (char) ch; ch = istr.get(); } if (ch != '\'') throw Poco::SyntaxException("bad attribute syntax: ''' expected", where()); } else throw Poco::SyntaxException("bad attribute syntax: '\"' or ''' expected", where()); ch = istr.get(); handleAttribute(name, value); while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get(); } } void PageReader::nextToken(std::istream& istr, std::string& token) { token.clear(); int ch = istr.get(); if (ch != -1) { if (ch == '<' && istr.peek() == '%') { token += "<%"; istr.get(); ch = istr.peek(); switch (ch) { case '%': case '@': case '=': ch = istr.get(); token += (char) ch; break; case '!': ch = istr.get(); token += (char) ch; if (istr.peek() == '!') { ch = istr.get(); token += (char) ch; } break; case '-': ch = istr.get(); token += (char) ch; if (istr.peek() == '-') { ch = istr.get(); token += (char) ch; } break; } } else if (ch == '%' && istr.peek() == '>') { token += "%>"; istr.get(); } else token += (char) ch; } } void PageReader::handleAttribute(const std::string& name, const std::string& value) { if (name == "include.page" || name == "include.file") { include(value); } else if (name == "header.include") { _page.headerDecls() << "#include \"" << value << "\"\n"; } else if (name == "header.sinclude") { _page.headerDecls() << "#include <" << value << ">\n"; } else if (name == "impl.include") { _page.implDecls() << "#include \"" << value << "\"\n"; } else if (name == "impl.sinclude") { _page.implDecls() << "#include <" << value << ">\n"; } else { _page.set(name, value); } } void PageReader::include(const std::string& path) { Poco::Path currentPath(_path); Poco::Path includePath(path); currentPath.resolve(includePath); _page.handler() << "\t// begin include " << currentPath.toString() << "\n"; Poco::FileInputStream includeStream(currentPath.toString()); PageReader includeReader(*this, currentPath.toString()); includeReader.emitLineDirectives(_emitLineDirectives); includeReader.parse(includeStream); _page.handler() << "\t// end include " << currentPath.toString() << "\n"; } std::string PageReader::where() const { std::stringstream result; result << "in file '" << _path << "', line " << _line; const PageReader* pParent = _pParent; while (pParent) { result << "\n\tincluded from file '"<< pParent->_path << "', line " << pParent->_line; pParent = pParent->_pParent; } return result.str(); } void PageReader::generateLineDirective(std::ostream& ostr) { if (_emitLineDirectives) { Poco::Path p(_path); p.makeAbsolute(); std::string absPath = p.toString(); ostr << "#line " << _line << " \""; for (std::string::const_iterator it = absPath.begin(); it != absPath.end(); ++it) { if (*it == '\\') ostr << "\\\\"; else ostr << *it; } ostr << "\"\n"; } }