1//
2// PageReader.cpp
3//
4// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
5// and Contributors.
6//
7// SPDX-License-Identifier: BSL-1.0
8//
9
10
11#include "PageReader.h"
12#include "Page.h"
13#include "Poco/FileStream.h"
14#include "Poco/CountingStream.h"
15#include "Poco/Path.h"
16#include "Poco/Exception.h"
17#include "Poco/Ascii.h"
18
19
20const std::string PageReader::MARKUP_BEGIN("\tresponseStream << \"");
21const std::string PageReader::MARKUP_END("\";\n");
22const std::string PageReader::EXPR_BEGIN("\tresponseStream << (");
23const std::string PageReader::EXPR_END(");\n");
24
25
26PageReader::PageReader(Page& page, const std::string& path):
27 _page(page),
28 _pParent(0),
29 _path(path),
30 _line(0),
31 _emitLineDirectives(false)
32{
33 _attrs.reserve(4096);
34}
35
36
37PageReader::PageReader(const PageReader& parent, const std::string& path):
38 _page(parent._page),
39 _pParent(&parent),
40 _path(path),
41 _line(0),
42 _emitLineDirectives(false)
43{
44 _attrs.reserve(4096);
45}
46
47
48PageReader::~PageReader()
49{
50}
51
52
53void PageReader::emitLineDirectives(bool flag)
54{
55 _emitLineDirectives = flag;
56}
57
58
59void PageReader::parse(std::istream& pageStream)
60{
61 ParsingState state = STATE_MARKUP;
62
63 _page.handler() << MARKUP_BEGIN;
64
65 Poco::CountingInputStream countingPageStream(pageStream);
66 std::string token;
67 nextToken(countingPageStream, token);
68 while (!token.empty())
69 {
70 _line = countingPageStream.getCurrentLineNumber();
71 if (token == "<%")
72 {
73 if (state == STATE_MARKUP)
74 {
75 _page.handler() << MARKUP_END;
76 generateLineDirective(_page.handler());
77 state = STATE_BLOCK;
78 }
79 else _page.handler() << token;
80 }
81 else if (token == "<%%")
82 {
83 if (state == STATE_MARKUP)
84 {
85 _page.handler() << MARKUP_END;
86 generateLineDirective(_page.preHandler());
87 state = STATE_PREHANDLER;
88 }
89 else _page.handler() << token;
90 }
91 else if (token == "<%!")
92 {
93 if (state == STATE_MARKUP)
94 {
95 _page.handler() << MARKUP_END;
96 generateLineDirective(_page.implDecls());
97 state = STATE_IMPLDECL;
98 }
99 else _page.handler() << token;
100 }
101 else if (token == "<%!!")
102 {
103 if (state == STATE_MARKUP)
104 {
105 _page.handler() << MARKUP_END;
106 generateLineDirective(_page.headerDecls());
107 state = STATE_HDRDECL;
108 }
109 else _page.handler() << token;
110 }
111 else if (token == "<%--")
112 {
113 if (state == STATE_MARKUP)
114 {
115 _page.handler() << MARKUP_END;
116 state = STATE_COMMENT;
117 }
118 else _page.handler() << token;
119 }
120 else if (token == "<%@")
121 {
122 if (state == STATE_MARKUP)
123 {
124 _page.handler() << MARKUP_END;
125 state = STATE_ATTR;
126 _attrs.clear();
127 }
128 else _page.handler() << token;
129 }
130 else if (token == "<%=")
131 {
132 if (state == STATE_MARKUP)
133 {
134 _page.handler() << MARKUP_END;
135 generateLineDirective(_page.handler());
136 _page.handler() << EXPR_BEGIN;
137 state = STATE_EXPR;
138 }
139 else _page.handler() << token;
140 }
141 else if (token == "%>")
142 {
143 if (state == STATE_EXPR)
144 {
145 _page.handler() << EXPR_END;
146 _page.handler() << MARKUP_BEGIN;
147 state = STATE_MARKUP;
148 }
149 else if (state == STATE_ATTR)
150 {
151 parseAttributes();
152 _attrs.clear();
153 _page.handler() << MARKUP_BEGIN;
154 state = STATE_MARKUP;
155 }
156 else if (state != STATE_MARKUP)
157 {
158 _page.handler() << MARKUP_BEGIN;
159 state = STATE_MARKUP;
160 }
161 else _page.handler() << token;
162 }
163 else
164 {
165 switch (state)
166 {
167 case STATE_MARKUP:
168 if (token == "\n")
169 {
170 _page.handler() << "\\n";
171 _page.handler() << MARKUP_END;
172 _page.handler() << MARKUP_BEGIN;
173 }
174 else if (token == "\t")
175 {
176 _page.handler() << "\\t";
177 }
178 else if (token == "\"")
179 {
180 _page.handler() << "\\\"";
181 }
182 else if (token == "\\")
183 {
184 _page.handler() << "\\\\";
185 }
186 else if (token != "\r")
187 {
188 _page.handler() << token;
189 }
190 break;
191 case STATE_IMPLDECL:
192 _page.implDecls() << token;
193 break;
194 case STATE_HDRDECL:
195 _page.headerDecls() << token;
196 break;
197 case STATE_PREHANDLER:
198 _page.preHandler() << token;
199 break;
200 case STATE_BLOCK:
201 _page.handler() << token;
202 break;
203 case STATE_EXPR:
204 _page.handler() << token;
205 break;
206 case STATE_COMMENT:
207 break;
208 case STATE_ATTR:
209 _attrs += token;
210 break;
211 }
212 }
213 nextToken(countingPageStream, token);
214 }
215
216 if (state == STATE_MARKUP)
217 {
218 _page.handler() << MARKUP_END;
219 }
220 else throw Poco::SyntaxException("unclosed meta or code block", where());
221}
222
223
224void PageReader::parseAttributes()
225{
226 static const int eof = std::char_traits<char>::eof();
227
228 std::string basename;
229 std::istringstream istr(_attrs);
230 int ch = istr.get();
231 while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get();
232 while (ch != eof && Poco::Ascii::isAlphaNumeric(ch)) { basename += (char) ch; ch = istr.get(); }
233 while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get();
234 while (ch != eof)
235 {
236 std::string name(basename + ".");
237 std::string value;
238 while (ch != eof && Poco::Ascii::isAlphaNumeric(ch)) { name += (char) ch; ch = istr.get(); }
239 while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get();
240 if (ch != '=') throw Poco::SyntaxException("bad attribute syntax: '=' expected", where());
241 ch = istr.get();
242 while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get();
243 if (ch == '"')
244 {
245 ch = istr.get();
246 while (ch != eof && ch != '"') { value += (char) ch; ch = istr.get(); }
247 if (ch != '"') throw Poco::SyntaxException("bad attribute syntax: '\"' expected", where());
248 }
249 else if (ch == '\'')
250 {
251 ch = istr.get();
252 while (ch != eof && ch != '\'') { value += (char) ch; ch = istr.get(); }
253 if (ch != '\'') throw Poco::SyntaxException("bad attribute syntax: ''' expected", where());
254 }
255 else throw Poco::SyntaxException("bad attribute syntax: '\"' or ''' expected", where());
256 ch = istr.get();
257 handleAttribute(name, value);
258 while (ch != eof && Poco::Ascii::isSpace(ch)) ch = istr.get();
259 }
260}
261
262
263void PageReader::nextToken(std::istream& istr, std::string& token)
264{
265 token.clear();
266 int ch = istr.get();
267 if (ch != -1)
268 {
269 if (ch == '<' && istr.peek() == '%')
270 {
271 token += "<%";
272 istr.get();
273 ch = istr.peek();
274 switch (ch)
275 {
276 case '%':
277 case '@':
278 case '=':
279 ch = istr.get();
280 token += (char) ch;
281 break;
282 case '!':
283 ch = istr.get();
284 token += (char) ch;
285 if (istr.peek() == '!')
286 {
287 ch = istr.get();
288 token += (char) ch;
289 }
290 break;
291 case '-':
292 ch = istr.get();
293 token += (char) ch;
294 if (istr.peek() == '-')
295 {
296 ch = istr.get();
297 token += (char) ch;
298 }
299 break;
300 }
301 }
302 else if (ch == '%' && istr.peek() == '>')
303 {
304 token += "%>";
305 istr.get();
306 }
307 else token += (char) ch;
308 }
309}
310
311
312void PageReader::handleAttribute(const std::string& name, const std::string& value)
313{
314 if (name == "include.page" || name == "include.file")
315 {
316 include(value);
317 }
318 else if (name == "header.include")
319 {
320 _page.headerDecls() << "#include \"" << value << "\"\n";
321 }
322 else if (name == "header.sinclude")
323 {
324 _page.headerDecls() << "#include <" << value << ">\n";
325 }
326 else if (name == "impl.include")
327 {
328 _page.implDecls() << "#include \"" << value << "\"\n";
329 }
330 else if (name == "impl.sinclude")
331 {
332 _page.implDecls() << "#include <" << value << ">\n";
333 }
334 else
335 {
336 _page.set(name, value);
337 }
338}
339
340
341void PageReader::include(const std::string& path)
342{
343 Poco::Path currentPath(_path);
344 Poco::Path includePath(path);
345 currentPath.resolve(includePath);
346
347 _page.handler() << "\t// begin include " << currentPath.toString() << "\n";
348
349 Poco::FileInputStream includeStream(currentPath.toString());
350 PageReader includeReader(*this, currentPath.toString());
351 includeReader.emitLineDirectives(_emitLineDirectives);
352 includeReader.parse(includeStream);
353
354 _page.handler() << "\t// end include " << currentPath.toString() << "\n";
355}
356
357
358std::string PageReader::where() const
359{
360 std::stringstream result;
361 result << "in file '" << _path << "', line " << _line;
362 const PageReader* pParent = _pParent;
363 while (pParent)
364 {
365 result << "\n\tincluded from file '"<< pParent->_path << "', line " << pParent->_line;
366 pParent = pParent->_pParent;
367 }
368 return result.str();
369}
370
371
372void PageReader::generateLineDirective(std::ostream& ostr)
373{
374 if (_emitLineDirectives)
375 {
376 Poco::Path p(_path);
377 p.makeAbsolute();
378 std::string absPath = p.toString();
379 ostr << "#line " << _line << " \"";
380 for (std::string::const_iterator it = absPath.begin(); it != absPath.end(); ++it)
381 {
382 if (*it == '\\')
383 ostr << "\\\\";
384 else
385 ostr << *it;
386 }
387 ostr << "\"\n";
388 }
389}
390