CBMC
xml.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module:
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
9 #include "xml.h"
10 
11 #include <ostream>
12 
13 #include "exception_utils.h"
14 #include "string2int.h"
15 #include "structured_data.h"
16 
18 {
19  data.clear();
20  name.clear();
21  attributes.clear();
22  elements.clear();
23 }
24 
26 {
27  xml.data.swap(data);
29  xml.elements.swap(elements);
30  xml.name.swap(name);
31 }
32 
33 void xmlt::output(std::ostream &out, unsigned indent) const
34 {
35  // 'name' needs to be set, or we produce mal-formed
36  // XML.
37 
38  PRECONDITION(!name.empty());
39 
40  do_indent(out, indent);
41 
42  out << '<' << name;
43 
44  for(const auto &attribute : attributes)
45  {
46  // it.first needs to be non-empty
47  if(attribute.first.empty())
48  continue;
49  out << ' ' << attribute.first
50  << '=' << '"';
51  escape_attribute(attribute.second, out);
52  out << '"';
53  }
54 
55  if(elements.empty() && data.empty())
56  {
57  out << "/>" << "\n";
58  return;
59  }
60 
61  out << '>';
62 
63  if(elements.empty())
64  escape(data, out);
65  else
66  {
67  out << "\n";
68 
69  for(const auto &element : elements)
70  element.output(out, indent+2);
71 
72  do_indent(out, indent);
73  }
74 
75  out << '<' << '/' << name << '>' << "\n";
76 }
77 
79 void xmlt::escape(const std::string &s, std::ostream &out)
80 {
81  for(const auto ch : s)
82  {
83  switch(ch)
84  {
85  case '&':
86  out << "&amp;";
87  break;
88 
89  case '<':
90  out << "&lt;";
91  break;
92 
93  case '>':
94  out << "&gt;";
95  break;
96 
97  case '\r':
98  break; // drop!
99 
100  case '\n':
101  out << '\n';
102  break;
103 
104  case 0x9: // TAB
105  case 0x7F: // DEL
106  out << "&#" << std::to_string((unsigned char)ch) << ';';
107  break;
108 
109  default:
110  DATA_INVARIANT(
111  static_cast<unsigned char>(ch) >= 32u,
112  "XML does not support escaping non-printable character " +
113  std::to_string((unsigned char)ch));
114  out << ch;
115  }
116  }
117 }
118 
121 void xmlt::escape_attribute(const std::string &s, std::ostream &out)
122 {
123  for(const auto ch : s)
124  {
125  switch(ch)
126  {
127  case '&':
128  out << "&amp;";
129  break;
130 
131  case '<':
132  out << "&lt;";
133  break;
134 
135  case '>':
136  out << "&gt;";
137  break;
138 
139  case '"':
140  out << "&quot;";
141  break;
142 
143  case 0x9: // TAB
144  case 0xA: // LF
145  case 0xD: // CR
146  case 0x7F: // DEL
147  out << "&#" << std::to_string((unsigned char)ch) << ';';
148  break;
149 
150  default:
151  DATA_INVARIANT(
152  static_cast<unsigned char>(ch) >= 32u,
153  "XML does not support escaping non-printable character " +
154  std::to_string((unsigned char)ch));
155  out << ch;
156  }
157  }
158 }
159 
160 bool xmlt::is_printable_xml(const std::string &s)
161 {
162  for(const auto ch : s)
163  {
164  if(ch < 0x20 && ch != 0x9 && ch != 0xA && ch != 0xD)
165  return false;
166  }
167 
168  return true;
169 }
170 
171 void xmlt::do_indent(std::ostream &out, unsigned indent)
172 {
173  out << std::string(indent, ' ');
174 }
175 
176 xmlt::elementst::const_iterator xmlt::find(const std::string &key) const
177 {
178  for(elementst::const_iterator it=elements.begin();
179  it!=elements.end();
180  it++)
181  if(it->name == key)
182  return it;
183 
184  return elements.end();
185 }
186 
187 xmlt::elementst::iterator xmlt::find(const std::string &key)
188 {
189  for(elementst::iterator it=elements.begin();
190  it!=elements.end();
191  it++)
192  if(it->name == key)
193  return it;
194 
195  return elements.end();
196 }
197 
198 void xmlt::set_attribute(
199  const std::string &attribute,
200  unsigned value)
201 {
202  set_attribute(attribute, std::to_string(value));
203 }
204 
205 void xmlt::set_attribute(
206  const std::string &attribute,
207  unsigned long value)
208 {
209  set_attribute(attribute, std::to_string(value));
210 }
211 
212 void xmlt::set_attribute(
213  const std::string &attribute,
214  unsigned long long value)
215 {
216  set_attribute(attribute, std::to_string(value));
217 }
218 
219 void xmlt::set_attribute(
220  const std::string &attribute,
221  const std::string &value)
222 {
223  if((value[0]=='\"' && value[value.size()-1]=='\"') ||
224  (value[0]=='\'' && value[value.size()-1]=='\''))
225  {
226  attributes[attribute]=value.substr(1, value.size()-2);
227  }
228  else
229  {
230  attributes[attribute]=value;
231  }
232 }
233 
237 std::string xmlt::unescape(const std::string &str)
238 {
239  std::string result;
240 
241  result.reserve(str.size());
242 
243  for(std::string::const_iterator it=str.begin();
244  it!=str.end();
245  it++)
246  {
247  if(*it=='&')
248  {
249  std::string tmp;
250  it++;
251 
252  while(it!=str.end() && *it!=';')
253  tmp+=*it++;
254 
255  if(tmp=="gt")
256  result+='>';
257  else if(tmp=="lt")
258  result+='<';
259  else if(tmp=="amp")
260  result+='&';
261  else if(tmp[0]=='#' && tmp[1]!='x')
262  {
263  char c=unsafe_string2int(tmp.substr(1, tmp.size()-1));
264  result+=c;
265  }
266  else
267  throw deserialization_exceptiont("unknown XML escape code: " + tmp);
268  }
269  else
270  result+=*it;
271  }
272 
273  return result;
274 }
275 bool operator==(const xmlt &a, const xmlt &b)
276 {
277  return a.name == b.name && a.data == b.data && a.elements == b.elements &&
278  a.attributes == b.attributes;
279 }
280 bool operator!=(const xmlt &a, const xmlt &b)
281 {
282  return !(a == b);
283 }
284 
285 xmlt xml_node(const std::pair<labelt, structured_data_entryt> &entry)
286 {
287  const labelt &label = entry.first;
288  const structured_data_entryt &data = entry.second;
289  xmlt output_data{label.kebab_case()};
290  if(data.is_leaf())
291  {
292  output_data.data = data.leaf_data();
293  }
294  else
295  {
296  const auto &children = data.children();
297  output_data.elements =
298  make_range(children).map(xml_node).collect<std::list<xmlt>>();
299  }
300  return output_data;
301 }
302 
303 xmlt to_xml(const structured_datat &data)
304 {
305  if(data.data().size() == 0)
306  return xmlt{};
307  if(data.data().size() == 1)
308  {
309  return xml_node(*data.data().begin());
310  }
311  else
312  {
313  xmlt root{"root"};
314  root.elements =
315  make_range(data.data()).map(xml_node).collect<std::list<xmlt>>();
316  return root;
317  }
318 }
Definition: xml.h:21
void swap(xmlt &xml)
Definition: xml.cpp:25
attributest attributes
Definition: xml.h:41
void clear()
Definition: xml.cpp:17
elementst elements
Definition: xml.h:42
std::string data
Definition: xml.h:39
void output(std::ostream &out, unsigned indent=0) const
Definition: xml.cpp:33
static void escape(const std::string &s, std::ostream &out)
escaping for XML elements
Definition: xml.cpp:79
static void escape_attribute(const std::string &s, std::ostream &out)
escaping for XML attributes, assuming that double quotes " are used consistently, not single quotes
Definition: xml.cpp:121
std::string name
Definition: xml.h:39
static void do_indent(std::ostream &out, unsigned indent)
Definition: xml.cpp:171
static int8_t r
Definition: irep_hash.h:60
xmlt xml(const irep_idt &property_id, const property_infot &property_info)
Definition: properties.cpp:110
#define PRECONDITION(CONDITION)
Definition: invariant.h:463