a bit of Makefile and string constants cleanup
[reactos.git] / reactos / subsys / system / explorer / utility / xmlstorage.cpp
1
2 //
3 // XML storage classes
4 //
5 // xmlstorage.cpp
6 //
7 // Copyright (c) 2004, 2005 Martin Fuchs <martin-fuchs@gmx.net>
8 //
9
10
11 /*
12
13 All rights reserved.
14
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are met:
17
18 * Redistributions of source code must retain the above copyright
19 notice, this list of conditions and the following disclaimer.
20 * Redistributions in binary form must reproduce the above copyright
21 notice, this list of conditions and the following disclaimer in
22 the documentation and/or other materials provided with the
23 distribution.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 POSSIBILITY OF SUCH DAMAGE.
36
37 */
38
39 #ifndef _NO_COMMENT
40 #define _NO_COMMENT // no #pragma comment(lib, ...) statements in .lib files
41 #endif
42
43 //#include "xmlstorage.h"
44 #include <precomp.h>
45
46
47 // work around GCC's wide string constant bug
48 #ifdef __GNUC__
49 const LPCXSSTR XMLStorage::XS_TRUE = XS_TRUE_STR;
50 const LPCXSSTR XMLStorage::XS_FALSE = XS_FALSE_STR;
51 const LPCXSSTR XMLStorage::XS_NUMBERFMT = XS_NUMBERFMT_STR;
52 #endif
53
54
55 namespace XMLStorage {
56
57
58 /// remove escape characters from zero terminated string
59 static std::string unescape(const char* s, char b='"', char e='"')
60 {
61 const char* end = s + strlen(s);
62
63 // if (*s == b)
64 // ++s;
65 //
66 // if (end>s && end[-1]==e)
67 // --end;
68
69 if (*s == b)
70 if (end>s && end[-1]==e)
71 ++s, --end;
72
73 return std::string(s, end-s);
74 }
75
76 /// remove escape characters from string with specified length
77 static std::string unescape(const char* s, int l, char b='"', char e='"')
78 {
79 const char* end = s + l;
80
81 // if (*s == b)
82 // ++s;
83 //
84 // if (end>s && end[-1]==e)
85 // --end;
86
87 if (*s == b)
88 if (end>s && end[-1]==e)
89 ++s, --end;
90
91 return std::string(s, end-s);
92 }
93
94
95 /// move XPath like to position in XML tree
96 bool XMLPos::go(const char* path)
97 {
98 XMLNode* node = _cur;
99
100 // Is this an absolute path?
101 if (*path == '/') {
102 node = _root;
103 ++path;
104 }
105
106 node = node->find_relative(path);
107
108 if (node) {
109 go_to(node);
110 return true;
111 } else
112 return false;
113 }
114
115 /// move XPath like to position in XML tree
116 bool const_XMLPos::go(const char* path)
117 {
118 const XMLNode* node = _cur;
119
120 // Is this an absolute path?
121 if (*path == '/') {
122 node = _root;
123 ++path;
124 }
125
126 node = node->find_relative(path);
127
128 if (node) {
129 go_to(node);
130 return true;
131 } else
132 return false;
133 }
134
135
136 const XMLNode* XMLNode::find_relative(const char* path) const
137 {
138 const XMLNode* node = this;
139
140 // parse relative path
141 while(*path) {
142 const char* slash = strchr(path, '/');
143 if (slash == path)
144 return NULL;
145
146 int l = slash? slash-path: strlen(path);
147 std::string comp(path, l);
148 path += l;
149
150 // look for [n] and [@attr_name="attr_value"] expressions in path components
151 const char* bracket = strchr(comp.c_str(), '[');
152 l = bracket? bracket-comp.c_str(): comp.length();
153 std::string child_name(comp.c_str(), l);
154 std::string attr_name, attr_value;
155
156 int n = 0;
157 if (bracket) {
158 std::string expr = unescape(bracket, '[', ']');
159 const char* p = expr.c_str();
160
161 n = atoi(p); // read index number
162
163 if (n)
164 n = n - 1; // convert into zero based index
165
166 const char* at = strchr(p, '@');
167
168 if (at) {
169 p = at + 1;
170 const char* equal = strchr(p, '=');
171
172 // read attribute name and value
173 if (equal) {
174 attr_name = unescape(p, equal-p);
175 attr_value = unescape(equal+1);
176 }
177 }
178 }
179
180 if (attr_name.empty())
181 // search n.th child node with specified name
182 node = node->find(child_name, n);
183 else
184 // search n.th child node with specified name and matching attribute value
185 node = node->find(child_name, attr_name, attr_value, n);
186
187 if (!node)
188 return NULL;
189
190 if (*path == '/')
191 ++path;
192 }
193
194 return node;
195 }
196
197 XMLNode* XMLNode::create_relative(const char* path)
198 {
199 XMLNode* node = this;
200
201 // parse relative path
202 while(*path) {
203 const char* slash = strchr(path, '/');
204 if (slash == path)
205 return NULL;
206
207 int l = slash? slash-path: strlen(path);
208 std::string comp(path, l);
209 path += l;
210
211 // look for [n] and [@attr_name="attr_value"] expressions in path components
212 const char* bracket = strchr(comp.c_str(), '[');
213 l = bracket? bracket-comp.c_str(): comp.length();
214 std::string child_name(comp.c_str(), l);
215 std::string attr_name, attr_value;
216
217 int n = 0;
218 if (bracket) {
219 std::string expr = unescape(bracket, '[', ']');
220 const char* p = expr.c_str();
221
222 n = atoi(p); // read index number
223
224 if (n)
225 n = n - 1; // convert into zero based index
226
227 const char* at = strchr(p, '@');
228
229 if (at) {
230 p = at + 1;
231 const char* equal = strchr(p, '=');
232
233 // read attribute name and value
234 if (equal) {
235 attr_name = unescape(p, equal-p);
236 attr_value = unescape(equal+1);
237 }
238 }
239 }
240
241 XMLNode* child;
242
243 if (attr_name.empty())
244 // search n.th child node with specified name
245 child = node->find(child_name, n);
246 else
247 // search n.th child node with specified name and matching attribute value
248 child = node->find(child_name, attr_name, attr_value, n);
249
250 if (!child) {
251 child = new XMLNode(child_name);
252 node->add_child(child);
253
254 if (!attr_name.empty())
255 (*node)[attr_name] = attr_value;
256 }
257
258 node = child;
259
260 if (*path == '/')
261 ++path;
262 }
263
264 return node;
265 }
266
267
268 /// read XML stream into XML tree below _pos
269 XML_Status XMLReaderBase::read()
270 {
271 XML_Status status = XML_STATUS_OK;
272
273 while(status == XML_STATUS_OK) {
274 char* buffer = (char*) XML_GetBuffer(_parser, BUFFER_LEN);
275
276 int l = read_buffer(buffer, BUFFER_LEN);
277 if (l < 0)
278 break;
279
280 status = XML_ParseBuffer(_parser, l, false);
281 }
282
283 if (status != XML_STATUS_ERROR)
284 status = XML_ParseBuffer(_parser, 0, true);
285
286 if (_pos->_children.empty())
287 _pos->_trailing.append(_content);
288 else
289 _pos->_children.back()->_trailing.append(_content);
290
291 _content.erase();
292
293 return status;
294 }
295
296
297 /// store XML version and encoding into XML reader
298 void XMLCALL XMLReaderBase::XML_XmlDeclHandler(void* userData, const XML_Char* version, const XML_Char* encoding, int standalone)
299 {
300 XMLReaderBase* pReader = (XMLReaderBase*) userData;
301
302 if (version)
303 pReader->_xml_version = version;
304
305 if (encoding)
306 pReader->_encoding = encoding;
307 }
308
309 /// notifications about XML start tag
310 void XMLCALL XMLReaderBase::XML_StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts)
311 {
312 XMLReaderBase* pReader = (XMLReaderBase*) userData;
313 XMLPos& pos = pReader->_pos;
314
315 // search for end of first line
316 const char* s = pReader->_content.c_str();
317 const char* p = s;
318 const char* e = p + pReader->_content.length();
319
320 for(; p<e; ++p)
321 if (*p == '\n') {
322 ++p;
323 break;
324 }
325
326 if (p != s)
327 if (pos->_children.empty()) { // no children in last node?
328 if (pReader->_last_tag == TAG_START)
329 pos->_content.append(s, p-s);
330 else if (pReader->_last_tag == TAG_END)
331 pos->_trailing.append(s, p-s);
332 // else TAG_NONE -> don't store white space in root node
333 } else
334 pos->_children.back()->_trailing.append(s, p-s);
335
336 std::string leading;
337
338 if (p != e)
339 leading.assign(p, e-p);
340
341 XMLNode* node = new XMLNode(String_from_XML_Char(name), leading);
342
343 pos.add_down(node);
344
345 while(*atts) {
346 const XML_Char* attr_name = *atts++;
347 const XML_Char* attr_value = *atts++;
348
349 (*node)[String_from_XML_Char(attr_name)] = String_from_XML_Char(attr_value);
350 }
351
352 pReader->_last_tag = TAG_START;
353 pReader->_content.erase();
354 }
355
356 /// notifications about XML end tag
357 void XMLCALL XMLReaderBase::XML_EndElementHandler(void* userData, const XML_Char* name)
358 {
359 XMLReaderBase* pReader = (XMLReaderBase*) userData;
360 XMLPos& pos = pReader->_pos;
361
362 // search for end of first line
363 const char* s = pReader->_content.c_str();
364 const char* p = s;
365 const char* e = p + pReader->_content.length();
366
367 for(; p<e; ++p)
368 if (*p == '\n') {
369 ++p;
370 break;
371 }
372
373 if (p != s)
374 if (pos->_children.empty()) // no children in current node?
375 pos->_content.append(s, p-s);
376 else
377 if (pReader->_last_tag == TAG_START)
378 pos->_content.append(s, p-s);
379 else
380 pos->_children.back()->_trailing.append(s, p-s);
381
382 if (p != e)
383 pos->_end_leading.assign(p, e-p);
384
385 pos.back();
386
387 pReader->_last_tag = TAG_END;
388 pReader->_content.erase();
389 }
390
391 /// store content, white space and comments
392 void XMLCALL XMLReaderBase::XML_DefaultHandler(void* userData, const XML_Char* s, int len)
393 {
394 XMLReaderBase* pReader = (XMLReaderBase*) userData;
395
396 pReader->_content.append(s, len);
397 }
398
399
400 /// return error strings for Expat errors
401 std::string XMLReaderBase::get_error_string() const
402 {
403 XML_Error error = XML_GetErrorCode(_parser);
404
405 switch(error) {
406 case XML_ERROR_NONE: return "XML_ERROR_NONE";
407 case XML_ERROR_NO_MEMORY: return "XML_ERROR_NO_MEMORY";
408 case XML_ERROR_SYNTAX: return "XML_ERROR_SYNTAX";
409 case XML_ERROR_NO_ELEMENTS: return "XML_ERROR_NO_ELEMENTS";
410 case XML_ERROR_INVALID_TOKEN: return "XML_ERROR_INVALID_TOKEN";
411 case XML_ERROR_UNCLOSED_TOKEN: return "XML_ERROR_UNCLOSED_TOKEN";
412 case XML_ERROR_PARTIAL_CHAR: return "XML_ERROR_PARTIAL_CHAR";
413 case XML_ERROR_TAG_MISMATCH: return "XML_ERROR_TAG_MISMATCH";
414 case XML_ERROR_DUPLICATE_ATTRIBUTE: return "XML_ERROR_DUPLICATE_ATTRIBUTE";
415 case XML_ERROR_JUNK_AFTER_DOC_ELEMENT: return "XML_ERROR_JUNK_AFTER_DOC_ELEMENT";
416 case XML_ERROR_PARAM_ENTITY_REF: return "XML_ERROR_PARAM_ENTITY_REF";
417 case XML_ERROR_UNDEFINED_ENTITY: return "XML_ERROR_UNDEFINED_ENTITY";
418 case XML_ERROR_RECURSIVE_ENTITY_REF: return "XML_ERROR_RECURSIVE_ENTITY_REF";
419 case XML_ERROR_ASYNC_ENTITY: return "XML_ERROR_ASYNC_ENTITY";
420 case XML_ERROR_BAD_CHAR_REF: return "XML_ERROR_BAD_CHAR_REF";
421 case XML_ERROR_BINARY_ENTITY_REF: return "XML_ERROR_BINARY_ENTITY_REF";
422 case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: return "XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF";
423 case XML_ERROR_MISPLACED_XML_PI: return "XML_ERROR_MISPLACED_XML_PI";
424 case XML_ERROR_UNKNOWN_ENCODING: return "XML_ERROR_UNKNOWN_ENCODING";
425 case XML_ERROR_INCORRECT_ENCODING: return "XML_ERROR_INCORRECT_ENCODING";
426 case XML_ERROR_UNCLOSED_CDATA_SECTION: return "XML_ERROR_UNCLOSED_CDATA_SECTION";
427 case XML_ERROR_EXTERNAL_ENTITY_HANDLING: return "XML_ERROR_EXTERNAL_ENTITY_HANDLING";
428 case XML_ERROR_NOT_STANDALONE: return "XML_ERROR_NOT_STANDALONE";
429 case XML_ERROR_UNEXPECTED_STATE: return "XML_ERROR_UNEXPECTED_STATE";
430 case XML_ERROR_ENTITY_DECLARED_IN_PE: return "XML_ERROR_ENTITY_DECLARED_IN_PE";
431 case XML_ERROR_FEATURE_REQUIRES_XML_DTD: return "XML_ERROR_FEATURE_REQUIRES_XML_DTD";
432 case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: return "XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING";
433 case XML_ERROR_UNBOUND_PREFIX: return "XML_ERROR_UNBOUND_PREFIX";
434 // EXPAT version >= 1.95.8
435 #if XML_MAJOR_VERSION>1 || (XML_MAJOR_VERSION==1 && XML_MINOR_VERSION>95) || (XML_MAJOR_VERSION==1 && XML_MINOR_VERSION==95 && XML_MICRO_VERSION>7)
436 case XML_ERROR_UNDECLARING_PREFIX: return "XML_ERROR_UNDECLARING_PREFIX";
437 case XML_ERROR_INCOMPLETE_PE: return "XML_ERROR_INCOMPLETE_PE";
438 case XML_ERROR_XML_DECL: return "XML_ERROR_XML_DECL";
439 case XML_ERROR_TEXT_DECL: return "XML_ERROR_TEXT_DECL";
440 case XML_ERROR_PUBLICID: return "XML_ERROR_PUBLICID";
441 case XML_ERROR_SUSPENDED: return "XML_ERROR_SUSPENDED";
442 case XML_ERROR_NOT_SUSPENDED: return "XML_ERROR_NOT_SUSPENDED";
443 case XML_ERROR_ABORTED: return "XML_ERROR_ABORTED";
444 case XML_ERROR_FINISHED: return "XML_ERROR_FINISHED";
445 case XML_ERROR_SUSPEND_PE: return "XML_ERROR_SUSPEND_PE";
446 //#endif
447 //#if XML_MAJOR_VERSION>=2
448 /* Added in 2.0. */
449 case XML_ERROR_RESERVED_PREFIX_XML: return "XML_ERROR_RESERVED_PREFIX_XML";
450 case XML_ERROR_RESERVED_PREFIX_XMLNS: return "XML_ERROR_RESERVED_PREFIX_XMLNS";
451 case XML_ERROR_RESERVED_NAMESPACE_URI: return "XML_ERROR_RESERVED_NAMESPACE_URI";
452 #endif
453 }
454
455 std::ostringstream out;
456
457 out << "XML parser error #" << error;
458
459 return out.str();
460 }
461
462
463 /// encode XML string literals
464 std::string EncodeXMLString(const XS_String& str)
465 {
466 LPCXSSTR s = str.c_str();
467 LPXSSTR buffer = (LPXSSTR)alloca(5*sizeof(XS_CHAR)*XS_len(s)); // worst case. "&amp;"
468 LPXSSTR o = buffer;
469
470 for(LPCXSSTR p=s; *p; ++p)
471 switch(*p) {
472 case '&':
473 *o++ = '&'; *o++ = 'a'; *o++ = 'm'; *o++ = 'p'; *o++ = ';';
474 break;
475
476 case '<':
477 *o++ = '&'; *o++ = 'l'; *o++ = 't'; *o++ = ';';
478 break;
479
480 case '>':
481 *o++ = '&'; *o++ = 'g'; *o++ = 't'; *o++ = ';';
482 break;
483
484 case '"':
485 *o++ = '&'; *o++ = 'q'; *o++ = 'u'; *o++ = 'o'; *o++ = 't'; *o++ = ';';
486 break;
487
488 case '\'':
489 *o++ = '&'; *o++ = 'a'; *o++ = 'p'; *o++ = 'o'; *o++ = 's'; *o++ = ';';
490 break;
491
492 default:
493 *o++ = *p;
494 }
495
496 #ifdef XS_STRING_UTF8
497 return XS_String(buffer, o-buffer);
498 #else
499 return get_utf8(buffer, o-buffer);
500 #endif
501 }
502
503 /// decode XML string literals
504 XS_String DecodeXMLString(const XS_String& str)
505 {
506 LPCXSSTR s = str.c_str();
507 LPXSSTR buffer = (LPXSSTR)alloca(sizeof(XS_CHAR)*XS_len(s));
508 LPXSSTR o = buffer;
509
510 for(LPCXSSTR p=s; *p; ++p)
511 if (*p == '&') {
512 if (!XS_nicmp(p+1, XS_TEXT("lt;"), 3)) {
513 *o++ = '<';
514 p += 3;
515 } else if (!XS_nicmp(p+1, XS_TEXT("gt;"), 3)) {
516 *o++ = '>';
517 p += 3;
518 } else if (!XS_nicmp(p+1, XS_TEXT("amp;"), 4)) {
519 *o++ = '&';
520 p += 4;
521 } else if (!XS_nicmp(p+1, XS_TEXT("quot;"), 5)) {
522 *o++ = '"';
523 p += 5;
524 } else if (!XS_nicmp(p+1, XS_TEXT("apos;"), 5)) {
525 *o++ = '\'';
526 p += 5;
527 } else
528 *o++ = *p;
529 } else
530 *o++ = *p;
531
532 return XS_String(buffer, o-buffer);
533 }
534
535
536 /// write node with children tree to output stream using original white space
537 void XMLNode::write_worker(std::ostream& out, int indent) const
538 {
539 out << _leading << '<' << EncodeXMLString(*this);
540
541 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
542 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
543
544 if (!_children.empty() || !_content.empty()) {
545 out << '>' << _content;
546
547 for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
548 (*it)->write_worker(out, indent+1);
549
550 out << _end_leading << "</" << EncodeXMLString(*this) << '>';
551 } else
552 out << "/>";
553
554 out << _trailing;
555 }
556
557
558 /// pretty print node with children tree to output stream
559 void XMLNode::pretty_write_worker(std::ostream& out, int indent) const
560 {
561 for(int i=indent; i--; )
562 out << XML_INDENT_SPACE;
563
564 out << '<' << EncodeXMLString(*this);
565
566 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
567 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
568
569 if (!_children.empty() || !_content.empty()) {
570 out << ">\n";
571
572 for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
573 (*it)->pretty_write_worker(out, indent+1);
574
575 for(int i=indent; i--; )
576 out << XML_INDENT_SPACE;
577
578 out << "</" << EncodeXMLString(*this) << ">\n";
579 } else
580 out << "/>\n";
581 }
582
583
584 /// write node with children tree to output stream using smart formating
585 void XMLNode::smart_write_worker(std::ostream& out, int indent) const
586 {
587 if (_leading.empty())
588 for(int i=indent; i--; )
589 out << XML_INDENT_SPACE;
590 else
591 out << _leading;
592
593 out << '<' << EncodeXMLString(*this);
594
595 for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
596 out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
597
598 if (_children.empty() && _content.empty())
599 out << "/>";
600 else {
601 out << '>';
602
603 if (_content.empty())
604 out << '\n';
605 else
606 out << _content;
607
608 Children::const_iterator it = _children.begin();
609
610 if (it != _children.end()) {
611 for(; it!=_children.end(); ++it)
612 (*it)->smart_write_worker(out, indent+1);
613
614 if (_end_leading.empty())
615 for(int i=indent; i--; )
616 out << XML_INDENT_SPACE;
617 else
618 out << _end_leading;
619 } else
620 out << _end_leading;
621
622 out << "</" << EncodeXMLString(*this) << '>';
623 }
624
625 if (_trailing.empty())
626 out << '\n';
627 else
628 out << _trailing;
629 }
630
631
632 } // namespace XMLStorage