use ObjectURI more consistently
[gnash.git] / libcore / asobj / XML_as.cpp
blobf33ba3b8c2e3805abbff252cc4c4c9763cf3c8a1
1 // XML_as.cpp: ActionScript "XMLDocument" class, for Gnash.
2 //
3 // Copyright (C) 2009, 2010 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "log.h"
22 #include "as_function.h" //for as_function
23 #include "fn_call.h"
24 #include "Global_as.h"
26 #include "LoadableObject.h"
27 #include "XMLNode_as.h"
28 #include "XML_as.h"
29 #include "builtin_function.h"
30 #include "NativeFunction.h"
31 #include "VM.h"
32 #include "namedStrings.h"
33 #include "StringPredicates.h"
34 #include "smart_ptr.h" // for boost intrusive_ptr
35 #include "GnashException.h" // for ActionException
36 #include "Object.h"
38 #include <string>
39 #include <sstream>
40 #include <vector>
41 #include <algorithm>
42 #include <boost/assign/list_of.hpp>
43 #include <boost/algorithm/string/compare.hpp>
44 #include <boost/algorithm/string/replace.hpp>
47 namespace gnash {
49 // Forward declarations
50 namespace {
52 as_value xml_new(const fn_call& fn);
53 as_value xml_createElement(const fn_call& fn);
54 as_value xml_createTextNode(const fn_call& fn);
55 as_value xml_parseXML(const fn_call& fn);
56 as_value xml_onData(const fn_call& fn);
57 as_value xml_onLoad(const fn_call& fn);
58 as_value xml_xmlDecl(const fn_call& fn);
59 as_value xml_docTypeDecl(const fn_call& fn);
60 as_value xml_escape(const fn_call& fn);
61 as_value xml_loaded(const fn_call& fn);
62 as_value xml_status(const fn_call& fn);
64 typedef XML_as::xml_iterator xml_iterator;
66 bool textAfterWhitespace(xml_iterator& it, xml_iterator end);
67 bool textMatch(xml_iterator& it, xml_iterator end,
68 const std::string& match, bool advance = true);
69 bool parseNodeWithTerminator( xml_iterator& it, xml_iterator end,
70 const std::string& terminator, std::string& content);
73 typedef std::map<std::string, std::string> Entities;
74 const Entities& getEntities();
76 void attachXMLProperties(as_object& o);
77 void attachXMLInterface(as_object& o);
82 XML_as::XML_as(as_object& object)
84 XMLNode_as(getGlobal(object)),
85 _loaded(XML_LOADED_UNDEFINED),
86 _status(XML_OK)
88 setObject(&object);
91 // Parse the ASCII XML string into an XMLNode tree
92 XML_as::XML_as(as_object& object, const std::string& xml)
94 XMLNode_as(getGlobal(object)),
95 _loaded(XML_LOADED_UNDEFINED),
96 _status(XML_OK)
98 setObject(&object);
99 parseXML(xml);
102 void
103 escapeXML(std::string& text)
105 const Entities& ent = getEntities();
107 for (Entities::const_iterator i = ent.begin(), e = ent.end();
108 i != e; ++i)
110 boost::replace_all(text, i->second, i->first);
114 void
115 unescapeXML(std::string& text)
117 const Entities& ent = getEntities();
119 for (Entities::const_iterator i = ent.begin(), e = ent.end();
120 i != e; ++i) {
121 boost::replace_all(text, i->first, i->second);
124 // Additionally, the &nbsp; entity is unescaped (but never escaped).
125 // Note we do this as UTF-8, which is most likely wrong for SWF5.
126 boost::replace_all(text, "&nbsp;", "\xc2\xa0");
129 void
130 XML_as::toString(std::ostream& o, bool encode) const
132 if (!_xmlDecl.empty()) o << _xmlDecl;
133 if (!_docTypeDecl.empty()) o << _docTypeDecl;
135 XMLNode_as::toString(o, encode);
138 void
139 XML_as::parseAttribute(XMLNode_as* node, xml_iterator& it,
140 const xml_iterator end, Attributes& attributes)
143 const std::string terminators("\r\t\n >=");
145 xml_iterator ourend = std::find_first_of(it, end,
146 terminators.begin(), terminators.end());
148 if (ourend == end) {
149 _status = XML_UNTERMINATED_ELEMENT;
150 return;
152 std::string name(it, ourend);
154 if (name.empty()) {
155 _status = XML_UNTERMINATED_ELEMENT;
156 return;
159 // Point iterator to the DisplayObject after the name.
160 it = ourend;
162 // Skip any whitespace before the '='. If we reach the end of the string
163 // or don't find an '=', it's a parser error.
164 if (!textAfterWhitespace(it, end) || *it != '=') {
165 _status = XML_UNTERMINATED_ELEMENT;
166 return;
169 // Point to the DisplayObject after the '='
170 ++it;
172 // Skip any whitespace. If we reach the end of the string, or don't find
173 // a " or ', it's a parser error.
174 if (!textAfterWhitespace(it, end) || (*it != '"' && *it != '\'')) {
175 _status = XML_UNTERMINATED_ELEMENT;
176 return;
179 // Find the end of the attribute, looking for the opening DisplayObject,
180 // as long as it's not escaped. We begin one after the present position,
181 // which should be the opening DisplayObject. We want to remember what the
182 // iterator is pointing to for a while, so don't advance it.
183 ourend = it;
184 do {
185 ++ourend;
186 ourend = std::find(ourend, end, *it);
187 } while (ourend != end && *(ourend - 1) == '\\');
189 if (ourend == end) {
190 _status = XML_UNTERMINATED_ATTRIBUTE;
191 return;
193 ++it;
195 std::string value(it, ourend);
197 // Replace entities in the value.
198 unescapeXML(value);
200 // We've already checked that ourend != end, so we can advance at
201 // least once.
202 it = ourend;
203 // Advance past the last attribute DisplayObject
204 ++it;
206 // Handle namespace. This is set once only for each node, and is also
207 // pushed to the attributes list once.
208 StringNoCaseEqual noCaseCompare;
209 if (noCaseCompare(name, "xmlns") || noCaseCompare(name, "xmlns:")) {
210 if (!node->getNamespaceURI().empty()) return;
211 node->setNamespaceURI(value);
214 // This ensures values are not inserted twice, which is expected
215 // behaviour
216 attributes.insert(std::make_pair(name, value));
220 /// Parse and set the docTypeDecl. This is stored without any validation and
221 /// with the same case as in the parsed XML.
222 void
223 XML_as::parseDocTypeDecl(xml_iterator& it, const xml_iterator end)
226 xml_iterator ourend;
227 xml_iterator current = it;
229 std::string::size_type count = 1;
231 // Look for angle brackets in the doctype declaration.
232 while (count) {
234 // Find the next closing bracket after the current position.
235 ourend = std::find(current, end, '>');
236 if (ourend == end) {
237 _status = XML_UNTERMINATED_DOCTYPE_DECL;
238 return;
240 --count;
242 // Count any opening brackets in between.
243 count += std::count(current, ourend, '<');
244 current = ourend;
245 ++current;
248 const std::string content(it, ourend);
249 std::ostringstream os;
250 os << '<' << content << '>';
251 _docTypeDecl = os.str();
252 it = ourend + 1;
256 void
257 XML_as::parseXMLDecl(xml_iterator& it, const xml_iterator end)
259 std::string content;
260 if (!parseNodeWithTerminator(it, end, "?>", content))
262 _status = XML_UNTERMINATED_XML_DECL;
263 return;
266 std::ostringstream os;
267 os << "<" << content << "?>";
269 // This is appended to any xmlDecl already there.
270 _xmlDecl += os.str();
274 // The iterator should be pointing to the first char after the '<'
275 void
276 XML_as::parseTag(XMLNode_as*& node, xml_iterator& it,
277 const xml_iterator end)
280 bool closing = (*it == '/');
281 if (closing) ++it;
283 // These are for terminating the tag name, not (necessarily) the tag.
284 const std::string terminators("\r\n\t >");
286 xml_iterator endName = std::find_first_of(it, end, terminators.begin(),
287 terminators.end());
289 // Check that one of the terminators was found; otherwise it's malformed.
290 if (endName == end) {
291 _status = XML_UNTERMINATED_ELEMENT;
292 return;
295 // Knock off the "/>" of a self-closing tag.
296 if (std::equal(endName - 1, endName + 1, "/>")) {
297 // This can leave endName before it, e.g when a self-closing tag is
298 // empty ("</>"). This must be checked before trying to construct
299 // a string!
300 --endName;
303 // If the tag is empty, the XML counts as malformed.
304 if (it >= endName) {
305 _status = XML_UNTERMINATED_ELEMENT;
306 return;
309 std::string tagName(it, endName);
311 if (!closing) {
313 XMLNode_as* childNode = new XMLNode_as(_global);
314 childNode->nodeNameSet(tagName);
315 childNode->nodeTypeSet(Element);
317 // Skip to the end of any whitespace after the tag name
318 it = endName;
320 if (!textAfterWhitespace(it, end)) {
321 _status = XML_UNTERMINATED_ELEMENT;
322 return;
325 // Parse any attributes in an opening tag only, stopping at "/>" or
326 // '>'
327 // Attributes are added in reverse order and without any duplicates.
328 Attributes attributes;
329 while (it != end && *it != '>' && _status == XML_OK)
331 if (end - it > 1 && std::equal(it, it + 2, "/>")) break;
333 // This advances the iterator
334 parseAttribute(childNode, it, end, attributes);
336 // Skip any whitespace. If we reach the end of the string,
337 // it's malformed.
338 if (!textAfterWhitespace(it, end)) {
339 _status = XML_UNTERMINATED_ELEMENT;
340 return;
344 // Do nothing more if there was an error in attributes parsing.
345 if (_status != XML_OK) return;
347 for (Attributes::const_reverse_iterator i = attributes.rbegin(),
348 e = attributes.rend(); i != e; ++i) {
349 childNode->setAttribute(i->first, i->second);
352 node->appendChild(childNode);
353 if (*it == '/') ++it;
354 else node = childNode;
356 if (*it == '>') ++it;
358 return;
361 // If we reach here, this is a closing tag.
363 it = std::find(endName, end, '>');
365 if (it == end) {
366 _status = XML_UNTERMINATED_ELEMENT;
367 return;
369 ++it;
371 StringNoCaseEqual noCaseCompare;
373 if (node->getParent() && noCaseCompare(node->nodeName(), tagName)) {
374 node = node->getParent();
376 else {
377 // Malformed. Search for the parent node.
378 XMLNode_as* s = node;
379 while (s && !noCaseCompare(s->nodeName(), tagName)) {
380 //log_debug("parent: %s, this: %s", s->nodeName(), tagName);
381 s = s->getParent();
383 if (s) {
384 // If there's a parent, the open tag is orphaned.
385 _status = XML_MISSING_CLOSE_TAG;
387 else {
388 // If no parent, the close tag is orphaned.
389 _status = XML_MISSING_OPEN_TAG;
395 void
396 XML_as::parseText(XMLNode_as* node, xml_iterator& it,
397 const xml_iterator end)
399 xml_iterator ourend = std::find(it, end, '<');
400 std::string content(it, ourend);
402 it = ourend;
404 if (ignoreWhite() &&
405 content.find_first_not_of("\t\r\n ") == std::string::npos) return;
407 XMLNode_as* childNode = new XMLNode_as(_global);
409 childNode->nodeTypeSet(XMLNode_as::Text);
411 // Replace any entitites.
412 unescapeXML(content);
414 childNode->nodeValueSet(content);
415 node->appendChild(childNode);
419 void
420 XML_as::parseComment(XMLNode_as* /*node*/, xml_iterator& it,
421 const xml_iterator end)
424 std::string content;
426 if (!parseNodeWithTerminator(it, end, "-->", content)) {
427 _status = XML_UNTERMINATED_COMMENT;
428 return;
430 // Comments are discarded at least up to SWF8
433 void
434 XML_as::parseCData(XMLNode_as* node, xml_iterator& it,
435 const xml_iterator end)
437 std::string content;
439 if (!parseNodeWithTerminator(it, end, "]]>", content)) {
440 _status = XML_UNTERMINATED_CDATA;
441 return;
444 XMLNode_as* childNode = new XMLNode_as(_global);
445 childNode->nodeValueSet(content);
446 childNode->nodeTypeSet(Text);
447 node->appendChild(childNode);
452 // This parses an XML string into a tree of XMLNodes.
453 void
454 XML_as::parseXML(const std::string& xml)
457 if (xml.empty()) {
458 log_error(_("XML data is empty"));
459 return;
462 // Clear current data
463 clear();
465 xml_iterator it = xml.begin();
466 const xml_iterator end = xml.end();
467 XMLNode_as* node = this;
469 while (it != end && _status == XML_OK)
471 if (*it == '<')
473 ++it;
474 if (textMatch(it, end, "!DOCTYPE", false))
476 // We should not advance past the DOCTYPE label, as
477 // the case is preserved.
478 parseDocTypeDecl(it, end);
480 else if (textMatch(it, end, "?xml", false))
482 // We should not advance past the xml label, as
483 // the case is preserved.
484 parseXMLDecl(it, end);
486 else if (textMatch(it, end, "!--"))
488 parseComment(node, it, end);
490 else if (textMatch(it, end, "![CDATA["))
492 parseCData(node, it, end);
494 else parseTag(node, it, end);
496 else parseText(node, it, end);
499 // If everything parsed correctly, check that we've got back to the
500 // parent node. If not, there is a missing closing tag.
501 if (_status == XML_OK && node != this) {
502 _status = XML_MISSING_CLOSE_TAG;
507 void
508 XML_as::clear()
510 // TODO: should set childs's parent to NULL ?
511 clearChildren();
512 _docTypeDecl.clear();
513 _xmlDecl.clear();
514 _status = XML_OK;
517 bool
518 XML_as::ignoreWhite()
521 // TODO: use NSV:
522 const ObjectURI& propnamekey =
523 getURI(getVM(_global), "ignoreWhite");
524 as_value val;
526 as_object* obj = object();
528 if (!obj->get_member(propnamekey, &val)) {
529 return false;
531 return toBool(val, getVM(*obj));
534 // XML.prototype is assigned after the class has been constructed, so it
535 // replaces the original prototype and does not have a 'constructor'
536 // property.
537 void
538 xml_class_init(as_object& where, const ObjectURI& uri)
541 Global_as& gl = getGlobal(where);
542 as_object* cl = gl.createClass(&xml_new, 0);
544 as_function* ctor = getMember(gl, NSV::CLASS_XMLNODE).to_function();
546 if (ctor) {
547 // XML.prototype is an XMLNode(1, "");
548 fn_call::Args args;
549 args += 1, "";
550 as_object* proto =
551 constructInstance(*ctor, as_environment(getVM(where)), args);
552 attachXMLInterface(*proto);
553 cl->init_member(NSV::PROP_PROTOTYPE, proto);
556 where.init_member(uri, cl, as_object::DefaultFlags);
560 void
561 registerXMLNative(as_object& where)
563 VM& vm = getVM(where);
564 vm.registerNative(xml_escape, 100, 5);
565 vm.registerNative(xml_createElement, 253, 10);
566 vm.registerNative(xml_createTextNode, 253, 11);
567 vm.registerNative(xml_parseXML, 253, 12);
570 namespace {
572 void
573 attachXMLProperties(as_object& o)
576 as_object* proto = o.get_prototype();
577 if (!proto) return;
578 const int flags = 0;
579 proto->init_member("contentType", "application/x-www-form-urlencoded",
580 flags);
581 proto->init_property("docTypeDecl", &xml_docTypeDecl, &xml_docTypeDecl,
582 flags);
583 proto->init_member("ignoreWhite", false, flags);
584 proto->init_property("loaded", xml_loaded, xml_loaded);
585 proto->init_property("status", xml_status, xml_status, flags);
586 proto->init_property("xmlDecl", &xml_xmlDecl, &xml_xmlDecl, flags);
591 void
592 attachXMLInterface(as_object& o)
595 VM& vm = getVM(o);
596 Global_as& gl = getGlobal(o);
598 const int flags = 0;
600 // No flags:
601 o.init_member("createElement", vm.getNative(253, 10), flags);
602 o.init_member("createTextNode", vm.getNative(253, 11), flags);
603 o.init_member("load", vm.getNative(301, 0), flags);
605 /// This handles getBytesLoaded, getBytesTotal, and addRequestHeader
606 attachLoadableInterface(o, flags);
608 o.init_member("parseXML", vm.getNative(253, 12), flags);
609 o.init_member("send", vm.getNative(301, 1), flags);
610 o.init_member("sendAndLoad", vm.getNative(301, 2), flags);
611 o.init_member("onData", gl.createFunction(xml_onData), flags);
612 o.init_member("onLoad", gl.createFunction(xml_onLoad), flags);
616 as_value
617 xml_new(const fn_call& fn)
620 as_object* obj = ensure<ValidThis>(fn);
622 if (fn.nargs && !fn.arg(0).is_undefined()) {
624 // Copy constructor clones nodes.
625 if (fn.arg(0).is_object()) {
626 as_object* other = toObject(fn.arg(0), getVM(fn));
627 XML_as* xml;
628 if (isNativeType(other, xml)) {
629 as_object* clone = xml->cloneNode(true)->object();
630 attachXMLProperties(*clone);
631 return as_value(clone);
635 const int version = getSWFVersion(fn);
636 const std::string& xml_in = fn.arg(0).to_string(version);
637 // It doesn't matter if the string is empty.
638 obj->setRelay(new XML_as(*obj, xml_in));
639 attachXMLProperties(*obj);
640 return as_value();
643 obj->setRelay(new XML_as(*obj));
644 attachXMLProperties(*obj);
646 return as_value();
649 /// This is attached to the prototype (an XMLNode) on construction of XML
651 /// It has the curious effect of giving the XML object an inherited 'loaded'
652 /// property that fails when called on the prototype, because the prototype
653 /// is of type XMLNode.
654 as_value
655 xml_loaded(const fn_call& fn)
657 XML_as* ptr = ensure<ThisIsNative<XML_as> >(fn);
659 if (!fn.nargs) {
660 XML_as::LoadStatus ls = ptr->loaded();
661 if (ls == XML_as::XML_LOADED_UNDEFINED) return as_value();
662 return as_value(static_cast<bool>(ls));
664 ptr->setLoaded(
665 static_cast<XML_as::LoadStatus>(toBool(fn.arg(0), getVM(fn))));
666 return as_value();
669 as_value
670 xml_status(const fn_call& fn)
672 XML_as* ptr = ensure<ThisIsNative<XML_as> >(fn);
674 if (!fn.nargs) {
675 return as_value(ptr->status());
678 const double status = toNumber(fn.arg(0), getVM(fn));
679 if (isNaN(status) ||
680 status > std::numeric_limits<boost::int32_t>::max() ||
681 status < std::numeric_limits<boost::int32_t>::min()) {
683 ptr->setStatus(static_cast<XML_as::ParseStatus>(
684 std::numeric_limits<boost::int32_t>::min()));
687 ptr->setStatus(static_cast<XML_as::ParseStatus>(int(status)));
688 return as_value();
691 /// Only available as ASnative.
692 as_value
693 xml_escape(const fn_call& fn)
695 if (!fn.nargs) return as_value();
697 std::string escaped = fn.arg(0).to_string();
698 escapeXML(escaped);
699 return as_value(escaped);
702 /// \brief create a new XML element
704 /// Method; creates a new XML element with the name specified in the
705 /// parameter. The new element initially has no parent, no children,
706 /// and no siblings. The method returns a reference to the newly
707 /// created XML object that represents the element. This method and
708 /// the XML.createTextNode() method are the constructor methods for
709 /// creating nodes for an XML object.
710 as_value
711 xml_createElement(const fn_call& fn)
714 if (fn.nargs > 0)
716 const std::string& text = fn.arg(0).to_string();
717 XMLNode_as *xml_obj = new XMLNode_as(getGlobal(fn));
718 xml_obj->nodeNameSet(text);
719 xml_obj->nodeTypeSet(XMLNode_as::Text);
721 return as_value(xml_obj->object());
724 else {
725 log_error(_("no text for element creation"));
727 return as_value();
731 /// \brief Create a new XML node
732 ///
733 /// Method; creates a new XML text node with the specified text. The
734 /// new node initially has no parent, and text nodes cannot have
735 /// children or siblings. This method returns a reference to the XML
736 /// object that represents the new text node. This method and the
737 /// XML.createElement() method are the constructor methods for
738 /// creating nodes for an XML object.
739 as_value
740 xml_createTextNode(const fn_call& fn)
743 if (fn.nargs > 0) {
744 const std::string& text = fn.arg(0).to_string();
745 XMLNode_as* xml_obj = new XMLNode_as(getGlobal(fn));
746 xml_obj->nodeValueSet(text);
747 xml_obj->nodeTypeSet(XMLNode_as::Text);
748 return as_value(xml_obj->object());
750 else {
751 log_error(_("no text for text node creation"));
753 return as_value();
757 as_value
758 xml_parseXML(const fn_call& fn)
761 XML_as* ptr = ensure<ThisIsNative<XML_as> >(fn);
763 if (fn.nargs < 1)
765 IF_VERBOSE_ASCODING_ERRORS(
766 log_aserror("XML.parseXML() needs one argument");
768 return as_value();
771 const std::string& text = fn.arg(0).to_string();
772 ptr->parseXML(text);
774 return as_value();
777 as_value
778 xml_xmlDecl(const fn_call& fn)
780 XML_as* ptr = ensure<ThisIsNative<XML_as> >(fn);
782 if (!fn.nargs)
784 // Getter
785 const std::string& xml = ptr->getXMLDecl();
786 if (xml.empty()) return as_value();
787 return as_value(xml);
790 // Setter
792 const std::string& xml = fn.arg(0).to_string();
793 ptr->setXMLDecl(xml);
795 return as_value();
799 as_value
800 xml_docTypeDecl(const fn_call& fn)
802 XML_as* ptr = ensure<ThisIsNative<XML_as> >(fn);
804 if (!fn.nargs)
806 // Getter
807 const std::string& docType = ptr->getDocTypeDecl();
808 if (docType.empty()) return as_value();
809 return as_value(docType);
812 // Setter
814 const std::string& docType = fn.arg(0).to_string();
815 ptr->setDocTypeDecl(docType);
817 return as_value();
821 /// XML.prototype has an empty onLoad function defined.
822 as_value
823 xml_onLoad(const fn_call& /*fn*/)
825 return as_value();
828 as_value
829 xml_onData(const fn_call& fn)
832 as_object* thisPtr = fn.this_ptr;
833 assert(thisPtr);
835 // See http://gitweb.freedesktop.org/?p=swfdec/swfdec.git;
836 // a=blob;f=libswfdec/swfdec_initialize.as
838 as_value src;
839 if (fn.nargs) src = fn.arg(0);
841 if (!src.is_undefined()) {
842 thisPtr->set_member(NSV::PROP_LOADED, true);
843 callMethod(thisPtr, NSV::PROP_PARSE_XML, src);
844 callMethod(thisPtr, NSV::PROP_ON_LOAD, true);
846 else {
847 thisPtr->set_member(NSV::PROP_LOADED, false);
848 callMethod(thisPtr, NSV::PROP_ON_LOAD, false);
851 return as_value();
854 /// Case insensitive match of a string, returning false if there too few
855 /// DisplayObjects left or if there is no match. If there is a match, and advance
856 /// is not false, the iterator points to the DisplayObject after the match.
857 bool
858 textMatch(xml_iterator& it, const xml_iterator end,
859 const std::string& match, bool advance)
862 const std::string::size_type len = match.length();
864 if (static_cast<size_t>(end - it) < len) return false;
866 if (!std::equal(it, it + len, match.begin(), boost::is_iequal())) {
867 return false;
869 if (advance) it += len;
870 return true;
873 /// Advance past whitespace
875 /// @return true if there is text after the whitespace, false if we
876 /// reach the end of the string.
877 bool
878 textAfterWhitespace(xml_iterator& it, const xml_iterator end)
880 const std::string whitespace("\r\t\n ");
881 while (it != end && whitespace.find(*it) != std::string::npos) ++it;
882 return (it != end);
885 /// Parse a complete node up to a specified terminator.
887 /// @return false if we reach the end of the text before finding the
888 /// terminator.
889 /// @param it The current position of the iterator. If the return is true,
890 /// this points to the first DisplayObject after the terminator
891 /// after return
892 /// @param content If the return is true, this is filled with the content of
893 /// the tag.
894 /// @param xml The complete XML string.
895 bool
896 parseNodeWithTerminator(xml_iterator& it, const xml_iterator end,
897 const std::string& terminator, std::string& content)
899 xml_iterator ourend = std::search(it, end, terminator.begin(),
900 terminator.end());
902 if (ourend == end) {
903 return false;
906 content = std::string(it, ourend);
907 it = ourend + terminator.length();
909 return true;
912 const Entities&
913 getEntities()
916 static const Entities entities = boost::assign::map_list_of
917 ("&amp;", "&")
918 ("&quot;", "\"")
919 ("&lt;", "<")
920 ("&gt;", ">")
921 ("&apos;", "'");
923 return entities;
927 } // anonymous namespace
928 } // gnash namespace
930 // local Variables:
931 // mode: C++
932 // indent-tabs-mode: t
933 // End: