Line data Source code
1 : // DUNE DAQ modification notice:
2 : // This file has been modified from the original ATLAS oks source for the DUNE DAQ project.
3 : // Fork baseline commit: oks-08-03-04 (2022-04-14).
4 : // Renamed since fork: no.
5 :
6 : #include "oks/xml.hpp"
7 : #include "oks/cstring.hpp"
8 : #include "oks/defs.hpp"
9 : #include "oks/exceptions.hpp"
10 : #include "oks/kernel.hpp"
11 : #include "oks/object.hpp"
12 :
13 : #include <iostream>
14 : #include <sstream>
15 :
16 : #include <boost/spirit/include/karma.hpp>
17 :
18 : #include <errno.h>
19 : #include <stdlib.h>
20 :
21 : #include "ers/ers.hpp"
22 : #include "logging/Logging.hpp"
23 :
24 : namespace dunedaq {
25 : namespace oks {
26 : OksXmlTokenPool OksXmlInputStream::s_tokens_pool;
27 :
28 : namespace xml {
29 :
30 : const char left_angle_bracket[] = "<";
31 : const char right_angle_bracket[] = ">";
32 : const char ampersand[] = "&";
33 : const char carriage_return[] = "
";
34 : const char new_line[] = "
";
35 : const char tabulation[] = "	";
36 : const char single_quote[] = "'";
37 : const char double_quote[] = """;
38 :
39 : }
40 :
41 677 : exception::exception(const std::string& what_arg, int level_arg) noexcept
42 677 : : p_level(level_arg)
43 : {
44 677 : std::ostringstream s;
45 677 : s << "oks[" << p_level << "] ***: " << what_arg << std::ends;
46 677 : p_what = s.str();
47 677 : }
48 :
49 : void
50 0 : throw_validate_not_empty(const char* name)
51 : {
52 0 : std::string text(name);
53 0 : text += " is not set";
54 0 : throw std::runtime_error(text.c_str());
55 0 : }
56 :
57 : std::string
58 0 : BadFileData::fill(const std::string& item, unsigned long line_no, unsigned long line_pos) noexcept
59 : {
60 0 : std::ostringstream s;
61 0 : s << item << " (line " << line_no << ", char " << line_pos << ')';
62 0 : return s.str();
63 0 : }
64 :
65 : std::string
66 0 : FailedRead::fill(const std::string& item, const std::string& reason) noexcept
67 : {
68 0 : return (std::string("Failed to read \'") + item + "\'\n" + reason);
69 : }
70 :
71 : std::string
72 0 : FailedSave::fill(const std::string& item, const std::string& name, const std::string& reason) noexcept
73 : {
74 0 : return (std::string("Failed to save ") + item + " \'" + name + "\'\n" + reason);
75 : }
76 :
77 : std::string
78 182 : EndOfXmlStream::fill(const std::string& tag)
79 : {
80 182 : return (std::string("Read end-of-stream tag \'") + tag + '\'');
81 : }
82 :
83 : static std::string
84 0 : report_unexpected(const char* what, const char expected, const char read)
85 : {
86 0 : std::string s;
87 :
88 0 : if (what) {
89 0 : s += "Failed to read ";
90 0 : s += what;
91 0 : s += ": ";
92 : }
93 :
94 0 : s += "symbol \'";
95 0 : s += expected;
96 0 : s += "\' is expected instead of \'";
97 0 : s += read;
98 0 : s += '\'';
99 :
100 0 : return s;
101 0 : }
102 :
103 : static void
104 0 : __throw_runtime_error_unexpected_symbol(const char expected, const char read)
105 : {
106 0 : throw std::runtime_error(report_unexpected(0, expected, read));
107 : }
108 :
109 : static void
110 0 : __throw_bad_file_data_unexpected_symbol(const char* what,
111 : const char expected,
112 : const char read,
113 : unsigned long l,
114 : unsigned long p)
115 : {
116 0 : throw oks::BadFileData(report_unexpected(what, expected, read), l, p);
117 : }
118 :
119 : void
120 0 : OksXmlOutputStream::__throw_write_failed()
121 : {
122 0 : throw std::runtime_error("write to file failed");
123 : }
124 :
125 : //
126 : // Save char to xml output stream
127 : // Throw std::exception if failed
128 : //
129 :
130 : void
131 0 : OksXmlOutputStream::put(char c)
132 : {
133 0 : if (c == '<')
134 0 : put_raw(oks::xml::left_angle_bracket, sizeof(oks::xml::left_angle_bracket) - 1);
135 : else if (c == '>')
136 0 : put_raw(oks::xml::right_angle_bracket, sizeof(oks::xml::right_angle_bracket) - 1);
137 : else if (c == '&')
138 0 : put_raw(oks::xml::ampersand, sizeof(oks::xml::ampersand) - 1);
139 : else if (c == '\'')
140 0 : put_raw(oks::xml::single_quote, sizeof(oks::xml::single_quote) - 1);
141 : else if (c == '\"')
142 0 : put_raw(oks::xml::double_quote, sizeof(oks::xml::double_quote) - 1);
143 : else if (c == '\r')
144 0 : put_raw(oks::xml::carriage_return, sizeof(oks::xml::carriage_return) - 1);
145 : else if (c == '\n')
146 0 : put_raw(oks::xml::new_line, sizeof(oks::xml::new_line) - 1);
147 : else if (c == '\t')
148 0 : put_raw(oks::xml::tabulation, sizeof(oks::xml::tabulation) - 1);
149 : else
150 0 : put_raw(c);
151 0 : }
152 :
153 : //
154 : // Save string to xml output stream
155 : // Throw std::exception if failed
156 : //
157 :
158 : void
159 0 : OksXmlOutputStream::put(const char* str)
160 : {
161 0 : while (*str)
162 0 : put(*str++);
163 0 : }
164 :
165 : //
166 : // Save quoted string to xml output stream
167 : // Throw std::exception if failed
168 : //
169 :
170 : void
171 0 : OksXmlOutputStream::put_quoted(const char* str)
172 : {
173 0 : put_raw('\"');
174 0 : put(str);
175 0 : put_raw('\"');
176 0 : }
177 :
178 : //
179 : // Save xml start tag and element name, i.e. '<element-name'
180 : // Throw std::exception if failed
181 : //
182 :
183 : void
184 0 : OksXmlOutputStream::put_start_tag(const char* name, size_t len)
185 : {
186 0 : put_raw('<');
187 0 : put_raw(name, len);
188 0 : }
189 :
190 : //
191 : // Save xml end tag, i.e. '/>\n'
192 : // Throw std::exception if failed
193 : //
194 :
195 : void
196 0 : OksXmlOutputStream::put_end_tag()
197 : {
198 0 : static const char __end_tag[] = "/>\n";
199 0 : put_raw(__end_tag, sizeof(__end_tag) - 1);
200 0 : }
201 :
202 : void
203 0 : OksXmlOutputStream::put_eol()
204 : {
205 0 : static const char __eol_tag[] = ">\n";
206 0 : put_raw(__eol_tag, sizeof(__eol_tag) - 1);
207 0 : }
208 :
209 : //
210 : // Save xml last tag, i.e. '</tag-name>\n'
211 : // Throw std::exception if failed
212 : //
213 :
214 : void
215 0 : OksXmlOutputStream::put_last_tag(const char* name, size_t len)
216 : {
217 0 : put_raw('<');
218 0 : put_raw('/');
219 0 : put_raw(name, len);
220 0 : put_raw('>');
221 0 : put_raw('\n');
222 0 : }
223 :
224 : //
225 : // Save xml element attribute with value, i.e. ' name="value"'
226 : // Throw std::exception if failed
227 : //
228 :
229 : void
230 0 : OksXmlOutputStream::put_attribute(const char* name, size_t len, const char* value)
231 : {
232 0 : put_raw(' ');
233 0 : put_raw(name, len);
234 0 : put_raw('=');
235 0 : put_raw('\"');
236 0 : put(value);
237 0 : put_raw('\"');
238 0 : }
239 :
240 : void
241 0 : OksXmlOutputStream::put_attribute(const char* name, size_t len, uint32_t value)
242 : {
243 0 : put_raw(' ');
244 0 : put_raw(name, len);
245 0 : put_raw('=');
246 0 : put_raw('\"');
247 0 : if (value == 0) {
248 0 : put_raw('0');
249 : } else {
250 0 : char buf[12];
251 0 : char* ptr = buf;
252 0 : boost::spirit::karma::generate(ptr, boost::spirit::uint_, (unsigned int)value);
253 0 : put_raw(buf, ptr - buf);
254 : }
255 0 : put_raw('\"');
256 0 : }
257 :
258 : void
259 0 : OksXmlOutputStream::put_attribute(const char* name, size_t len, const OksData& value)
260 : {
261 0 : put_raw(' ');
262 0 : put_raw(name, len);
263 0 : put_raw('=');
264 0 : put_raw('\"');
265 0 : value.WriteTo(*this);
266 0 : put_raw('\"');
267 0 : }
268 :
269 : void
270 0 : OksXmlInputStream::throw_unexpected_tag(const char* what, const char* expected)
271 : {
272 0 : std::ostringstream s;
273 :
274 0 : if (!*what) {
275 0 : if (expected) {
276 0 : s << "expected tag \'" << expected << '\'';
277 : } else {
278 0 : s << "empty tag";
279 : }
280 : } else {
281 0 : s << "unexpected tag \'" << what << '\'';
282 0 : if (expected) {
283 0 : s << " instead of \'" << expected << '\'';
284 : }
285 : }
286 :
287 0 : throw oks::BadFileData(s.str(), line_no, line_pos);
288 0 : }
289 :
290 : void
291 0 : OksXmlInputStream::throw_unexpected_attribute(const char* what)
292 : {
293 0 : throw oks::BadFileData(std::string("Value \'") + what + "\' is not a valid attribute name", line_no, line_pos);
294 : }
295 :
296 : size_t
297 0 : OksXmlInputStream::get_quoted()
298 : {
299 0 : size_t pos(0);
300 :
301 0 : try {
302 0 : char c = get_first_non_empty();
303 :
304 0 : if (__builtin_expect((c != '\"'), 0)) {
305 0 : __throw_runtime_error_unexpected_symbol('\"', c);
306 : }
307 :
308 0 : while (true) {
309 0 : c = get();
310 :
311 0 : if (__builtin_expect((c == '\"'), 0)) {
312 0 : m_v1->m_buf[pos] = 0;
313 0 : return pos;
314 0 : } else if (__builtin_expect((c == '&'), 0)) {
315 0 : c = cvt_char();
316 : }
317 :
318 0 : m_v1->realloc(pos);
319 0 : m_v1->m_buf[pos++] = c;
320 : }
321 :
322 : throw std::runtime_error("cannot find closing quote character");
323 0 : } catch (std::exception& ex) {
324 0 : m_v1->m_buf[pos] = 0;
325 0 : throw oks::BadFileData(std::string("Failed to read quoted string: ") + ex.what(), line_no, line_pos);
326 0 : }
327 : }
328 :
329 : // check for start of comment
330 :
331 : inline bool
332 580659 : __is_comment(const char* s)
333 : {
334 580659 : return oks::cmp_str4n(s, "<!--");
335 : }
336 :
337 : //
338 : // Read xml tag while number of closing angles (i.e. '>') will not be equal
339 : // number of opening ones (i.e. '<')
340 : //
341 :
342 : const char*
343 630 : OksXmlInputStream::get_tag()
344 : {
345 630 : m_v1->m_buf[0] = 0;
346 :
347 630 : unsigned long start_line_no = line_no;
348 630 : unsigned long start_line_pos = line_pos;
349 :
350 630 : size_t pos(0);
351 :
352 945 : try {
353 945 : while (true) {
354 945 : char c(get_first_non_empty());
355 :
356 945 : if (__builtin_expect((c != '<'), 0)) {
357 0 : __throw_bad_file_data_unexpected_symbol("start-of-tag", '<', c, line_no, line_pos);
358 : }
359 :
360 945 : start_line_no = line_no;
361 945 : start_line_pos = line_pos;
362 :
363 945 : m_v1->m_buf[0] = c;
364 945 : pos = 1;
365 :
366 945 : int count(1);
367 :
368 582549 : while (true) {
369 582549 : char c2 = get();
370 :
371 582549 : if (c2 == '<')
372 6349 : count++;
373 576200 : else if (c2 == '>')
374 6979 : count--;
375 :
376 582549 : m_v1->realloc(pos);
377 582549 : m_v1->m_buf[pos++] = c2;
378 :
379 582549 : if (pos > 3) {
380 580659 : if (__is_comment(m_v1->m_buf + pos - 4)) {
381 315 : char buf[3] = { 0, 0, 0 };
382 315 : unsigned long start_comment_line_no = line_no;
383 8239 : try {
384 8239 : while (true) {
385 8239 : buf[0] = buf[1];
386 8239 : buf[1] = buf[2];
387 8239 : buf[2] = get();
388 :
389 8239 : if (oks::cmp_str3n(buf, "-->")) {
390 : break; // end of comment = '-->'
391 : }
392 : }
393 0 : } catch (std::exception& failure) {
394 0 : std::ostringstream what;
395 0 : what << "Cannot find end of comment started on line " << start_comment_line_no << ": " << failure.what();
396 0 : throw oks::BadFileData(what.str(), line_no, line_pos);
397 0 : }
398 :
399 315 : pos -= 4;
400 315 : count--;
401 : }
402 : }
403 :
404 582549 : if (count == 0) {
405 945 : m_v1->m_buf[pos] = '\0';
406 945 : break;
407 : }
408 : }
409 :
410 945 : if (pos > 0) {
411 630 : if (count != 0) {
412 : throw std::runtime_error("unbalanced tags");
413 : } else {
414 630 : return m_v1->m_buf;
415 : }
416 : }
417 : }
418 0 : } catch (std::exception& ex) {
419 0 : m_v1->m_buf[pos] = '\0';
420 0 : throw oks::BadFileData(std::string(ex.what()) + " when read xml tag started at", start_line_no, start_line_pos);
421 0 : }
422 : }
423 :
424 : //
425 : // Reads token from stream until any symbol from 'separator' will be found.
426 : // If 'first_symbol' is not 0, if will be first symbol of token.
427 : //
428 :
429 : inline size_t
430 215142 : OksXmlInputStream::get_token(const char __s1, OksXmlToken& token)
431 : {
432 215142 : size_t pos(0);
433 :
434 6197406 : while (true) {
435 3206274 : last_read_c = get();
436 :
437 3206416 : if (last_read_c == __s1) {
438 215326 : token.m_buf[pos] = '\0';
439 215326 : return pos;
440 2991090 : } else if (__builtin_expect((last_read_c == '&'), 0))
441 16930 : last_read_c = cvt_char();
442 :
443 2991090 : token.realloc(pos);
444 2991132 : token.m_buf[pos++] = last_read_c;
445 : }
446 :
447 : token.m_buf[pos] = 0;
448 :
449 : throw std::runtime_error("cannot find closing separator while read token");
450 : }
451 :
452 : inline size_t
453 263098 : OksXmlInputStream::get_token2(char __fs, const char __s1, const char __s2, OksXmlToken& token)
454 : {
455 263098 : size_t pos(1);
456 263098 : token.m_buf[0] = __fs;
457 263098 : if (__fs == __s1 || __fs == __s2) {
458 18462 : token.m_buf[1] = '\0';
459 18462 : return 1;
460 : }
461 :
462 2443826 : while (true) {
463 1344231 : last_read_c = get();
464 :
465 1344237 : if (last_read_c == __s1 || last_read_c == __s2) {
466 244634 : token.m_buf[pos] = '\0';
467 244634 : return pos;
468 1099603 : } else if (__builtin_expect((last_read_c == '&'), 0))
469 0 : last_read_c = cvt_char();
470 :
471 1099603 : token.realloc(pos);
472 1099595 : token.m_buf[pos++] = last_read_c;
473 : }
474 :
475 : token.m_buf[pos] = 0;
476 :
477 : throw std::runtime_error("cannot find closing separator while read token");
478 : }
479 :
480 : inline void
481 84647 : OksXmlInputStream::get_token5(const char __s1,
482 : const char __s2,
483 : const char __s3,
484 : const char __s4,
485 : const char __s5,
486 : OksXmlToken& token)
487 : {
488 84647 : size_t pos(0);
489 :
490 1221393 : while (true) {
491 653020 : last_read_c = get();
492 :
493 653038 : if (last_read_c == __s1 || last_read_c == __s2 || last_read_c == __s3 || last_read_c == __s4 ||
494 : last_read_c == __s5) {
495 84663 : token.m_buf[pos] = '\0';
496 84663 : return;
497 568375 : } else if (__builtin_expect((last_read_c == '&'), 0))
498 0 : last_read_c = cvt_char();
499 :
500 568375 : token.realloc(pos);
501 568373 : token.m_buf[pos++] = last_read_c;
502 : }
503 :
504 : token.m_buf[pos] = 0;
505 :
506 : throw std::runtime_error("cannot find closing separator while read token");
507 : }
508 :
509 : const char*
510 84620 : OksXmlInputStream::get_tag_start()
511 : {
512 84638 : try {
513 84638 : while (true) {
514 84638 : char c = get_first_non_empty();
515 :
516 84650 : if (__builtin_expect((c != '<'), 0)) {
517 0 : __throw_runtime_error_unexpected_symbol('<', c);
518 : }
519 :
520 84650 : get_token5(' ', '>', '\t', '\n', '\r', *m_v1);
521 84653 : TLOG_DEBUG(8) << "read tag \'" << m_v1->m_buf << '\'';
522 :
523 84637 : if (__builtin_expect((oks::cmp_str3n(m_v1->m_buf, "!--")), 0)) {
524 18 : char buf[3] = { 0, 0, 0 };
525 18 : unsigned long start_comment_line_no = line_no;
526 18 : auto tag_len = strlen(m_v1->m_buf);
527 18 : if (tag_len < 5 || !oks::cmp_str2n(m_v1->m_buf + tag_len - 2,
528 : "--")) // special case for comments without empty symbols like <!--foo-->
529 3231 : try {
530 3231 : while (true) {
531 3231 : buf[0] = buf[1];
532 3231 : buf[1] = buf[2];
533 3231 : c = buf[2] = get();
534 3231 : if (oks::cmp_str3n(buf, "-->")) {
535 : break; // end of comment = '-->'
536 : }
537 : }
538 0 : } catch (std::exception& failure) {
539 0 : std::ostringstream what;
540 0 : what << "Cannot find end of comment started on line " << start_comment_line_no << ": " << failure.what();
541 0 : throw oks::BadFileData(what.str(), line_no, line_pos);
542 0 : }
543 : }
544 :
545 84637 : if (__builtin_expect((c != '>'), 1))
546 84619 : return m_v1->m_buf;
547 : }
548 0 : } catch (std::exception& ex) {
549 0 : throw oks::BadFileData(std::string("Failed to read start-of-tag: ") + ex.what(), line_no, line_pos);
550 0 : }
551 :
552 : return 0; // never reach this line
553 : }
554 :
555 263096 : OksXmlAttribute::OksXmlAttribute(OksXmlInputStream& s)
556 263096 : : p_name(*s.m_v1)
557 263096 : , p_value(*s.m_v2)
558 : {
559 263096 : unsigned long start_line_no = s.line_no;
560 263096 : unsigned long start_line_pos = s.line_pos;
561 :
562 263096 : try {
563 263096 : char c = s.get_first_non_empty();
564 :
565 263089 : start_line_no = s.line_no;
566 263089 : start_line_pos = s.line_pos;
567 :
568 263089 : size_t pos = s.get_token2(c, '=', '>', p_name);
569 :
570 262829 : char* m_buf_ptr(p_name.m_buf);
571 :
572 : // skip empty symbols
573 :
574 262829 : for (char* str(m_buf_ptr + pos - 1); str >= m_buf_ptr; --str) {
575 262656 : if (*str == ' ' || *str == '\n' || *str == '\r' || *str == '\t') {
576 0 : *str = 0;
577 : } else {
578 : break;
579 : }
580 : }
581 :
582 262829 : if (c == '>' || c == '/' || c == '?')
583 : return; // end of attribute
584 :
585 198183 : c = s.get_first_non_empty();
586 :
587 198211 : if (__builtin_expect((c != '\"'), 0)) {
588 0 : __throw_bad_file_data_unexpected_symbol("start-of-attribute-value", '\"', c, s.line_no, s.line_pos);
589 : }
590 :
591 198211 : p_value_len = s.get_token('\"', p_value);
592 :
593 198397 : TLOG_DEBUG(8) << "read attribute \'" << m_buf_ptr << "\' and value \'" << p_value.m_buf << '\'';
594 0 : } catch (std::exception& ex) {
595 0 : throw oks::BadFileData(
596 0 : std::string(ex.what()) + " when read xml attribute started at", start_line_no, start_line_pos);
597 0 : }
598 : }
599 :
600 : //
601 : // This method is used to convert special xml symbols:
602 : // "<" -> <
603 : // ">" -> >
604 : // "&" -> &
605 : // "'" -> '
606 : // """ -> "
607 : //
608 :
609 : char
610 16930 : OksXmlInputStream::cvt_char()
611 : {
612 16930 : get_token(';', *m_cvt_char);
613 :
614 16930 : if (oks::cmp_str2n(m_cvt_char->m_buf, oks::xml::left_angle_bracket + 1))
615 : return '<';
616 13964 : else if (oks::cmp_str2n(m_cvt_char->m_buf, oks::xml::right_angle_bracket + 1))
617 : return '>';
618 10826 : else if (oks::cmp_str3n(m_cvt_char->m_buf, oks::xml::ampersand + 1))
619 : return '&';
620 9442 : else if (oks::cmp_str3n(m_cvt_char->m_buf, oks::xml::carriage_return + 1))
621 : return '\r';
622 9442 : else if (oks::cmp_str3n(m_cvt_char->m_buf, oks::xml::new_line + 1))
623 : return '\n';
624 2417 : else if (oks::cmp_str3n(m_cvt_char->m_buf, oks::xml::tabulation + 1))
625 : return '\t';
626 2417 : else if (oks::cmp_str4n(m_cvt_char->m_buf, oks::xml::single_quote + 1))
627 : return '\'';
628 1808 : else if (oks::cmp_str4n(m_cvt_char->m_buf, oks::xml::double_quote + 1))
629 : return '\"';
630 : else {
631 0 : std::string text("bad symbol \'&");
632 0 : text += m_cvt_char->m_buf;
633 0 : text += '\'';
634 0 : throw std::runtime_error(text.c_str());
635 0 : }
636 : }
637 :
638 : std::ostream&
639 0 : OksXmlInputStream::error_msg(const char* msg)
640 : {
641 0 : return Oks::error_msg(msg) << "(line " << line_no << ", char " << line_pos << ")\n";
642 : }
643 :
644 : void
645 0 : OksXmlInputStream::__throw_eof() const
646 : {
647 0 : throw std::runtime_error("unexpected end of file");
648 : }
649 :
650 : void
651 0 : OksXmlInputStream::__throw_strto(const char* f,
652 : const char* where,
653 : const char* what,
654 : unsigned long line_no,
655 : unsigned long line_pos)
656 : {
657 0 : std::ostringstream text;
658 0 : text << "function " << f << "(\'" << what << "\') has failed";
659 0 : if (*where)
660 0 : text << " on unrecognized characters \'" << where << "\'";
661 0 : if (errno)
662 0 : text << " with code " << errno << ", reason = \'" << oks::strerror(errno) << '\'';
663 0 : throw oks::BadFileData(text.str(), line_no, line_pos);
664 0 : }
665 :
666 : } // namespace oks
667 : } // namespace dunedaq
|