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 | |
20 | const std::string PageReader::("\tresponseStream << \"" ); |
21 | const std::string PageReader::("\";\n" ); |
22 | const std::string PageReader::("\tresponseStream << (" ); |
23 | const std::string PageReader::(");\n" ); |
24 | |
25 | |
26 | 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 | |
37 | 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 | |
48 | PageReader::() |
49 | { |
50 | } |
51 | |
52 | |
53 | void PageReader::(bool flag) |
54 | { |
55 | _emitLineDirectives = flag; |
56 | } |
57 | |
58 | |
59 | void PageReader::(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 | |
224 | void PageReader::() |
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 | |
263 | void PageReader::(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 | |
312 | void 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 | |
341 | void PageReader::(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 | |
358 | std::string PageReader::() 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 | |
372 | void PageReader::(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 | |