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