1 
2  //
3  // XML storage C++ classes version 1.3
4  //
5  // Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Martin Fuchs <martin-fuchs@gmx.net>
6  //
7 
8  /// \file xmlstorage.cpp
9  /// XMLStorage implementation file
10 
11 
12 /*
13 
14   All rights reserved.
15 
16   Redistribution and use in source and binary forms, with or without
17   modification, are permitted provided that the following conditions are met:
18 
19   * Redistributions of source code must retain the above copyright
20 	notice, this list of conditions and the following disclaimer.
21   * Redistributions in binary form must reproduce the above copyright
22 	notice, this list of conditions and the following disclaimer in
23 	the documentation and/or other materials provided with the
24 	distribution.
25 
26   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36   POSSIBILITY OF SUCH DAMAGE.
37 
38 */
39 
40 #include <precomp.h>
41 
42 #ifndef XS_NO_COMMENT
43 #define XS_NO_COMMENT	// no #pragma comment(lib, ...) statements in .lib files to enable static linking
44 #endif
45 
46 //#include "xmlstorage.h"
47 
48 
49 namespace XMLStorage {
50 
51 
52  // work around GCC's wide string constant bug
53 #ifdef __GNUC__
54 const LPCXSSTR XS_EMPTY = XS_EMPTY_STR;
55 const LPCXSSTR XS_TRUE = XS_TRUE_STR;
56 const LPCXSSTR XS_FALSE = XS_FALSE_STR;
57 const LPCXSSTR XS_INTFMT = XS_INTFMT_STR;
58 const LPCXSSTR XS_FLOATFMT = XS_FLOATFMT_STR;
59 #endif
60 
61 const XS_String XS_KEY = XS_KEY_STR;
62 const XS_String XS_VALUE = XS_VALUE_STR;
63 const XS_String XS_PROPERTY = XS_PROPERTY_STR;
64 
65 
66  /// remove escape characters from zero terminated string
67 static std::string unescape(const char* s, char b, char e)
68 {
69 	const char* end = s + strlen(s);
70 
71 //	if (*s == b)
72 //		++s;
73 //
74 //	if (end>s && end[-1]==e)
75 //		--end;
76 
77 	if (*s == b)
78 		if (end>s && end[-1]==e)
79 			++s, --end;
80 
81 	return std::string(s, end-s);
82 }
83 
84 inline std::string unescape(const char* s)
85 {
86 	return unescape(s, '"', '"');
87 }
88 
89  /// remove escape characters from string with specified length
90 static std::string unescape(const char* s, size_t l, char b, char e)
91 {
92 	const char* end = s + l;
93 
94 //	if (*s == b)
95 //		++s;
96 //
97 //	if (end>s && end[-1]==e)
98 //		--end;
99 
100 	if (*s == b)
101 		if (end>s && end[-1]==e)
102 			++s, --end;
103 
104 	return std::string(s, end-s);
105 }
106 
107 inline std::string unescape(const char* s, size_t l)
108 {
109 	return unescape(s, l, '"', '"');
110 }
111 
112 
113  /// move to the position defined by xpath in XML tree
114 bool XMLPos::go(const XPath& xpath)
115 {
116 	XMLNode* node = xpath._absolute? _root: _cur;
117 
118 	node = node->find_relative(xpath);
119 
120 	if (node) {
121 		go_to(node);
122 		return true;
123 	} else
124 		return false;
125 }
126 
127  /// move to the position defined by xpath in XML tree
128 bool const_XMLPos::go(const XPath& xpath)
129 {
130 	const XMLNode* node = xpath._absolute? _root: _cur;
131 
132 	node = node->find_relative(xpath);
133 
134 	if (node) {
135 		go_to(node);
136 		return true;
137 	} else
138 		return false;
139 }
140 
141 
142 const char* XPathElement::parse(const char* path)
143 {
144 	const char* slash = strchr(path, '/');
145 	if (slash == path)
146 		return NULL;
147 
148 	size_t l = slash? slash-path: strlen(path);
149 	std::string comp(path, l);
150 	path += l;
151 
152 	 // look for [n] and [@attr_name="attr_value"] expressions in path components
153 	const char* bracket = strchr(comp.c_str(), '[');
154 	l = bracket? bracket-comp.c_str(): comp.length();
155 	_child_name.assign(comp.c_str(), l);
156 
157 	int n = 0;
158 	if (bracket) {
159 		std::string expr = unescape(bracket, '[', ']');
160 		const char* p = expr.c_str();
161 
162 		n = atoi(p);	// read index number
163 
164 		if (n)
165 			_child_idx = n - 1;	// convert into zero based index
166 
167 		const char* at = strchr(p, '@');
168 
169 		if (at) {
170 			p = at + 1;
171 			const char* equal = strchr(p, '=');
172 
173 			 // read attribute name and value
174 			if (equal) {
175 				_attr_name = unescape(p, equal-p);
176 				_attr_value = unescape(equal+1);
177 			}
178 		}
179 	}
180 
181 	return path;
182 }
183 
184 XMLNode* XPathElement::find(XMLNode* node) const
185 {
186 	int n = 0;
187 
188 	for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it)
189 		if (matches(**it, n))
190 			return *it;
191 
192 	return NULL;
193 }
194 
195 const XMLNode* XPathElement::const_find(const XMLNode* node) const
196 {
197 	int n = 0;
198 
199 	for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it)
200 		if (matches(**it, n))
201 			return *it;
202 
203 	return NULL;
204 }
205 
206 bool XPathElement::matches(const XMLNode& node, int& n) const
207 {
208 	if (node != _child_name)
209 		if (_child_name != XS_TEXT("*"))	// use asterisk as wildcard
210 			return false;
211 
212 	if (!_attr_name.empty())
213 		if (node.get(_attr_name) != _attr_value)
214 			return false;
215 
216 	if (_child_idx == -1)
217 		return true;
218 	else if (n++ == _child_idx)
219 		return true;
220 	else
221 		return false;
222 }
223 
224 
225 void XPath::init(const char* path)
226 {
227 	 // Is this an absolute path?
228 	if (*path == '/') {
229 		_absolute = true;
230 		++path;
231 	} else
232 		_absolute = false;
233 
234 	 // parse path
235 	while(*path) {
236 		XPathElement elem;
237 
238 		path = elem.parse(path);
239 
240 		if (!path)
241 			break;
242 
243 		if (*path == '/')
244 			++path;
245 
246 		push_back(elem);
247 	}
248 }
249 
250 
251 const XMLNode* XMLNode::find_relative(const XPath& xpath) const
252 {
253 	const XMLNode* node = this;
254 
255 	for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
256 		node = it->const_find(node);
257 
258 		if (!node)
259 			return NULL;
260 	}
261 
262 	return node;
263 }
264 
265 XMLNode* XMLNode::find_relative(const XPath& xpath)
266 {
267 	XMLNode* node = this;
268 
269 	for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
270 		node = it->find(node);
271 
272 		if (!node)
273 			return NULL;
274 	}
275 
276 	return node;
277 }
278 
279 XMLNode* XMLNode::create_relative(const XPath& xpath)
280 {
281 	XMLNode* node = this;
282 
283 	for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
284 		XMLNode* child = it->find(node);
285 
286 		if (!child) {
287 			child = new XMLNode(it->_child_name);
288 			node->add_child(child);
289 
290 			if (!it->_attr_name.empty())
291 				(*this)[it->_attr_name] = it->_attr_value;
292 		}
293 
294 		node = child;
295 	}
296 
297 	return node;
298 }
299 
300  /// count the nodes matching the given relative XPath expression
301 int XMLNode::count(XPath::const_iterator from, const XPath::const_iterator& to) const
302 {
303 	const XPathElement& elem = *from++;
304 	int cnt = 0;
305 	int n = 0;
306 
307 	for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
308 		if (elem.matches(**it, n)) {
309 			if (from != to)
310 				 // iterate deeper
311 				cnt += (*it)->count(from, to);
312 			else
313 				 // increment match counter
314 				++cnt;
315 		}
316 
317 	return cnt;
318 }
319 
320  /// copy matching tree nodes using the given XPath filter expression
321 bool XMLNode::filter(const XPath& xpath, XMLNode& target) const
322 {
323 	XMLNode* ret = filter(xpath.begin(), xpath.end());
324 
325 	if (ret) {
326 		 // move returned nodes to target node
327 		target._children.move(ret->_children);
328 		target._attributes = ret->_attributes;
329 
330 		delete ret;
331 
332 		return true;
333 	} else
334 		return false;
335 }
336 
337  /// create a new node tree using the given XPath filter expression
338 XMLNode* XMLNode::filter(XPath::const_iterator from, const XPath::const_iterator& to) const
339 {
340 	XMLNode* copy = NULL;
341 
342 	const XPathElement& elem = *from++;
343 	int cnt = 0;
344 	int n = 0;
345 
346 	for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
347 		if (elem.matches(**it, n)) {
348 			if (!copy)
349 				copy = new XMLNode(*this, XMLNode::COPY_NOCHILDREN);
350 
351 			if (from != to) {
352 				XMLNode* ret = (*it)->filter(from, to);
353 
354 				if (ret) {
355 					copy->add_child(ret);
356 					++cnt;
357 				}
358 			} else {
359 				copy->add_child(new XMLNode(**it, XMLNode::COPY_NOCHILDREN));
360 				++cnt;
361 			}
362 		}
363 
364 	if (cnt > 0) {
365 		return copy;
366 	} else {
367 		delete copy;
368 		return NULL;
369 	}
370 }
371 
372 
373  /// encode XML string literals
374 std::string EncodeXMLString(const XS_String& str, bool cdata)
375 {
376 	LPCXSSTR s = str.c_str();
377 	size_t l = XS_len(s);
378 
379 	if (cdata) {
380 		 // encode the whole string in a CDATA section
381 		std::string ret = CDATA_START;
382 
383 #ifdef XS_STRING_UTF8
384 		ret += str;
385 #else
386 		ret += get_utf8(str);
387 #endif
388 
389 		ret += CDATA_END;
390 
391 		return ret;
392 	} else if (l <= BUFFER_LEN) {
393 		LPXSSTR buffer = (LPXSSTR)alloca(6*sizeof(XS_CHAR)*XS_len(s));	// worst case "&quot;" / "&apos;"
394 		LPXSSTR o = buffer;
395 
396 		for(LPCXSSTR p=s; *p; ++p)
397 			switch(*p) {
398 			  case '&':
399 				*o++ = '&';	*o++ = 'a';	*o++ = 'm';	*o++ = 'p';	*o++ = ';';				// "&amp;"
400 				break;
401 
402 			  case '<':
403 				*o++ = '&';	*o++ = 'l'; *o++ = 't';	*o++ = ';';							// "&lt;"
404 				break;
405 
406 			  case '>':
407 				*o++ = '&';	*o++ = 'g'; *o++ = 't';	*o++ = ';';							// "&gt;"
408 				break;
409 
410 			  case '"':
411 				*o++ = '&';	*o++ = 'q'; *o++ = 'u'; *o++ = 'o'; *o++ = 't';	*o++ = ';';	// "&quot;"
412 				break;
413 
414 			  case '\'':
415 				*o++ = '&';	*o++ = 'a'; *o++ = 'p'; *o++ = 'o'; *o++ = 's';	*o++ = ';';	// "&apos;"
416 				break;
417 
418 			  default:
419 				if ((unsigned)*p<0x20 && *p!='\t' && *p!='\r' && *p!='\n') {
420 					char b[16];
421 					sprintf(b, "&#%d;", (unsigned)*p);
422 					for(const char*q=b; *q; )
423 						*o++ = *q++;
424 				} else
425 					*o++ = *p;
426 			}
427 
428 #ifdef XS_STRING_UTF8
429 		return XS_String(buffer, o-buffer);
430 #else
431 		return get_utf8(buffer, o-buffer);
432 #endif
433 	} else { // l > BUFFER_LEN
434 		 // alternative code for larger strings using ostringstream
435 		 // and avoiding to use alloca() for preallocated memory
436 		fast_ostringstream out;
437 
438 		LPCXSSTR s = str.c_str();
439 
440 		for(LPCXSSTR p=s; *p; ++p)
441 			switch(*p) {
442 			  case '&':
443 				out << "&amp;";
444 				break;
445 
446 			  case '<':
447 				out << "&lt;";
448 				break;
449 
450 			  case '>':
451 				out << "&gt;";
452 				break;
453 
454 			  case '"':
455 				out << "&quot;";
456 				break;
457 
458 			  case '\'':
459 				out << "&apos;";
460 				break;
461 
462 			  default:
463 				if ((unsigned)*p<0x20 && *p!='\t' && *p!='\r' && *p!='\n')
464 					out << "&#" << (unsigned)*p << ";";
465 				else
466 					out << *p;
467 			}
468 
469 #ifdef XS_STRING_UTF8
470 		return XS_String(out.str());
471 #else
472 		return get_utf8(out.str());
473 #endif
474 	}
475 }
476 
477  /// decode XML string literals
478 XS_String DecodeXMLString(const std::string& str)
479 {
480 #ifdef XS_STRING_UTF8
481 	const XS_String& str_utf8 = str;
482 #else
483 	XS_String str_utf8;
484 	assign_utf8(str_utf8, str.c_str(), str.length());
485 #endif
486 
487 	LPCXSSTR s = str_utf8.c_str();
488 	LPXSSTR buffer = (LPXSSTR)alloca(sizeof(XS_CHAR)*XS_len(s));
489 	LPXSSTR o = buffer;
490 
491 	for(LPCXSSTR p=s; *p; ++p)
492 		if (*p == '&') {
493 			if (!XS_nicmp(p+1, XS_TEXT("lt;"), 3)) {
494 				*o++ = '<';
495 				p += 3;
496 			} else if (!XS_nicmp(p+1, XS_TEXT("gt;"), 3)) {
497 				*o++ = '>';
498 				p += 3;
499 			} else if (!XS_nicmp(p+1, XS_TEXT("amp;"), 4)) {
500 				*o++ = '&';
501 				p += 4;
502 			} else if (!XS_nicmp(p+1, XS_TEXT("quot;"), 5)) {
503 				*o++ = '"';
504 				p += 5;
505 			} else if (!XS_nicmp(p+1, XS_TEXT("apos;"), 5)) {
506 				*o++ = '\'';
507 				p += 5;
508 			} else	//@@ maybe decode "&#xx;" special characters
509 				*o++ = *p;
510 		} else if (*p=='<' && !XS_nicmp(p+1,XS_TEXT("![CDATA["),8)) {
511 			LPCXSSTR e = XS_strstr(p+9, XS_TEXT(CDATA_END));
512 			if (e) {
513 				p += 9;
514 				size_t l = e - p;
515 				memcpy(o, p, l);
516 				o += l;
517 				p = e + 2;
518 			} else
519 				*o++ = *p;
520 		} else
521 			*o++ = *p;
522 
523 	return XS_String(buffer, o-buffer);
524 }
525 
526 
527  /// write node with children tree to output stream using original white space
528 void XMLNode::original_write_worker(std::ostream& out) const
529 {
530 	out << _leading << '<' << EncodeXMLString(*this);
531 
532 	for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
533 		out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
534 
535 	if (!_children.empty() || !_content.empty()) {
536 		out << '>';
537 
538 		if (_cdata_content)
539 			out << CDATA_START << _content << CDATA_END;
540 		else
541 			out << _content;
542 
543 		for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
544 			(*it)->original_write_worker(out);
545 
546 		out << _end_leading << "</" << EncodeXMLString(*this) << '>';
547 	} else
548 		out << "/>";
549 
550 	out << _trailing;
551 }
552 
553 
554  /// print node without any white space
555 void XMLNode::plain_write_worker(std::ostream& out) const
556 {
557 	out << '<' << EncodeXMLString(*this);
558 
559 	for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
560 		out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
561 
562 	 // strip leading white space from content
563 	const char* content = _content.c_str();
564 	while(isspace((unsigned char)*content)) ++content;
565 
566 	if (!_children.empty() || *content) {
567 		out << ">" << content;
568 
569 		for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
570 			(*it)->plain_write_worker(out);
571 
572 		out << "</" << EncodeXMLString(*this) << ">";
573 	} else
574 		out << "/>";
575 }
576 
577 
578  /// pretty print node with children tree to output stream
579 void XMLNode::pretty_write_worker(std::ostream& out, const XMLFormat& format, int indent) const
580 {
581 	for(int i=indent; i--; )
582 		out << XML_INDENT_SPACE;
583 
584 	out << '<' << EncodeXMLString(*this);
585 
586 	for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
587 		out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
588 
589 	 // strip leading white space from content
590 	const char* content = _content.c_str();
591 	while(isspace((unsigned char)*content)) ++content;
592 
593 	if (!_children.empty() || *content) {
594 		out << '>' << content;
595 
596 		if (!_children.empty())
597 			out << format._endl;
598 
599 		for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
600 			(*it)->pretty_write_worker(out, format, indent+1);
601 
602 		for(int i=indent; i--; )
603 			out << XML_INDENT_SPACE;
604 
605 		out << "</" << EncodeXMLString(*this) << '>' << format._endl;
606 	} else
607 		out << "/>" << format._endl;
608 }
609 
610 
611  /// write node with children tree to output stream using smart formating
612 void XMLNode::smart_write_worker(std::ostream& out, const XMLFormat& format, int indent) const
613 {
614 	 // strip the first line feed from _leading
615 	const char* leading = _leading.c_str();
616 	if (*leading == '\n') ++leading;
617 
618 	if (!*leading)
619 		for(int i=indent; i--; )
620 			out << XML_INDENT_SPACE;
621 	else
622 		out << leading;
623 
624 	out << '<' << EncodeXMLString(*this);
625 
626 	for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
627 		out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
628 
629 	 // strip leading white space from content
630 	const char* content = _content.c_str();
631 	while(isspace((unsigned char)*content)) ++content;
632 
633 	if (_children.empty() && !*content)
634 		out << "/>";
635 	else {
636 		out << '>';
637 
638 		if (_cdata_content)
639 			out << CDATA_START << _content << CDATA_END;
640 		else if (!*content)
641 			out << format._endl;
642 		else
643 			out << content;
644 
645 		Children::const_iterator it = _children.begin();
646 
647 		if (it != _children.end()) {
648 			for(; it!=_children.end(); ++it)
649 				(*it)->smart_write_worker(out, format, indent+1);
650 
651 			 // strip the first line feed from _end_leading
652 			const char* end_leading = _end_leading.c_str();
653 			if (*end_leading == '\n') ++end_leading;
654 
655 			if (!*end_leading)
656 				for(int i=indent; i--; )
657 					out << XML_INDENT_SPACE;
658 			else
659 				out << end_leading;
660 		} else
661 			out << _end_leading;
662 
663 		out << "</" << EncodeXMLString(*this) << '>';
664 	}
665 
666 	if (_trailing.empty())
667 		out << format._endl;
668 	else
669 		out << _trailing;
670 }
671 
672 
673 std::ostream& operator<<(std::ostream& out, const XMLError& err)
674 {
675 	out << err._systemId << "(" << err._line << ") [column " << err._column << "] : "
676 		<< err._message;
677 
678 	return out;
679 }
680 
681 
682 const char* get_xmlsym_end_utf8(const char* p)
683 {
684 	for(; *p; ++p) {
685 		char c = *p;
686 
687 		// NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
688 		if (c == '\xC3')	// UTF-8 escape character
689 			++p;	//TODO only continue on umlaut characters
690 		else if (!isalnum(c) && c!='.' && c!='-' && c!='_' && c!=':')
691 			break;
692 	}
693 
694 	return p;
695 }
696 
697 
698 void DocType::parse(const char* p)
699 {
700 	while(isspace((unsigned char)*p)) ++p;
701 
702 	const char* start = p;
703 	p = get_xmlsym_end_utf8(p);
704 	_name.assign(start, p-start);
705 
706 	while(isspace((unsigned char)*p)) ++p;
707 
708 	start = p;
709 	p = get_xmlsym_end_utf8(p);
710 	std::string keyword(p, p-start);	// "PUBLIC" or "SYSTEM"
711 
712 	while(isspace((unsigned char)*p)) ++p;
713 
714 	if (*p=='"' || *p=='\'') {
715 		char delim = *p;
716 
717 		start = ++p;
718 		while(*p && *p!=delim) ++p;
719 
720 		if (*p == delim)
721 			_public.assign(start, p++-start);
722 	} else
723 		_public.erase();
724 
725 	while(isspace((unsigned char)*p)) ++p;
726 
727 	if (*p=='"' || *p=='\'') {
728 		char delim = *p;
729 
730 		start = ++p;
731 		while(*p && *p!=delim) ++p;
732 
733 		if (*p == delim)
734 			_system.assign(start, p++-start);
735 	} else
736 		_system.erase();
737 }
738 
739 
740 void XMLFormat::print_header(std::ostream& out, bool lf) const
741 {
742 	out << "<?xml version=\"" << _version << "\" encoding=\"" << _encoding << "\"";
743 
744 	if (_standalone != -1)
745 		out << " standalone=\"yes\"";
746 
747 	out << "?>";
748 
749 	if (lf)
750 		out << _endl;
751 
752 	if (!_doctype.empty()) {
753 		out << "<!DOCTYPE " << _doctype._name;
754 
755 		if (!_doctype._public.empty()) {
756 			out << " PUBLIC \"" << _doctype._public << '"';
757 
758 			if (lf)
759 				out << _endl;
760 
761 			out << " \"" << _doctype._system << '"';
762 		} else if (!_doctype._system.empty())
763 			out << " SYSTEM \"" << _doctype._system << '"';
764 
765 		out << "?>";
766 
767 		if (lf)
768 			out << _endl;
769 	}
770 
771 	for(StyleSheetList::const_iterator it=_stylesheets.begin(); it!=_stylesheets.end(); ++it) {
772 		it->print(out);
773 
774 		if (lf)
775 			out << _endl;
776 	}
777 
778 /*	if (!_additional.empty()) {
779 		out << _additional;
780 
781 		if (lf)
782 			out << _endl;
783 	} */
784 }
785 
786 void StyleSheet::print(std::ostream& out) const
787 {
788 	out << "<?xml-stylesheet"
789 			" href=\"" << _href << "\""
790 			" type=\"" << _type << "\"";
791 
792 	if (!_title.empty())
793 		out << " title=\"" << _title << "\"";
794 
795 	if (!_media.empty())
796 		out << " media=\"" << _media << "\"";
797 
798 	if (!_charset.empty())
799 		out << " charset=\"" << _charset << "\"";
800 
801 	if (_alternate)
802 		out << " alternate=\"yes\"";
803 
804 	out << "?>";
805 }
806 
807 
808  /// return formated error message
809 std::string XMLError::str() const
810 {
811 	std::ostringstream out;
812 
813 	out << *this;
814 
815 	return out.str();
816 }
817 
818 
819  /// return merged error strings
820 XS_String XMLErrorList::str() const
821 {
822 	std::ostringstream out;
823 
824 	for(const_iterator it=begin(); it!=end(); ++it)
825 		out << *it << std::endl;
826 
827 	return out.str();
828 }
829 
830 
831 void XMLReaderBase::finish_read()
832 {
833 	if (_pos->_children.empty())
834 		_pos->_trailing.append(_content);
835 	else
836 		_pos->_children.back()->_trailing.append(_content);
837 
838 	_content.erase();
839 }
840 
841 
842  /// store XML version and encoding into XML reader
843 void XMLReaderBase::XmlDeclHandler(const char* version, const char* encoding, int standalone)
844 {
845 	if (version)
846 		_format._version = version;
847 
848 	if (encoding)
849 		_format._encoding = encoding;
850 
851 	_format._standalone = standalone;
852 }
853 
854 
855  /// notifications about XML start tag
856 void XMLReaderBase::StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attributes)
857 {
858 	const char* s = _content.c_str();
859 	const char* e = s + _content.length();
860 	const char* p = s;
861 
862 	 // search for content end leaving only white space for leading
863 	for(p=e; p>s; --p)
864 		if (!isspace((unsigned char)p[-1]))
865 			break;
866 
867 	if (p != s) {
868 		if (_pos->_children.empty()) {	// no children in last node?
869 			if (_last_tag == TAG_START)
870 				_pos->_content.append(s, p-s);
871 			else if (_last_tag == TAG_END)
872 				_pos->_trailing.append(s, p-s);
873 			else // TAG_NONE at root node
874 				p = s;
875 		} else
876 			_pos->_children.back()->_trailing.append(s, p-s);
877 	}
878 
879 	std::string leading;
880 
881 	if (p != e)
882 		leading.assign(p, e-p);
883 
884 	XMLNode* node = new XMLNode(name, leading);
885 
886 	_pos.add_down(node);
887 
888 #ifdef XMLNODE_LOCATION
889 	node->_location = get_location();
890 #endif
891 
892 	node->_attributes = attributes;
893 
894 	_last_tag = TAG_START;
895 	_content.erase();
896 }
897 
898  /// notifications about XML end tag
899 void XMLReaderBase::EndElementHandler()
900 {
901 	const char* s = _content.c_str();
902 	const char* e = s + _content.length();
903 	const char* p;
904 
905 	if (!strncmp(s,CDATA_START,9) && !strncmp(e-3,CDATA_END,3)) {
906 		s += 9;
907 		p = (e-=3);
908 
909 		_pos->_cdata_content = true;
910 	} else {
911 		 // search for content end leaving only white space for _end_leading
912 		for(p=e; p>s; --p)
913 			if (!isspace((unsigned char)p[-1]))
914 				break;
915 
916 		_pos->_cdata_content = false;
917 	}
918 
919 	if (p != s) {
920 		if (_pos->_children.empty())	// no children in current node?
921 			_pos->_content.append(s, p-s);
922 		else if (_last_tag == TAG_START)
923 			_pos->_content.append(s, p-s);
924 		else
925 			_pos->_children.back()->_trailing.append(s, p-s);
926 	}
927 
928 	if (p != e)
929 		_pos->_end_leading.assign(p, e-p);
930 
931 	_pos.back();
932 
933 	_last_tag = TAG_END;
934 	_content.erase();
935 }
936 
937 #if defined(XS_USE_XERCES) || defined(XS_USE_EXPAT)
938  /// store content, white space and comments
939 void XMLReaderBase::DefaultHandler(const XML_Char* s, int len)
940 {
941 #if defined(XML_UNICODE) || defined(XS_USE_XERCES)
942 	_content.append(String_from_XML_Char(s, len));
943 #else
944 	_content.append(s, len);
945 #endif
946 }
947 #endif
948 
949 
950 XS_String XMLWriter::s_empty_attr;
951 
952 void XMLWriter::create(const XS_String& name)
953 {
954 	if (!_stack.empty()) {
955 		StackEntry& last = _stack.top();
956 
957 		if (last._state < PRE_CLOSED) {
958 			write_attributes(last);
959 			close_pre(last);
960 		}
961 
962 		++last._children;
963 	}
964 
965 	StackEntry entry;
966 	entry._node_name = name;
967 	_stack.push(entry);
968 
969 	write_pre(entry);
970 }
971 
972 bool XMLWriter::back()
973 {
974 	if (!_stack.empty()) {
975 		write_post(_stack.top());
976 
977 		_stack.pop();
978 		return true;
979 	} else
980 		return false;
981 }
982 
983 void XMLWriter::close_pre(StackEntry& entry)
984 {
985 	_out << '>';
986 
987 	entry._state = PRE_CLOSED;
988 }
989 
990 void XMLWriter::write_pre(StackEntry& entry)
991 {
992 	if (_format._pretty >= PRETTY_LINEFEED)
993 		_out << _format._endl;
994 
995 	if (_format._pretty == PRETTY_INDENT) {
996 		for(size_t i=_stack.size(); --i>0; )
997 			_out << XML_INDENT_SPACE;
998 	}
999 
1000 	_out << '<' << EncodeXMLString(entry._node_name);
1001 	//entry._state = PRE;
1002 }
1003 
1004 void XMLWriter::write_attributes(StackEntry& entry)
1005 {
1006 	for(AttrMap::const_iterator it=entry._attributes.begin(); it!=entry._attributes.end(); ++it)
1007 		_out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
1008 
1009 	entry._state = ATTRIBUTES;
1010 }
1011 
1012 void XMLWriter::write_post(StackEntry& entry)
1013 {
1014 	if (entry._state < ATTRIBUTES)
1015 		write_attributes(entry);
1016 
1017 	if (entry._children || !entry._content.empty()) {
1018 		if (entry._state < PRE_CLOSED)
1019 			close_pre(entry);
1020 
1021 		_out << entry._content;
1022 		//entry._state = CONTENT;
1023 
1024 		if (_format._pretty>=PRETTY_LINEFEED && entry._content.empty())
1025 			_out << _format._endl;
1026 
1027 		if (_format._pretty==PRETTY_INDENT && entry._content.empty()) {
1028 			for(size_t i=_stack.size(); --i>0; )
1029 				_out << XML_INDENT_SPACE;
1030 		}
1031 
1032 		_out << "</" << EncodeXMLString(entry._node_name) << ">";
1033 	} else {
1034 		_out << "/>";
1035 	}
1036 
1037 	entry._state = POST;
1038 }
1039 
1040 
1041 }	// namespace XMLStorage
1042