fix8  version 1.4.0
Open Source C++ FIX Framework
xml.cpp
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------------------
2 /*
3 
4 Fix8 is released under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
5 
6 Fix8 Open Source FIX Engine.
7 Copyright (C) 2010-16 David L. Dight <fix@fix8.org>
8 
9 Fix8 is free software: you can redistribute it and / or modify it under the terms of the
10 GNU Lesser General Public License as published by the Free Software Foundation, either
11 version 3 of the License, or (at your option) any later version.
12 
13 Fix8 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
14 even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 You should have received a copy of the GNU Lesser General Public License along with Fix8.
17 If not, see <http://www.gnu.org/licenses/>.
18 
19 BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO
20 THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
21 COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
22 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
24 THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
25 YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
26 
27 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT
28 HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
29 ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
30 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
31 NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
32 THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
33 HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 
35 */
36 //-----------------------------------------------------------------------------------------
37 #include "precomp.hpp"
38 #include <fix8/f8includes.hpp>
39 
40 //----------------------------------------------------------------------------------------
41 using namespace FIX8;
42 using namespace std;
43 
44 //----------------------------------------------------------------------------------------
47 RegExp XmlElement::rCE_("&#(x[A-Fa-f0-9]+|[0-9]+);"), XmlElement::rCX_("&([a-z]{2,}[1-4]{0,});"),
48  XmlElement::rIn_("href=\"([^\"]+)\""),
49  XmlElement::rEn_("\\$\\{([^}]+)\\}"), XmlElement::rEv_("!\\{([^}]+)\\}");
51 
52 //----------------------------------------------------------------------------------------
54 {
55  {"amp", '&'}, {"lt", '<'}, {"gt", '>'},
56  {"apos", '\''}, {"quot", '"'}, {"nbsp", 160},
57  {"iexcl", 161}, {"cent", 162}, {"pound", 163},
58  {"curren", 164}, {"yen", 165}, {"brvbar", 166},
59  {"sect", 167}, {"uml", 168}, {"copy", 169},
60  {"ordf", 170}, {"laquo", 171}, {"not", 172},
61  {"shy", 173}, {"reg", 174}, {"macr", 175},
62  {"deg", 176}, {"plusmn", 177}, {"sup2", 178},
63  {"sup3", 179}, {"acute", 180}, {"micro", 181},
64  {"para", 182}, {"middot", 183}, {"cedil", 184},
65  {"sup1", 185}, {"ordm", 186}, {"raquo", 187},
66  {"frac14", 188}, {"frac12", 189}, {"frac34", 190},
67  {"iquest", 191}
68 };
69 
70 //-----------------------------------------------------------------------------------------
71 ostream& operator<<(ostream& os, const XmlElement& en)
72 {
73  const string spacer(en.depth_ * 3, ' ');
74  if (en.decl_)
75  os << *en.decl_ << endl;
76  os << spacer << en.tag_;
77 
78  if (en.value_)
79  os << "=\"" << *en.value_ << '\"';
80 
81  if (en.attrs_)
82  {
83  os << " [";
84  bool first(true);
85  for (XmlElement::XmlAttrs::iterator itr(en.attrs_->begin()); itr != en.attrs_->end(); ++itr)
86  {
87  if (first)
88  first = false;
89  else
90  os << ' ';
91  os << itr->first << "=\"" << itr->second << '"';
92  }
93  os << ']';
94  }
95 
96  if (en.txtline_)
97  os << " #" << en.txtline_;
98  os << endl;
99 
100  if (en.children_)
101  {
102  os << spacer << '{' << endl;
103  for (XmlElement::XmlSet::iterator itr(en.ordchildren_->begin()); itr != en.ordchildren_->end(); ++itr)
104  os << **itr;
105  os << spacer << '}' << endl;
106  }
107 
108  return os;
109 }
110 
111 //-----------------------------------------------------------------------------------------
112 namespace {
113 
114 // execute command and pipe output to string; only 1 line is captured.
115 bool exec_cmd(const string& cmd, string& result)
116 {
117 #ifdef _MSC_VER
118  FILE *apipe(_popen(cmd.c_str(), "r"));
119 #else
120  FILE *apipe(popen(cmd.c_str(), "r"));
121 #endif
122  if (apipe)
123  {
124  const size_t maxcmdresultlen(1024);
125  char buffer[maxcmdresultlen] {};
126  if (!feof(apipe) && fgets(buffer, maxcmdresultlen, apipe) && buffer[0])
127  {
128  result = buffer;
129  result.resize(result.size() - 1); // remove lf
130  }
131 #ifdef _MSC_VER
132  _pclose(apipe);
133 #else
134  pclose(apipe);
135 #endif
136  }
137  return !result.empty();
138 }
139 
140 }
141 
142 //-----------------------------------------------------------------------------------------
143 // finite state machine with simple recursive descent parser
144 //-----------------------------------------------------------------------------------------
145 XmlElement::XmlElement(istream& ifs, int subidx, XmlElement *parent, int txtline, int depth, const char *rootAttr)
146  : parent_(parent), root_(parent_ ? parent_->root_ : this), errors_(), line_(1), incline_(1), maxdepth_(),
147  seq_(), value_(), decl_(), depth_(depth), sequence_(++root_->seq_), txtline_(txtline),
148  chldcnt_(), subidx_(subidx), attrs_(), children_(), _was_include(), ordchildren_()
149 {
150  istream *ifsptr(&ifs);
151 
152  enum
153  {
154  olb, otag, ocom0, ocom1, comment, ccom0, ccom1, ccomment,
155  oattr, odec, cdec, value, cls, ctag,
156  cdata0, cdata1, cdata2, cdata3, cdata4, cdata5, cdata6, cdata7, // ![CDATA[
157  vcdata, ecdata0, ecdata1, // ]]
158  finished
159  }
160  state(olb);
161 
162  string tmpotag, tmpctag, tmpval, tmpattr, tmpdec;
163  int starttmpotag(0);
164 
165  if (rootAttr)
166  {
167  attrs_ = new XmlAttrs;
168  attrs_->insert({"docpath", rootAttr});
169  }
170 
171  if (root_->maxdepth_ < depth)
172  root_->maxdepth_ = depth_;
173 
174  while (ifsptr->good() && state != finished)
175  {
176  char c;
177  *ifsptr >> noskipws >> c;
178  switch (c)
179  {
180  case '\n':
181  if (!root_->inclusion_.empty())
182  ++root_->incline_;
183  else
184  ++root_->line_; // drop through
185  case '\r':
186  continue;
187  default:
188  break;
189  }
190 
191  switch (state)
192  {
193  case olb:
194  if (c == '<')
195  state = otag;
196  break;
197  case otag:
198  if (c == '>')
199  state = value;
200  else if (c == '/' && ifsptr->peek() == '>') //empty
201  {
202  state = ctag;
203  tmpctag = tmpotag;
204  }
205  else if (isspace(c) && !tmpotag.empty())
206  state = oattr;
207  else if (c == '?' && tmpotag.empty())
208  state = odec;
209  else if (c == '!' && tmpotag.empty())
210  state = ocom0;
211  else if (c == '=' || c == '\\' || c == '"' || c == '\'')
212  goto illegal_tag;
213  else if (!isspace(c))
214  {
215  if (!starttmpotag)
216  starttmpotag = root_->line_;
217  tmpotag += c;
218  }
219  break;
220  case ocom0:
221  if (c == '-')
222  state = ocom1;
223  else
224  {
225  tmpotag += "!";
226  state = otag;
227  }
228  break;
229  case ocom1:
230  if (c == '-')
231  state = comment;
232  else
233  {
234  tmpotag += "!-";
235  state = otag;
236  }
237  break;
238  case odec:
239  if (c == '?') //declaration
240  {
241  state = cdec;
242  if (!tmpdec.empty())
243  decl_ = new string(tmpdec);
244  }
245  else
246  tmpdec += c;
247  break;
248  case oattr:
249  if (c == '/' && ifsptr->peek() == '>') //empty
250  {
251  state = ctag;
252  tmpctag = tmpotag;
253  }
254  else if (c == '>')
255  state = value;
256  else
257  tmpattr += c;
258  break;
259  case comment:
260  if (c == '-')
261  state = ccom0;
262  break;
263  case ccom0:
264  if (c == '-')
265  state = ccomment;
266  else
267  state = comment;
268  break;
269  case ccomment:
270  if (c == '>')
271  state = depth_ ? finished : olb;
272  else
273  state = comment;
274  break;
275  case cdec:
276  if (c == '>')
277  state = depth_ ? finished : olb;
278  break;
279  case cdata0:
280  state = cdata1;
281  break;
282  case cdata1:
283  if (c == '[')
284  state = cdata2;
285  else
286  {
287  state = value;
288  tmpval += "![";
289  tmpval += c;
290  }
291  break;
292  case cdata2:
293  if (c == 'C')
294  state = cdata3;
295  else
296  {
297  state = value;
298  tmpval += "![";
299  tmpval += c;
300  }
301  break;
302  case cdata3:
303  if (c == 'D')
304  state = cdata4;
305  else
306  {
307  state = value;
308  tmpval += "![C";
309  tmpval += c;
310  }
311  break;
312  case cdata4:
313  if (c == 'A')
314  state = cdata5;
315  else
316  {
317  state = value;
318  tmpval += "![CD";
319  tmpval += c;
320  }
321  break;
322  case cdata5:
323  if (c == 'T')
324  state = cdata6;
325  else
326  {
327  state = value;
328  tmpval += "![CDA";
329  tmpval += c;
330  }
331  break;
332  case cdata6:
333  if (c == 'A')
334  state = cdata7;
335  else
336  {
337  state = value;
338  tmpval += "![CDAT";
339  tmpval += c;
340  }
341  break;
342  case cdata7:
343  if (c == '[')
344  state = vcdata;
345  else
346  {
347  state = value;
348  tmpval += "![CDATA";
349  tmpval += c;
350  }
351  break;
352  case vcdata:
353  if (c == ']')
354  state = ecdata0;
355  else
356  tmpval += c;
357  break;
358  case ecdata0:
359  if (c == ']')
360  state = ecdata1;
361  else
362  {
363  tmpval += ']';
364  tmpval += c;
365  state = vcdata;
366  }
367  break;
368  case ecdata1:
369  if (c == '>')
370  state = value;
371  else
372  {
373  tmpval += "]]";
374  state = vcdata;
375  }
376  break;
377  case value:
378  if (c == '<')
379  {
380  /* // FIXME - breaks comments
381  if (ifsptr->peek() == '!')
382  {
383  state = cdata0;
384  break;
385  }
386  */
387  if (ifsptr->peek() != '/')
388  {
389  ifsptr->putback(c);
390  if (depth_ + 1 > MaxDepth)
391  {
392  ++root_->errors_;
393  ostringstream ostr;
394  ostr << "Error (" << root_->line_ << "): maximum depth exceeded (" << MaxDepth << ')';
395  state = olb;
396  throw XMLError(ostr.str());
397  }
398  else
399  {
400  XmlElement *child(new XmlElement(*ifsptr, chldcnt_ + 1, this, root_->line_, depth_ + 1));
401  if (child->GetTag().empty()
402  || (child->_was_include && (!child->children_ || !child->children_->begin()->second->children_)))
403  {
404  delete child;
405  --root_->seq_;
406  }
407  else
408  {
409  if (!children_)
410  {
411  children_ = new XmlSubEls;
412  ordchildren_ = new XmlSet;
413  }
414 
415  if (child->_was_include) // move great-grandchildren to children
416  {
417  for (XmlSubEls::const_iterator itr(child->children_->begin()->second->children_->begin());
418  itr != child->children_->begin()->second->children_->end(); ++itr)
419  {
420  --itr->second->depth_;
421  children_->insert({itr->first, itr->second});
422  ordchildren_->insert(itr->second);
423  }
424 
425  delete child;
426  root_->inclusion_.clear();
427  }
428  else
429  {
430  ++chldcnt_;
431  children_->insert({child->GetTag(), child});
432  ordchildren_->insert(child);
433  }
434  }
435  }
436  }
437  else
438  state = cls;
439  }
440  else
441  tmpval += c;
442  break;
443  case cls:
444  if (c == '/')
445  state = ctag;
446  break;
447  case ctag:
448  if (c == '>')
449  {
450  state = finished;
451  if (tmpotag != tmpctag)
452  {
453 illegal_tag:
454  ++root_->errors_;
455  ostringstream ostr;
456  ostr << "Error (" << root_->line_ << "): unmatched tag " << '\''
457  << tmpotag << "' (" << starttmpotag << ") does not close with " << '\'' << tmpctag << '\'';
458  if (!root_->inclusion_.empty())
459  ostr << " in inclusion " << root_->inclusion_ << " (" << root_->incline_ << ')';
460  throw XMLError(ostr.str());
461  }
462  else
463  {
464  if ((tag_ = tmpotag) == "xi:include") // handle inclusion
465  {
466  RegMatch match;
467  if (rIn_.SearchString(match, tmpattr, 2) == 2)
468  {
469  string whatv;
470  rIn_.SubExpr(match, tmpattr, whatv, 0, 1);
471  ifstream *ifs1(new ifstream(InplaceXlate(whatv).c_str()));
472  if (!*ifs1)
473  {
474  ++root_->errors_;
475  ostringstream ostr;
476  ostr << "Error (" << root_->line_ << "): could not process include " << '\'' << whatv << '\'';
477  throw XMLError(ostr.str());
478  }
479  else
480  {
481  ifsptr = ifs1; // process xml elements from this stream now
482  state = olb; // reset
483  root_->inclusion_ = whatv;
484  root_->incline_ = 1;
485  tmpctag.clear();
486  tmpval.clear();
487  tmpattr.clear();
488  tmpdec.clear();
489  _was_include = true;
490  break;
491  }
492  }
493  else
494  {
495  ostringstream ostr;
496  ++root_->errors_;
497  ostr << "Error (" << root_->line_ << "): invalid xml include specification " << '\'' << tmpattr << '\'';
498  throw XMLError(ostr.str());
499  }
500  }
501 
502  tag_ = tmpotag;
503  if (!tmpval.empty() && tmpval.find_first_not_of(" \t\n\r") != string::npos)
504  value_ = new string(InplaceXlate(tmpval));
505  }
506  }
507  else if (!isspace(c))
508  tmpctag += c;
509  break;
510  default:
511  break;
512  }
513  }
514 
515  if (!tmpattr.empty())
516  ParseAttrs(tmpattr);
517 
518  if (_was_include)
519  delete ifsptr;
520 }
521 
522 //-----------------------------------------------------------------------------------------
524 {
525  if (!what)
526  return false;
527 
528  if (!children_)
529  {
530  children_ = new XmlSubEls;
531  ordchildren_ = new XmlSet;
532  }
533 
534  ++chldcnt_;
535  children_->insert({what->GetTag(), what});
536  ordchildren_->insert(what);
537 
538  return true;
539 }
540 
541 //-----------------------------------------------------------------------------------------
542 int XmlElement::ParseAttrs(const string& attlst)
543 {
544  istringstream istr(attlst);
545  enum { ews, tag, es, oq, value, oc0, comment, cc0 } state(ews);
546  string tmptag, tmpval;
547  char comchar(0);
548 
549  while (istr.good())
550  {
551  char c;
552  istr >> noskipws >> c;
553 
554  switch (state)
555  {
556  case comment:
557  if (c == '*')
558  state = cc0;
559  break;
560  case cc0:
561  if (c == '/')
562  state = ews;
563  else
564  state = comment;
565  break;
566  case ews:
567  if (c == '/')
568  state = oc0;
569  else if (!isspace(c))
570  {
571  tmptag += c;
572  state = tag;
573  }
574  break;
575  case oc0:
576  if (c == '*' && !(flags_ & noextensions))
577  {
578  state = comment;
579  break;
580  }
581  else
582  {
583  tmptag += '/';
584  tmptag += c;
585  state = tag;
586  }
587  case tag:
588  if (isspace(c))
589  state = es;
590  else if (c == '=')
591  state = oq;
592  else if (c == '"' || c == '\'')
593  {
594 illegal_char:
595  ostringstream ostr;
596  ostr << "Error (" << root_->line_ << ") attribute \'" << tmptag << "\' illegal character defined";
597  if (!root_->inclusion_.empty())
598  ostr << " in inclusion " << root_->inclusion_ << " (" << root_->incline_ << ')';
599  throw XMLError(ostr.str());
600  }
601  else
602  tmptag += c;
603  break;
604  case es:
605  if (c == '=')
606  state = oq;
607  else if (c == '"' || c == '\'')
608  goto illegal_char;
609  break;
610  case oq:
611  if (c == '"' || c == '\'')
612  {
613  comchar = c;
614  state = value;
615  }
616  else if (!isspace(c))
617  goto illegal_char;
618  break;
619  case value:
620  if (c != comchar)
621  tmpval += c;
622  else
623  {
624  if (tmptag.find_first_of("\\\'\"=") != string::npos)
625  goto illegal_char;
626  if (tmptag != "docpath")
627  {
628  if (!attrs_)
629  attrs_ = new XmlAttrs;
630  if (!attrs_->insert({tmptag, InplaceXlate(tmpval)}).second)
631  {
632  ++root_->errors_;
633  ostringstream ostr;
634  ostr << "Error (" << root_->line_ << ") attribute \'" << tmptag << "\' already defined";
635  if (!root_->inclusion_.empty())
636  ostr << " in inclusion " << root_->inclusion_ << " (" << root_->incline_ << ')';
637  throw XMLError(ostr.str());
638  }
639  }
640  tmptag.clear();
641  tmpval.clear();
642  state = ews;
643  comchar = 0;
644  }
645  break;
646  default:
647  break;
648  }
649  }
650 
651  return attrs_ ? static_cast<int>(attrs_->size()) : 0;
652 }
653 
654 //-----------------------------------------------------------------------------------------
656 {
657  delete value_;
658  delete attrs_;
659  delete decl_;
660 
661  if (children_ && !_was_include)
662  for_each (children_->begin(), children_->end(), [](XmlSubEls::value_type& pp) { delete pp.second; });
663  delete children_;
664  delete ordchildren_;
665 }
666 
667 //-----------------------------------------------------------------------------------------
668 const XmlElement *XmlElement::find(const string& what, const string *atag,
669  const string *aval, const char delim) const // find 1st matching entity
670 {
671  if (what.compare(0, 2, "//") == 0) // root based
672  return root_->find(what.substr(2), atag, aval, delim);
673 
674  if (flags_ & nocase ? what % tag_ : what == tag_)
675  return atag && aval && !findAttrByValue(*atag, *aval) ? nullptr : this;
676 
677  if (children_)
678  {
679  string lwhat(what);
680  string::size_type fpos(lwhat.find_first_of(delim));
681 
682  if (fpos != string::npos && lwhat.substr(0, fpos) == tag_)
683  {
684  lwhat.erase(0, fpos + 1);
685  fpos = lwhat.find_first_of(delim);
686  const string nwhat(fpos == string::npos ? lwhat : lwhat.substr(0, fpos));
687  pair<XmlSubEls::iterator, XmlSubEls::iterator> result(children_->equal_range(nwhat));
688  while (result.first != result.second)
689  {
690  const XmlElement *ptr((*result.first++).second->find(lwhat, atag, aval, delim));
691  if (ptr)
692  return ptr;
693  }
694  }
695  }
696 
697  return nullptr;
698 }
699 
700 //-----------------------------------------------------------------------------------------
701 int XmlElement::find(const string& what, XmlSet& eset, const string *atag, const string *aval,
702  const char delim) const // find all matching entities
703 {
704  if (what.compare(0, 2, "//") == 0) // root based
705  return root_->find(what.substr(2), eset, atag, aval, delim);
706 
707  if (flags_ & nocase ? what % tag_ : what == tag_)
708  {
709  if (atag && aval && !findAttrByValue(*atag, *aval))
710  return 0;
711  eset.insert(this);
712  return static_cast<int>(eset.size());
713  }
714 
715  if (children_)
716  {
717  string lwhat(what);
718  string::size_type fpos(lwhat.find_first_of(delim));
719 
720  if (fpos != string::npos && lwhat.substr(0, fpos) == tag_)
721  {
722  lwhat.erase(0, fpos + 1);
723  fpos = lwhat.find_first_of(delim);
724  const string nwhat(fpos == string::npos ? lwhat : lwhat.substr(0, fpos));
725  pair<XmlSubEls::iterator, XmlSubEls::iterator> result(children_->equal_range(nwhat));
726  while (result.first != result.second)
727  (*result.first++).second->find(lwhat, eset, atag, aval, delim);
728  return static_cast<int>(eset.size());
729  }
730  }
731 
732  return 0;
733 }
734 
735 //-----------------------------------------------------------------------------------------
736 bool XmlElement::findAttrByValue(const string& what, const string& val) const
737 {
738  if (attrs_)
739  {
740  XmlAttrs::iterator itr(attrs_->find(what));
741  return itr != attrs_->end() && itr->second == val;
742  }
743 
744  return false;
745 }
746 
747 //-----------------------------------------------------------------------------------------
748 bool XmlElement::GetAttr(const string& what, string& target) const
749 {
750  if (attrs_)
751  {
752  XmlAttrs::iterator itr(attrs_->find(what));
753  if (itr != attrs_->end())
754  {
755  target = itr->second;
756  return true;
757  }
758  }
759 
760  return false;
761 }
762 
763 //-----------------------------------------------------------------------------------------
764 const string& XmlElement::InplaceXlate (string& what)
765 {
766  RegMatch match;
767  while (rCX_.SearchString(match, what, 2) == 2)
768  {
769  string whatv;
770  rCX_.SubExpr(match, what, whatv, 0, 1);
771  const auto sitr(stringtochar_.find(whatv));
772  rCX_.Replace(match, what, sitr == stringtochar_.cend() ? '?' : sitr->second); // not found character entity replaces string with '?'
773  }
774 
775  while (rCE_.SearchString(match, what, 2) == 2) // translate Numeric character references &#x12d; or &#12;
776  {
777  string whatv;
778  rCE_.SubExpr(match, what, whatv, 0, 1);
779  istringstream istr(whatv);
780  int value;
781  if (whatv[0] == 'x')
782  {
783  istr.ignore();
784  istr >> hex >> value;
785  }
786  else
787  istr >> dec >> value;
788  string oval;
789  if (value & 0xff00) // handle hi byte
790  oval += static_cast<char>(value >> 8 & 0xff);
791  oval += static_cast<char>(value & 0xff);
792  rCE_.Replace(match, what, oval);
793  }
794 
795  if (!(flags_ & noextensions))
796  {
797  if (rEn_.SearchString(match, what, 2) == 2) // environment var replacement ${XXX}
798  {
799  string whatv;
800  rEn_.SubExpr(match, what, whatv, 0, 1);
801  const char *gresult(getenv(whatv.c_str()));
802  if (gresult)
803  {
804  const string result(gresult);
805  if (!result.empty())
806  rEn_.Replace(match, what, result);
807  }
808  }
809 
810  if (rEv_.SearchString(match, what, 2) == 2) // evaluate shell command and replace with result !{XXX}
811  {
812  string whatv;
813  rEv_.SubExpr(match, what, whatv, 0, 1);
814  string result;
815  if (exec_cmd(whatv, result))
816  rEv_.Replace(match, what, result);
817  }
818  }
819 
820  return what;
821 }
822 
823 //-----------------------------------------------------------------------------------------
824 XmlElement *XmlElement::Factory(istream& ifs, const char *docpath)
825 {
826 #ifdef _MSC_VER
827  stringstream buffer;
828  buffer << ifs.rdbuf();
829  return ifs ? new XmlElement(buffer, 0, nullptr, 0, 0, docpath) : nullptr;
830 #else
831  return ifs ? new XmlElement(ifs, 0, nullptr, 0, 0, docpath) : nullptr;
832 #endif
833 }
834 
835 //-----------------------------------------------------------------------------------------
836 XmlElement *XmlElement::Factory(const string& fname)
837 {
838  ifstream ifs(fname.c_str());
839  return Factory(ifs, fname.c_str());
840 }
841 
842 //-----------------------------------------------------------------------------------------
static F8API XmlElement * Factory(std::istream &istr, const char *docpath=nullptr)
std::string inclusion_
Definition: xml.hpp:60
F8API int ParseAttrs(const std::string &attlst)
Definition: xml.cpp:542
XmlSet * ordchildren_
Set of all child elements in file order.
Definition: xml.hpp:91
static std::string & SubExpr(RegMatch &match, const std::string &source, std::string &target, const int offset=0, const int num=0)
Definition: f8utils.hpp:452
POSIX regex wrapper class.
Definition: f8utils.hpp:370
std::map< std::string, std::string > XmlAttrs
Definition: xml.hpp:75
F8API bool findAttrByValue(const std::string &what, const std::string &value) const
Definition: xml.cpp:736
XmlElement * root_
Definition: xml.hpp:57
static FIX8::RegExp rEv_
Definition: xml.hpp:55
XmlSubEls * children_
simple n-ary tree
Definition: xml.hpp:87
std::map< std::string, unsigned char > Str2Chr
Definition: xml.hpp:44
static const Str2Chr stringtochar_
XML entity char lookup.
Definition: xml.hpp:51
static FIX8::RegExp rIn_
Definition: xml.hpp:55
static XmlFlags flags_
Definition: xml.hpp:81
int depth_
Definition: xml.hpp:63
std::ostream & operator<<(std::ostream &os, const GroupBase &what)
Definition: message.hpp:1373
static std::string & Replace(RegMatch &match, std::string &source, const std::string &with, const int num=0)
Definition: f8utils.hpp:489
string spacer
Definition: f8c.cpp:96
std::multimap< std::string, XmlElement * > XmlSubEls
Definition: xml.hpp:85
F8API bool Insert(XmlElement *what)
Definition: xml.cpp:523
std::string * value_
Definition: xml.hpp:62
A simple xml parser with Xpath style lookup.
Definition: xml.hpp:48
XmlAttrs * attrs_
Definition: xml.hpp:76
static FIX8::RegExp rEn_
Definition: xml.hpp:55
virtual F8API ~XmlElement()
Dtor.
Definition: xml.cpp:655
std::set< const XmlElement *, EntityOrderComp > XmlSet
Definition: xml.hpp:74
static F8API const XmlSet emptyset_
Definition: xml.hpp:92
int SearchString(RegMatch &match, const std::string &source, const int subExpr, const int offset=0) const
Definition: f8utils.hpp:431
An invalid configuration parameter was passed.
F8API XmlElement(std::istream &ifs, int subidx, XmlElement *parent=nullptr, int txtline=0, int depth=0, const char *rootAttr=nullptr)
bool _was_include
Definition: xml.hpp:88
F8API bool GetAttr(const std::string &what, std::string &target) const
Definition: xml.cpp:748
std::string * decl_
Definition: xml.hpp:62
F8API const std::string & InplaceXlate(std::string &what)
Definition: xml.cpp:764
int txtline_
Definition: xml.hpp:63
A class to contain regex matches using RegExp.
Definition: f8utils.hpp:308
const std::string & GetTag() const
Definition: xml.hpp:292
static FIX8::RegExp rCE_
Definition: xml.hpp:55
static FIX8::RegExp rCX_
Definition: xml.hpp:55
std::string tag_
Definition: xml.hpp:62
int chldcnt_
Definition: xml.hpp:63
F8API const XmlElement * find(const std::string &what, const std::string *atag=nullptr, const std::string *aval=nullptr, const char delim='/') const
int errors_
Definition: xml.hpp:59
int line_
Definition: xml.hpp:59
static F8API const XmlAttrs emptyattrs_
Definition: xml.hpp:77
int incline_
Definition: xml.hpp:59