File indexing completed on 2024-04-21 15:38:19

0001 /**
0002  * Copyright (C) 2004 by Koos Vriezen <koos.vriezen@gmail.com>
0003  *
0004  *  This library is free software; you can redistribute it and/or
0005  *  modify it under the terms of the GNU Library General Public
0006  *  License version 2 as published by the Free Software Foundation.
0007  *
0008  *  This library is distributed in the hope that it will be useful,
0009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011  *  Library General Public License for more details.
0012  *
0013  *  You should have received a copy of the GNU Library General Public License
0014  *  along with this library; see the file COPYING.LIB.  If not, write to
0015  *  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
0016  *  Boston, MA 02110-1301, USA.
0017  **/
0018 
0019 #include "config-kmplayer.h"
0020 #include <time.h>
0021 
0022 #include <qtextstream.h>
0023 #include <kdebug.h>
0024 #ifdef KMPLAYER_WITH_EXPAT
0025 #include <expat.h>
0026 #endif
0027 #include "kmplayerplaylist.h"
0028 #include "kmplayer_asx.h"
0029 #include "kmplayer_atom.h"
0030 #include "kmplayer_opml.h"
0031 #include "kmplayer_rp.h"
0032 #include "kmplayer_rss.h"
0033 #include "kmplayer_smil.h"
0034 #include "kmplayer_xspf.h"
0035 #include "mediaobject.h"
0036 
0037 #include <kurl.h>
0038 
0039 #ifdef SHAREDPTR_DEBUG
0040 KMPLAYER_EXPORT int shared_data_count;
0041 #endif
0042 
0043 using namespace KMPlayer;
0044 
0045 //-----------------------------------------------------------------------------
0046 
0047 Node *KMPlayer::fromXMLDocumentTag (NodePtr & d, const QString & tag) {
0048     const QByteArray ba = tag.toAscii ();
0049     const char * const name = ba.constData();
0050     if (!strcmp (name, "smil"))
0051         return new SMIL::Smil (d);
0052     else if (!strcasecmp (name, "asx"))
0053         return new ASX::Asx (d);
0054     else if (!strcasecmp (name, "imfl"))
0055         return new RP::Imfl (d);
0056     else if (!strcasecmp (name, "rss"))
0057         return new RSS::Rss (d);
0058     else if (!strcasecmp (name, "feed"))
0059         return new ATOM::Feed (d);
0060     else if (!strcasecmp (name, "playlist"))
0061         return new XSPF::Playlist (d);
0062     else if (!strcasecmp (name, "opml"))
0063         return new OPML::Opml (d);
0064     else if (!strcasecmp (name, "url"))
0065         return new GenericURL (d, QString ());
0066     else if (!strcasecmp (name, "mrl") ||
0067             !strcasecmp (name, "document"))
0068         return new GenericMrl (d);
0069     return 0L;
0070 }
0071 
0072 //-----------------------------------------------------------------------------
0073 
0074 QTextStream &KMPlayer::operator << (QTextStream &out, const XMLStringlet &txt) {
0075     int len = int (txt.str.size ());
0076     for (int i = 0; i < len; ++i) {
0077         if (txt.str [i] == QChar ('<')) {
0078             out <<  "&lt;";
0079         } else if (txt.str [i] == QChar ('>')) {
0080             out <<  "&gt;";
0081         } else if (txt.str [i] == QChar ('"')) {
0082             out <<  "&quot;";
0083         } else if (txt.str [i] == QChar ('&')) {
0084             out <<  "&amp;";
0085         } else
0086             out << txt.str [i];
0087     }
0088     return out;
0089 }
0090 
0091 //-----------------------------------------------------------------------------
0092 
0093 Connection::Connection (Node *invoker, Node *receiver, VirtualVoid *pl)
0094  : connectee (invoker), connecter (receiver), payload (pl) {
0095 #ifdef KMPLAYER_TEST_CONNECTION
0096     connection_counter++;
0097 #endif
0098 }
0099 
0100 ConnectionLink::ConnectionLink () : connection (NULL) {}
0101 
0102 ConnectionLink::~ConnectionLink () {
0103     disconnect ();
0104 }
0105 
0106 bool ConnectionLink::connect (Node *send, MessageType msg, Node *rec,
0107         VirtualVoid *payload) {
0108     disconnect ();
0109     ConnectionList *list = nodeMessageReceivers (send, msg);
0110     if (list) {
0111         connection = new Connection (send, rec, payload);
0112         connection->list = list;
0113         connection->link = &connection;
0114         connection->prev = list->link_last;
0115         connection->next = NULL;
0116         if (list->link_last)
0117             list->link_last->next = connection;
0118         else
0119             list->link_first = connection;
0120         list->link_last = connection;
0121     }
0122     return list;
0123 }
0124 
0125 void ConnectionLink::disconnect () const {
0126     if (connection) {
0127         Connection *tmp = connection;
0128         if (tmp->prev)
0129             tmp->prev->next = tmp->next;
0130         else
0131             tmp->list->link_first = tmp->next;
0132         if (tmp->next)
0133             tmp->next->prev = tmp->prev;
0134         else
0135             tmp->list->link_last = tmp->prev;
0136         *tmp->link = NULL;
0137         if (tmp->list->link_next == tmp)
0138             tmp->list->link_next = tmp->next;
0139         delete tmp;
0140     }
0141     ASSERT (!connection);
0142 }
0143 
0144 void ConnectionLink::assign (const ConnectionLink *link) const {
0145     disconnect ();
0146     connection = link->connection;
0147     link->connection = NULL;
0148     if (connection)
0149         connection->link = &connection;
0150 }
0151 
0152 Node *ConnectionLink::signaler () const {
0153     return connection ? connection->connectee.ptr () : NULL;
0154 }
0155 
0156 ConnectionList::ConnectionList ()
0157     : link_first (NULL), link_last (NULL), link_next (NULL) {}
0158 
0159 ConnectionList::~ConnectionList () {
0160     clear ();
0161 }
0162 
0163 void ConnectionList::clear () {
0164     while (link_first) {
0165         Connection *tmp = link_first;
0166         link_first = tmp->next;
0167         *tmp->link = NULL;
0168         delete tmp;
0169     }
0170     link_last = link_next = NULL;
0171 }
0172 
0173 //-----------------------------------------------------------------------------
0174 
0175 KDE_NO_CDTOR_EXPORT TimerPosting::TimerPosting (int ms, unsigned eid)
0176  : Posting (NULL, MsgEventTimer),
0177    event_id (eid),
0178    milli_sec (ms),
0179    interval (false) {}
0180 
0181 //-----------------------------------------------------------------------------
0182 
0183 Matrix::Matrix () : a (1.0), b (0.0), c (0.0), d (1.0), tx (0), ty (0) {}
0184 
0185 Matrix::Matrix (const Matrix & m)
0186  : a (m.a), b (m.b), c (m.c), d (m.d), tx (m.tx), ty (m.ty) {}
0187 
0188 Matrix::Matrix (Single xoff, Single yoff, float xscale, float yscale)
0189  : a (xscale), b (0.0), c (0.0), d (yscale), tx (xoff), ty (yoff) {}
0190 
0191 void Matrix::getXY (Single & x, Single & y) const {
0192     x = Single (x * a) + tx;
0193     y = Single (y * d) + ty;
0194 }
0195 
0196 void Matrix::getWH (Single &w, Single &h) const {
0197     w *= a;
0198     h *= d;
0199 }
0200 
0201 IRect Matrix::toScreen (const SRect &rect) const {
0202     return IRect (
0203             (int) (Single (rect.x () * a) + tx),
0204             (int) (Single (rect.y () * d) + ty),
0205             (int) (rect.width () * a),
0206             (int) (rect.height () * d));
0207 }
0208 
0209 SRect Matrix::toUser (const IRect &rect) const {
0210     if (a > 0.00001 && d > 0.00001) {
0211         return SRect (
0212                 Single ((Single (rect.x ()) - tx) / a),
0213                 Single ((Single (rect.y ()) - ty) / d),
0214                 rect.width () / a,
0215                 rect.height () / d);
0216     } else {
0217         kWarning () << "Not invering " << a << ", " << d << " scale";
0218         return SRect ();
0219     }
0220 }
0221 
0222 void Matrix::transform (const Matrix & matrix) {
0223     // TODO: rotate
0224     a *= matrix.a;
0225     d *= matrix.d;
0226     tx = Single (tx * matrix.a) + matrix.tx;
0227     ty = Single (ty * matrix.d) + matrix.ty;
0228 }
0229 
0230 void Matrix::scale (float sx, float sy) {
0231     a *= sx;
0232     d *= sy;
0233     tx *= sx;
0234     ty *= sy;
0235 }
0236 
0237 void Matrix::translate (Single x, Single y) {
0238     tx += x;
0239     ty += y;
0240 }
0241 
0242 //-----------------------------------------------------------------------------
0243 
0244 KDE_NO_CDTOR_EXPORT Node::Node (NodePtr & d, short _id)
0245  : m_doc (d), state (state_init), id (_id),
0246    auxiliary_node (false), open (false) {}
0247 
0248 Node::~Node () {
0249     clear ();
0250 }
0251 
0252 Document * Node::document () {
0253     return convertNode <Document> (m_doc);
0254 }
0255 
0256 Mrl * Node::mrl () {
0257     return 0L;
0258 }
0259 
0260 const char * Node::nodeName () const {
0261     return "node";
0262 }
0263 
0264 void Node::setState (State nstate) {
0265     if (state != nstate && (state_init == nstate || state != state_resetting)) {
0266         State old = state;
0267         state = nstate;
0268         if (document ()->notify_listener)
0269             document()->notify_listener->stateElementChanged (this, old, state);
0270     }
0271 }
0272 
0273 void Node::activate () {
0274     //kDebug () << nodeName () << " Node::activate";
0275     setState (state_activated);
0276     if (firstChild ())
0277         firstChild ()->activate (); // activate only the first
0278     else
0279         finish (); // a quicky :-)
0280 }
0281 
0282 void Node::begin () {
0283     if (active ()) {
0284         setState (state_began);
0285     } else
0286         kError () << nodeName() << " begin call on not active element" << endl;
0287 }
0288 
0289 void Node::defer () {
0290     if (active ()) {
0291         setState (state_deferred);
0292     } else
0293         kError () << "Node::defer () call on not activated element" << endl;
0294 }
0295 
0296 void Node::undefer () {
0297     if (state == state_deferred) {
0298         if (firstChild () && firstChild ()->state > state_init) {
0299             state = state_began;
0300         } else {
0301             setState (state_activated);
0302             activate ();
0303         }
0304     } else
0305         kWarning () << nodeName () << " call on not deferred element";
0306 }
0307 
0308 void Node::finish () {
0309     if (active ()) {
0310         setState (state_finished);
0311         if (m_parent)
0312             document ()->post (m_parent, new Posting (this, MsgChildFinished));
0313         else
0314             deactivate (); // document deactivates itself on finish
0315     } else
0316         kWarning () <<"Node::finish () call on not active element";
0317 }
0318 
0319 void Node::deactivate () {
0320     //kDebug () << nodeName () << " Node::deactivate";
0321     bool need_finish (unfinished ());
0322     if (state_resetting != state)
0323         setState (state_deactivated);
0324     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
0325         if (e->state > state_init && e->state < state_deactivated)
0326             e->deactivate ();
0327         else
0328             break; // remaining not yet activated
0329     }
0330     if (need_finish && m_parent && m_parent->active ())
0331         document ()->post (m_parent, new Posting (this, MsgChildFinished));
0332 }
0333 
0334 void Node::reset () {
0335     //kDebug () << nodeName () << " Node::reset";
0336     if (active ()) {
0337         setState (state_resetting);
0338         deactivate ();
0339     }
0340     setState (state_init);
0341     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
0342         if (e->state != state_init)
0343             e->reset ();
0344         // else
0345         //    break; // rest not activated yet
0346     }
0347 }
0348 
0349 void Node::clear () {
0350     clearChildren ();
0351 }
0352 
0353 void Node::clearChildren () {
0354     if (m_doc)
0355         document()->m_tree_version++;
0356     while (m_first_child != m_last_child) {
0357         // avoid stack abuse with 10k children derefing each other
0358         m_last_child->m_parent = 0L;
0359         m_last_child = m_last_child->m_prev;
0360         m_last_child->m_next = 0L;
0361     }
0362     if (m_first_child)
0363         m_first_child->m_parent = 0L;
0364     m_first_child = m_last_child = 0L;
0365 }
0366 
0367 template <>
0368 void TreeNode<Node>::appendChild (Node *c) {
0369     static_cast <Node *> (this)->document()->m_tree_version++;
0370     ASSERT (!c->parentNode ());
0371     appendChildImpl (c);
0372 }
0373 
0374 template <>
0375 void TreeNode<Node>::insertBefore (Node *c, Node *b) {
0376     ASSERT (!c->parentNode ());
0377     static_cast <Node *> (this)->document()->m_tree_version++;
0378     insertBeforeImpl (c, b);
0379 }
0380 
0381 template <>
0382 void TreeNode<Node>::removeChild (NodePtr c) {
0383     static_cast <Node *> (this)->document()->m_tree_version++;
0384     removeChildImpl (c);
0385 }
0386 
0387 KDE_NO_EXPORT void Node::replaceChild (NodePtr _new, NodePtr old) {
0388     document()->m_tree_version++;
0389     if (old->m_prev) {
0390         old->m_prev->m_next = _new;
0391         _new->m_prev = old->m_prev;
0392         old->m_prev = 0L;
0393     } else {
0394         _new->m_prev = 0L;
0395         m_first_child = _new;
0396     }
0397     if (old->m_next) {
0398         old->m_next->m_prev = _new;
0399         _new->m_next = old->m_next;
0400         old->m_next = 0L;
0401     } else {
0402         _new->m_next = 0L;
0403         m_last_child = _new;
0404     }
0405     _new->m_parent = this;
0406     old->m_parent = 0L;
0407 }
0408 
0409 Node *Node::childFromTag (const QString &) {
0410     return NULL;
0411 }
0412 
0413 KDE_NO_EXPORT void Node::characterData (const QString & s) {
0414     document()->m_tree_version++;
0415     if (!m_last_child || m_last_child->id != id_node_text)
0416         appendChild (new TextNode (m_doc, s));
0417     else
0418         convertNode <TextNode> (m_last_child)->appendText (s);
0419 }
0420 
0421 void Node::normalize () {
0422     Node *e = firstChild ();
0423     while (e) {
0424         Node *tmp = e->nextSibling ();
0425         if (!e->isElementNode () && e->id == id_node_text) {
0426             QString val = e->nodeValue ().simplified ();
0427             if (val.isEmpty ())
0428                 removeChild (e);
0429             else
0430                 static_cast <TextNode *> (e)->setText (val);
0431         } else
0432             e->normalize ();
0433         e = tmp;
0434     }
0435 }
0436 
0437 static void getInnerText (const Node *p, QTextStream & out) {
0438     for (Node *e = p->firstChild (); e; e = e->nextSibling ()) {
0439         if (e->id == id_node_text || e->id == id_node_cdata)
0440             out << e->nodeValue ();
0441         else
0442             getInnerText (e, out);
0443     }
0444 }
0445 
0446 QString Node::innerText () const {
0447     QString buf;
0448     QTextStream out (&buf, QIODevice::WriteOnly);
0449     getInnerText (this, out);
0450     return buf;
0451 }
0452 
0453 static void getOuterXML (const Node *p, QTextStream & out, int depth) {
0454     if (!p->isElementNode ()) { // #text or #cdata
0455         if (p->id == id_node_cdata)
0456             out << "<![CDATA[" << p->nodeValue () << "]]>" << QChar ('\n');
0457         else
0458             out << XMLStringlet (p->nodeValue ()) << QChar ('\n');
0459     } else {
0460         const Element *e = static_cast <const Element *> (p);
0461         QString indent (QString ().fill (QChar (' '), depth));
0462         out << indent << QChar ('<') << XMLStringlet (e->nodeName ());
0463         for (Attribute *a = e->attributes().first(); a; a = a->nextSibling())
0464             out << " " << XMLStringlet (a->name ().toString ()) <<
0465                 "=\"" << XMLStringlet (a->value ()) << "\"";
0466         if (e->hasChildNodes ()) {
0467             out << QChar ('>') << QChar ('\n');
0468             for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0469                 getOuterXML (c, out, depth + 1);
0470             out << indent << QString("</") << XMLStringlet (e->nodeName()) <<
0471                 QChar ('>') << QChar ('\n');
0472         } else
0473             out << QString ("/>") << QChar ('\n');
0474     }
0475 }
0476 
0477 QString Node::innerXML () const {
0478     QString buf;
0479     QTextStream out (&buf, QIODevice::WriteOnly);
0480     for (Node *e = firstChild (); e; e = e->nextSibling ())
0481         getOuterXML (e, out, 0);
0482     return buf;
0483 }
0484 
0485 QString Node::outerXML () const {
0486     QString buf;
0487     QTextStream out (&buf, QIODevice::WriteOnly);
0488     getOuterXML (this, out, 0);
0489     return buf;
0490 }
0491 
0492 Node::PlayType Node::playType () {
0493     return play_type_none;
0494 }
0495 
0496 void Node::opened () {
0497     open = true;
0498 }
0499 
0500 void Node::closed () {
0501     open = false;
0502 }
0503 
0504 void Node::message (MessageType msg, void *content) {
0505     switch (msg) {
0506 
0507     case MsgChildFinished: {
0508         Posting *post = (Posting *) content;
0509         if (unfinished ()) {
0510             if (post->source && post->source->state == state_finished)
0511                 post->source->deactivate ();
0512             if (post->source && post->source->nextSibling ())
0513                 post->source->nextSibling ()->activate ();
0514             else
0515                 finish (); // we're done
0516         }
0517         break;
0518     }
0519 
0520     default:
0521         break;
0522     }
0523 }
0524 
0525 void *Node::role (RoleType msg, void *) {
0526     switch (msg) {
0527     case RoleReady:
0528         return MsgBool (true);
0529     default:
0530         break;
0531     }
0532     return NULL;
0533 }
0534 
0535 KDE_NO_EXPORT void Node::deliver (MessageType msg, void *content) {
0536     ConnectionList *nl = nodeMessageReceivers (this, msg);
0537     if (nl)
0538         for (Connection *c = nl->first(); c; c = nl->next ())
0539             if (c->connecter)
0540                 c->connecter->message (msg, content);
0541 }
0542 
0543 void Node::accept (Visitor * v) {
0544     v->visit (this);
0545 }
0546 
0547 QString Node::nodeValue () const {
0548     return innerText ().trimmed ();
0549 }
0550 
0551 //-----------------------------------------------------------------------------
0552 
0553 namespace {
0554     struct KMPLAYER_NO_EXPORT ParamValue {
0555         QString val;
0556         QStringList  * modifications;
0557         ParamValue (const QString & v) : val (v), modifications (0L) {}
0558         ~ParamValue () { delete modifications; }
0559         QString value ();
0560         void setValue (const QString & v) { val = v; }
0561     };
0562     typedef QMap <TrieString, ParamValue *> ParamMap;
0563 }
0564 
0565 namespace KMPlayer {
0566     class KMPLAYER_NO_EXPORT ElementPrivate {
0567     public:
0568         ~ElementPrivate ();
0569         ParamMap params;
0570         void clear ();
0571     };
0572 }
0573 
0574 QString ParamValue::value() {
0575     return modifications && modifications->size ()
0576         ? modifications->back () : val;
0577 }
0578 
0579 KDE_NO_CDTOR_EXPORT ElementPrivate::~ElementPrivate () {
0580     clear ();
0581 }
0582 
0583 KDE_NO_EXPORT void ElementPrivate::clear () {
0584     const ParamMap::iterator e = params.end ();
0585     for (ParamMap::iterator i = params.begin (); i != e; ++i)
0586         delete i.value ();
0587     params.clear ();
0588 }
0589 
0590 Element::Element (NodePtr & d, short id)
0591     : Node (d, id), d (new ElementPrivate) {}
0592 
0593 Element::~Element () {
0594     delete d;
0595 }
0596 
0597 void Element::setParam (const TrieString &name, const QString &val, int *mid) {
0598     ParamValue * pv = d->params [name];
0599     if (!pv) {
0600         pv = new ParamValue (mid ? getAttribute (name) : val);
0601         d->params.insert (name, pv);
0602     }
0603     if (mid) {
0604         if (!pv->modifications)
0605             pv->modifications = new QStringList;
0606         if (*mid >= 0 && *mid < int (pv->modifications->size ())) {
0607             (*pv->modifications) [*mid] = val;
0608         } else {
0609             *mid = pv->modifications->size ();
0610             pv->modifications->push_back (val);
0611         }
0612     } else {
0613         pv->setValue (val);
0614     }
0615     parseParam (name, val);
0616 }
0617 
0618 QString Element::param (const TrieString & name) {
0619     ParamValue * pv = d->params [name];
0620     if (pv)
0621         return pv->value ();
0622     return getAttribute (name);
0623 }
0624 
0625 void Element::resetParam (const TrieString &name, int mid) {
0626     ParamValue * pv = d->params [name];
0627     if (pv && pv->modifications) {
0628         if (int (pv->modifications->size ()) > mid && mid > -1) {
0629             (*pv->modifications) [mid] = QString ();
0630             while (pv->modifications->size () > 0 &&
0631                     pv->modifications->back ().isNull ())
0632                 pv->modifications->pop_back ();
0633         }
0634         QString val = pv->value ();
0635         if (pv->modifications->size () == 0) {
0636             delete pv->modifications;
0637             pv->modifications = 0L;
0638             if (val.isNull ()) {
0639                 delete pv;
0640                 d->params.remove (name);
0641             }
0642         }
0643         parseParam (name, val);
0644     } else
0645         kError () << "resetting " << name.toString() << " that doesn't exists" << endl;
0646 }
0647 
0648 void Element::setAttribute (const TrieString & name, const QString & value) {
0649     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
0650         if (name == a->name ()) {
0651             if (value.isNull ())
0652                 m_attributes.remove (a);
0653             else
0654                 a->setValue (value);
0655             return;
0656         }
0657     if (!value.isNull ())
0658         m_attributes.append (new Attribute (TrieString (), name, value));
0659 }
0660 
0661 QString Element::getAttribute (const TrieString & name) {
0662     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
0663         if (name == a->name ())
0664             return a->value ();
0665     return QString ();
0666 }
0667 
0668 void Element::init () {
0669     d->clear();
0670     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
0671         QString v = a->value ();
0672         int p = v.indexOf ('{');
0673         if (p > -1) {
0674             int q = v.indexOf ('}', p + 1);
0675             if (q > -1)
0676                 continue;
0677         }
0678         parseParam (a->name (), v);
0679     }
0680 }
0681 
0682 void Element::reset () {
0683     d->clear();
0684     Node::reset ();
0685 }
0686 
0687 void Element::clear () {
0688     m_attributes = AttributeList (); // remove attributes
0689     d->clear();
0690     Node::clear ();
0691 }
0692 
0693 void Element::setAttributes (const AttributeList &attrs) {
0694     m_attributes = attrs;
0695 }
0696 
0697 void Element::accept (Visitor * v) {
0698     v->visit (this);
0699 }
0700 
0701 //-----------------------------------------------------------------------------
0702 
0703 Attribute::Attribute (const TrieString &ns, const TrieString &n, const QString &v)
0704   : m_namespace (ns), m_name (n), m_value (v) {}
0705 
0706 void Attribute::setName (const TrieString & n) {
0707     m_name = n;
0708 }
0709 
0710 void Attribute::setValue (const QString & v) {
0711     m_value = v;
0712 }
0713 
0714 //-----------------------------------------------------------------------------
0715 
0716 QString PlaylistRole::caption () const {
0717     return title;
0718 }
0719 
0720 void PlaylistRole::setCaption (const QString &s) {
0721     title = s;
0722 }
0723 
0724 //-----------------------------------------------------------------------------
0725 
0726 static bool hasMrlChildren (const NodePtr & e) {
0727     for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0728         if (c->isPlayable () || hasMrlChildren (c))
0729             return true;
0730     return false;
0731 }
0732 
0733 Mrl::Mrl (NodePtr & d, short id)
0734     : Element (d, id), cached_ismrl_version (~0),
0735       media_info (NULL),
0736       aspect (0), repeat (0),
0737       view_mode (SingleMode),
0738       resolved (false), bookmarkable (true), access_granted (false) {}
0739 
0740 Mrl::~Mrl () {
0741     delete media_info;
0742 }
0743 
0744 Node::PlayType Mrl::playType () {
0745     if (cached_ismrl_version != document()->m_tree_version) {
0746         bool ismrl = !hasMrlChildren (this);
0747         cached_play_type = ismrl ? play_type_unknown : play_type_none;
0748         cached_ismrl_version = document()->m_tree_version;
0749     }
0750     return cached_play_type;
0751 }
0752 
0753 QString Mrl::absolutePath () {
0754     QString path = src;
0755     if (!path.isEmpty() && !path.startsWith ("tv:/")) {
0756         for (Node *e = parentNode (); e; e = e->parentNode ()) {
0757             Mrl * mrl = e->mrl ();
0758             if (mrl && !mrl->src.isEmpty () && mrl->src != src) {
0759                 path = KURL (mrl->absolutePath (), src).url ();
0760                 break;
0761             }
0762         }
0763     }
0764     return path;
0765 }
0766 
0767 Node *Mrl::childFromTag (const QString & tag) {
0768     Node * elm = fromXMLDocumentTag (m_doc, tag);
0769     if (elm)
0770         return elm;
0771     return NULL;
0772 }
0773 
0774 Mrl * Mrl::mrl () {
0775     return this;
0776 }
0777 
0778 void Mrl::message (MessageType msg, void *content) {
0779     switch (msg) {
0780     case MsgMediaReady:
0781         resolved = true;
0782         if (state == state_deferred) {
0783             if (isPlayable ()) {
0784                 setState (state_activated);
0785                 begin ();
0786             } else {
0787                 Element::activate ();
0788             }
0789         }
0790         break;
0791 
0792     case MsgMediaFinished:
0793         if (state == state_deferred &&
0794                 !isPlayable () && firstChild ()) {//if backend added child links
0795             state = state_activated;
0796             firstChild ()->activate ();
0797         } else if (unfinished ()) {
0798             finish ();
0799         }
0800         break;
0801 
0802     default:
0803         break;
0804     }
0805     Node::message (msg, content);
0806 }
0807 
0808 void *Mrl::role (RoleType msg, void *content) {
0809     switch (msg) {
0810 
0811     case RoleChildDisplay:
0812         for (Node *p = parentNode (); p; p = p->parentNode ())
0813             if (p->mrl ())
0814                 return p->role (msg, content);
0815         return NULL;
0816 
0817     case RolePlaylist:
0818         if (title.isEmpty ())
0819             title = src;
0820         return !title.isEmpty () ? (PlaylistRole *) this : NULL;
0821 
0822     default:
0823         break;
0824     }
0825     return Node::role (msg, content);
0826 }
0827 
0828 void Mrl::activate () {
0829     if (!resolved && isPlayable ()) {
0830         setState (state_deferred);
0831         media_info = new MediaInfo (this, MediaManager::AudioVideo);
0832         resolved = media_info->wget (absolutePath ());
0833         if (resolved && isPlayable ()) {
0834             // ignore the MsgMediaReady message redirection
0835             setState (state_activated);
0836             begin ();
0837         }
0838     } else if (isPlayable ()) {
0839         setState (state_activated);
0840         begin ();
0841     } else {
0842         Element::activate ();
0843     }
0844 }
0845 
0846 void Mrl::begin () {
0847     kDebug () << nodeName () << src << this;
0848     if (!src.isEmpty ()) {
0849         if (!media_info)
0850             media_info = new MediaInfo (this, MediaManager::AudioVideo);
0851         if (!media_info->media)
0852             media_info->create ();
0853         if (media_info->media->play ())
0854             setState (state_began);
0855         else
0856             deactivate ();
0857     } else {
0858         deactivate (); // nothing to activate
0859     }
0860 }
0861 
0862 void Mrl::defer () {
0863     if (media_info && media_info->media)
0864         media_info->media->pause ();
0865     Node::defer ();
0866 }
0867 
0868 void Mrl::undefer () {
0869     if (media_info && media_info->media) {
0870         media_info->media->unpause ();
0871         setState (state_began);
0872     } else {
0873         Node::undefer ();
0874     }
0875 }
0876 
0877 void Mrl::deactivate () {
0878     delete media_info;
0879     media_info = NULL;
0880     Node::deactivate ();
0881 }
0882 
0883 void Mrl::parseParam (const TrieString & para, const QString & val) {
0884     if (para == Ids::attr_src && !src.startsWith ("#")) {
0885         QString abs = absolutePath ();
0886         if (abs != src)
0887             src = val;
0888         else
0889             src = KURL (abs, val).url ();
0890         for (NodePtr c = firstChild (); c; c = c->nextSibling ())
0891             if (c->mrl () && c->mrl ()->opener.ptr () == this) {
0892                 removeChild (c);
0893                 c->reset();
0894             }
0895         resolved = false;
0896     }
0897 }
0898 
0899 unsigned int Mrl::parseTimeString (const QString &ts) {
0900     QString s (ts);
0901     int multiply[] = { 1, 60, 60 * 60, 24 * 60 * 60, 0 };
0902     int mpos = 0;
0903     double d = 0;
0904     while (!s.isEmpty () && multiply[mpos]) {
0905         int p = s.lastIndexOf (QChar (':'));
0906         QString t = p >= 0 ? s.mid (p + 1) : s;
0907         d += multiply[mpos++] * t.toDouble();
0908         s = p >= 0 ? s.left (p) : QString ();
0909     }
0910     if (d > 0.01)
0911         return (unsigned int) (d * 100);
0912     return 0;
0913 }
0914 
0915 //----------------------%<-----------------------------------------------------
0916 
0917 EventData::EventData (Node *t, Posting *e, EventData *n)
0918  : target (t), event (e), next (n) {}
0919 
0920 EventData::~EventData () {
0921     delete event;
0922 }
0923 //-----------------------------------------------------------------------------
0924 
0925 Postpone::Postpone (NodePtr doc) : m_doc (doc) {
0926     if (m_doc)
0927         m_doc->document ()->timeOfDay (postponed_time);
0928 }
0929 
0930 Postpone::~Postpone () {
0931     if (m_doc)
0932         m_doc->document ()->proceed (postponed_time);
0933 }
0934 
0935 //-----------------------------------------------------------------------------
0936 
0937 static NodePtr dummy_element;
0938 
0939 Document::Document (const QString & s, PlayListNotify * n)
0940  : Mrl (dummy_element, id_node_document),
0941    notify_listener (n),
0942    m_tree_version (0),
0943    event_queue (NULL),
0944    paused_queue (NULL),
0945    cur_event (NULL),
0946    cur_timeout (-1) {
0947     m_doc = m_self; // just-in-time setting fragile m_self to m_doc
0948     src = s;
0949 }
0950 
0951 Document::~Document () {
0952     kDebug () << "~Document " << src;
0953 }
0954 
0955 static Node *getElementByIdImpl (Node *n, const QString & id, bool inter) {
0956     NodePtr elm;
0957     if (!n->isElementNode ())
0958         return NULL;
0959     Element *e = static_cast <Element *> (n);
0960     if (e->getAttribute (Ids::attr_id) == id)
0961         return n;
0962     for (Node *c = e->firstChild (); c; c = c->nextSibling ()) {
0963         if (!inter && c->mrl () && c->mrl ()->opener.ptr () == n)
0964             continue;
0965         if ((elm = getElementByIdImpl (c, id, inter)))
0966             break;
0967     }
0968     return elm;
0969 }
0970 
0971 Node *Document::getElementById (const QString & id) {
0972     return getElementByIdImpl (this, id, true);
0973 }
0974 
0975 Node *Document::getElementById (Node *n, const QString & id, bool inter) {
0976     return getElementByIdImpl (n, id, inter);
0977 }
0978 
0979 Node *Document::childFromTag (const QString & tag) {
0980     Node * elm = fromXMLDocumentTag (m_doc, tag);
0981     if (elm)
0982         return elm;
0983     return NULL;
0984 }
0985 
0986 void Document::dispose () {
0987     clear ();
0988     m_doc = 0L;
0989 }
0990 
0991 void Document::activate () {
0992     first_event_time.tv_sec = 0;
0993     last_event_time = 0;
0994     Mrl::activate ();
0995 }
0996 
0997 void Document::defer () {
0998     if (resolved)
0999         postpone_lock = postpone ();
1000     Mrl::defer ();
1001 }
1002 
1003 void Document::undefer () {
1004     postpone_lock = 0L;
1005     Mrl::undefer ();
1006 }
1007 
1008 void Document::reset () {
1009     Mrl::reset ();
1010     if (event_queue) {
1011         if (notify_listener)
1012             notify_listener->setTimeout (-1);
1013         while (event_queue) {
1014             EventData *ed = event_queue;
1015             event_queue = ed->next;
1016             delete ed;
1017         }
1018         cur_timeout = -1;
1019     }
1020     postpone_lock = 0L;
1021 }
1022 
1023 static inline
1024 int diffTime (const struct timeval & tv1, const struct timeval & tv2) {
1025     //kDebug () << "diffTime sec:" << ((tv1.tv_sec - tv2.tv_sec) * 1000) << " usec:" << ((tv1.tv_usec - tv2.tv_usec) /1000);
1026     return (tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) /1000;
1027 }
1028 
1029 static inline void addTime (struct timeval & tv, int ms) {
1030     if (ms >= 1000) {
1031         tv.tv_sec += ms / 1000;
1032         ms %= 1000;
1033     }
1034     tv.tv_sec += (tv.tv_usec + ms*1000) / 1000000;
1035     tv.tv_usec = (tv.tv_usec + ms*1000) % 1000000;
1036 }
1037 
1038 KDE_NO_CDTOR_EXPORT UpdateEvent::UpdateEvent (Document *doc, unsigned int skip)
1039  : skipped_time (skip) {
1040     struct timeval tv;
1041     doc->timeOfDay (tv);
1042     cur_event_time = doc->last_event_time;
1043 }
1044 
1045 //-----------------------------------------------------------------------------
1046 /*static inline void subtractTime (struct timeval & tv, int ms) {
1047     int sec = ms / 1000;
1048     int msec = ms % 1000;
1049     tv.tv_sec -= sec;
1050     if (tv.tv_usec / 1000 >= msec) {
1051         tv.tv_usec -= msec * 1000;
1052     } else {
1053         tv.tv_sec--;
1054         tv.tv_usec = 1000000 - (msec - tv.tv_usec / 1000 );
1055     }
1056 }*/
1057 
1058 void Document::timeOfDay (struct timeval & tv) {
1059     gettimeofday (&tv, 0L);
1060     if (!first_event_time.tv_sec) {
1061         first_event_time = tv;
1062         last_event_time = 0;
1063     } else {
1064         last_event_time = diffTime (tv, first_event_time);
1065     }
1066 }
1067 
1068 static bool postponedSensible (MessageType msg) {
1069     return msg == MsgEventTimer ||
1070         msg == MsgEventStarted ||
1071         msg == MsgEventStopped;
1072 }
1073 
1074 void Document::insertPosting (Node *n, Posting *e, const struct timeval &tv) {
1075     if (!notify_listener)
1076         return;
1077     bool postponed_sensible = postponedSensible (e->message);
1078     EventData *prev = NULL;
1079     EventData *ed = event_queue;
1080     for (; ed; ed = ed->next) {
1081         int diff = diffTime (ed->timeout, tv);
1082         bool psens = postponedSensible (ed->event->message);
1083         if ((diff > 0 && postponed_sensible == psens) || (!postponed_sensible && psens))
1084             break;
1085         prev = ed;
1086     }
1087     ed = new EventData (n, e, ed);
1088     ed->timeout = tv;
1089     if (prev)
1090         prev->next = ed;
1091     else
1092         event_queue = ed;
1093     //kDebug () << "setTimeout " << ms << " at:" << pos << " tv:" << tv.tv_sec << "." << tv.tv_usec;
1094 }
1095 
1096 void Document::setNextTimeout (const struct timeval &now) {
1097     if (!cur_event) {              // if we're not processing events
1098         int timeout = 0x7FFFFFFF;
1099         if (event_queue && active () &&
1100                 (!postpone_ref || !postponedSensible (event_queue->event->message)))
1101             timeout = diffTime (event_queue->timeout, now);
1102         timeout = 0x7FFFFFFF != timeout ? (timeout > 0 ? timeout : 0) : -1;
1103         if (timeout != cur_timeout) {
1104             cur_timeout = timeout;
1105             notify_listener->setTimeout (cur_timeout);
1106         }
1107     }
1108 }
1109 
1110 void Document::updateTimeout () {
1111     if (!postpone_ref && event_queue && notify_listener) {
1112         struct timeval now;
1113         if (cur_event)
1114             now = cur_event->timeout;
1115         else
1116             timeOfDay (now);
1117         setNextTimeout (now);
1118     }
1119 }
1120 
1121 Posting *Document::post (Node *n, Posting *e) {
1122     int ms = e->message == MsgEventTimer
1123         ? static_cast<TimerPosting *>(e)->milli_sec
1124         : 0;
1125     struct timeval now, tv;
1126     if (cur_event)
1127         now = cur_event->timeout;
1128     else
1129         timeOfDay (now);
1130     tv = now;
1131     addTime (tv, ms);
1132     insertPosting (n, e, tv);
1133     if (postpone_ref || event_queue->event == e)
1134         setNextTimeout (now);
1135     return e;
1136 }
1137 
1138 static EventData *findPosting (EventData *queue, EventData **prev, const Posting *e) {
1139     *prev = NULL;
1140     for (EventData *ed = queue; ed; ed = ed->next) {
1141         if (e == ed->event)
1142             return ed;
1143         *prev = ed;
1144     }
1145     return NULL;
1146 }
1147 
1148 void Document::cancelPosting (Posting *e) {
1149     if (cur_event && cur_event->event == e) {
1150         delete cur_event->event;
1151         cur_event->event = NULL;
1152     } else {
1153         EventData *prev;
1154         EventData **queue = &event_queue;
1155         EventData *ed = findPosting (event_queue, &prev, e);
1156         if (!ed) {
1157             ed = findPosting (paused_queue, &prev, e);
1158             queue = &paused_queue;
1159         }
1160         if (ed) {
1161             if (prev) {
1162                 prev->next = ed->next;
1163             } else {
1164                 *queue = ed->next;
1165                 if (!cur_event && queue == &event_queue) {
1166                     struct timeval now;
1167                     if (event_queue) // save a sys call
1168                         timeOfDay (now);
1169                     setNextTimeout (now);
1170                 }
1171             }
1172             delete ed;
1173         } else {
1174             kError () << "Posting not found";
1175         }
1176     }
1177 }
1178 
1179 void Document::pausePosting (Posting *e) {
1180     if (cur_event && cur_event->event == e) {
1181         paused_queue = new EventData (cur_event->target, cur_event->event, paused_queue);
1182         paused_queue->timeout = cur_event->timeout;
1183         cur_event->event = NULL;
1184     } else {
1185         EventData *prev;
1186         EventData *ed = findPosting (event_queue, &prev, e);
1187         if (ed) {
1188             if (prev)
1189                 prev->next = ed->next;
1190             else
1191                 event_queue = ed->next;
1192             ed->next = paused_queue;
1193             paused_queue = ed;
1194         } else {
1195             kError () << "pauseEvent not found";
1196         }
1197     }
1198 }
1199 
1200 void Document::unpausePosting (Posting *e, int ms) {
1201     EventData *prev;
1202     EventData *ed = findPosting (paused_queue, &prev, e);
1203     if (ed) {
1204         if (prev)
1205             prev->next = ed->next;
1206         else
1207             paused_queue = ed->next;
1208         addTime (ed->timeout, ms);
1209         insertPosting (ed->target, ed->event, ed->timeout);
1210         ed->event = NULL;
1211         delete ed;
1212     } else {
1213         kError () << "pausePosting not found";
1214     }
1215 }
1216 
1217 void Document::timer () {
1218     struct timeval now;
1219     cur_event = event_queue;
1220     if (cur_event) {
1221         NodePtrW guard = this;
1222         struct timeval start = cur_event->timeout;
1223         timeOfDay (now);
1224 
1225         // handle max 100 timeouts with timeout set to now
1226         for (int i = 0; i < 100 && active (); ++i) {
1227             if (postpone_ref && postponedSensible (cur_event->event->message))
1228                 break;
1229             // remove from queue
1230             event_queue = cur_event->next;
1231 
1232             if (!cur_event->target) {
1233                 // some part of document has gone and didn't remove timer
1234                 kError () << "spurious timer" << endl;
1235             } else {
1236                 EventData *ed = cur_event;
1237                 cur_event->target->message (cur_event->event->message, cur_event->event);
1238                 if (!guard) {
1239                     delete ed;
1240                     return;
1241                 }
1242                 if (cur_event->event && cur_event->event->message == MsgEventTimer) {
1243                     TimerPosting *te = static_cast <TimerPosting *> (cur_event->event);
1244                     if (te->interval) {
1245                         te->interval = false; // reset interval
1246                         addTime (cur_event->timeout, te->milli_sec);
1247                         insertPosting (cur_event->target,
1248                                 cur_event->event,
1249                                 cur_event->timeout);
1250                         cur_event->event = NULL;
1251                     }
1252                 }
1253             }
1254             delete cur_event;
1255             cur_event = event_queue;
1256             if (!cur_event || diffTime (cur_event->timeout, start) > 5)
1257                 break;
1258         }
1259         cur_event = NULL;
1260     }
1261     setNextTimeout (now);
1262 }
1263 
1264 PostponePtr Document::postpone () {
1265     if (postpone_ref)
1266         return postpone_ref;
1267     kDebug () << "postpone";
1268     PostponePtr p = new Postpone (this);
1269     postpone_ref = p;
1270     PostponedEvent event (true);
1271     deliver (MsgEventPostponed, &event);
1272     if (notify_listener)
1273         notify_listener->enableRepaintUpdaters (false, 0);
1274     if (!cur_event) {
1275         struct timeval now;
1276         if (event_queue) // save a sys call
1277             timeOfDay (now);
1278         setNextTimeout (now);
1279     }
1280     return p;
1281 }
1282 
1283 void Document::proceed (const struct timeval &postponed_time) {
1284     kDebug () << "proceed";
1285     postpone_ref = NULL;
1286     struct timeval now;
1287     timeOfDay (now);
1288     int diff = diffTime (now, postponed_time);
1289     if (event_queue) {
1290         for (EventData *ed = event_queue; ed; ed = ed->next)
1291             if (ed->event && postponedSensible (ed->event->message))
1292                 addTime (ed->timeout, diff);
1293         setNextTimeout (now);
1294     }
1295     if (notify_listener)
1296         notify_listener->enableRepaintUpdaters (true, diff);
1297     PostponedEvent event (false);
1298     deliver (MsgEventPostponed, &event);
1299 }
1300 
1301 void *Document::role (RoleType msg, void *content) {
1302     if (RoleReceivers == msg) {
1303         MessageType m = (MessageType) (long) content;
1304         if (MsgEventPostponed == m)
1305             return &m_PostponedListeners;
1306     }
1307     return Mrl::role (msg, content);
1308 }
1309 
1310 //-----------------------------------------------------------------------------
1311 
1312 KDE_NO_CDTOR_EXPORT TextNode::TextNode (NodePtr & d, const QString & s, short i)
1313  : Node (d, i), text (s) {}
1314 
1315 void TextNode::appendText (const QString & s) {
1316     text += s;
1317 }
1318 
1319 QString TextNode::nodeValue () const {
1320     return text;
1321 }
1322 
1323 //-----------------------------------------------------------------------------
1324 
1325 KDE_NO_CDTOR_EXPORT CData::CData (NodePtr & d, const QString & s)
1326  : TextNode (d, s, id_node_cdata) {}
1327 
1328 //-----------------------------------------------------------------------------
1329 
1330 DarkNode::DarkNode (NodePtr & d, const QByteArray &n, short id)
1331  : Element (d, id), name (n) {
1332 }
1333 
1334 Node *DarkNode::childFromTag (const QString & tag) {
1335     return new DarkNode (m_doc, tag.toUtf8 ());
1336 }
1337 
1338 //-----------------------------------------------------------------------------
1339 
1340 GenericURL::GenericURL (NodePtr & d, const QString & s, const QString & name)
1341  : Mrl (d, id_node_playlist_item) {
1342     src = s;
1343     if (!src.isEmpty ())
1344         setAttribute (Ids::attr_src, src);
1345     title = name;
1346 }
1347 
1348 KDE_NO_EXPORT void GenericURL::closed () {
1349     if (src.isEmpty ())
1350         src = getAttribute (Ids::attr_src);
1351     Mrl::closed ();
1352 }
1353 
1354 //-----------------------------------------------------------------------------
1355 
1356 GenericMrl::GenericMrl (NodePtr & d, const QString &s, const QString &name, const QByteArray &tag)
1357  : Mrl (d, id_node_playlist_item), node_name (tag) {
1358     src = s;
1359     if (!src.isEmpty ())
1360         setAttribute (Ids::attr_src, src);
1361     title = name;
1362     if (!name.isEmpty ())
1363         setAttribute (Ids::attr_name, name);
1364 }
1365 
1366 void GenericMrl::closed () {
1367     if (src.isEmpty ()) {
1368         src = getAttribute (Ids::attr_src);
1369         if (src.isEmpty ())
1370             src = getAttribute (Ids::attr_url);
1371     }
1372     if (title.isEmpty ())
1373         title = getAttribute (Ids::attr_name);
1374     Mrl::closed ();
1375 }
1376 
1377 void *GenericMrl::role (RoleType msg, void *content)
1378 {
1379     if (RolePlaylist == msg)
1380         return !title.isEmpty () || //return false if no title and only one
1381             previousSibling () || nextSibling ()
1382             ? (PlaylistRole *) this : NULL;
1383     return Mrl::role (msg, content);
1384 }
1385 
1386 //-----------------------------------------------------------------------------
1387 
1388 void Visitor::visit (Element *elm) {
1389     visit (static_cast <Node *> (elm));
1390 }
1391 
1392 void Visitor::visit (TextNode *text) {
1393     visit (static_cast <Node *> (text));
1394 }
1395 
1396 //-----------------------------------------------------------------------------
1397 
1398 CacheAllocator::CacheAllocator (size_t s)
1399     : pool ((void**) malloc (10 * sizeof (void *))), size (s), count (0) {}
1400 
1401 void *CacheAllocator::alloc () {
1402     return count ? pool[--count] : malloc (size);
1403 }
1404 
1405 void CacheAllocator::dealloc (void *p) {
1406     if (count < 10)
1407         pool[count++] = p;
1408     else
1409         free (p);
1410 }
1411 
1412 KMPLAYER_EXPORT CacheAllocator *KMPlayer::shared_data_cache_allocator = NULL;
1413 
1414 //-----------------------------------------------------------------------------
1415 
1416 namespace KMPlayer {
1417 
1418 class KMPLAYER_NO_EXPORT DocumentBuilder {
1419     int m_ignore_depth;
1420     bool m_set_opener;
1421     bool m_root_is_first;
1422     NodePtr m_node;
1423     NodePtr m_root;
1424 public:
1425     DocumentBuilder (NodePtr d, bool set_opener);
1426     ~DocumentBuilder () {}
1427     bool startTag (const QString & tag, const AttributeList &attr);
1428     bool endTag (const QString & tag);
1429     bool characterData (const QString & data);
1430     bool cdataData (const QString & data);
1431 #ifdef KMPLAYER_WITH_EXPAT
1432     void cdataStart ();
1433     void cdataEnd ();
1434 private:
1435     bool in_cdata;
1436     QString cdata;
1437 #endif
1438 };
1439 
1440 } // namespace KMPlayer
1441 
1442 DocumentBuilder::DocumentBuilder (NodePtr d, bool set_opener)
1443  : m_ignore_depth (0), m_set_opener (set_opener), m_root_is_first (false)
1444  , m_node (d), m_root (d)
1445 #ifdef KMPLAYER_WITH_EXPAT
1446  , in_cdata (false)
1447 #endif
1448 {}
1449 
1450 bool DocumentBuilder::startTag(const QString &tag, const AttributeList &attr) {
1451     if (m_ignore_depth) {
1452         m_ignore_depth++;
1453         //kDebug () << "Warning: ignored tag " << tag.latin1 () << " ignore depth = " << m_ignore_depth;
1454     } else if (!m_node) {
1455         return false; // had underflow
1456     } else {
1457         NodePtr n = m_node->childFromTag (tag);
1458         if (!n) {
1459             kDebug () << "Warning: unknown tag " << tag.toLocal8Bit ().constData();
1460             NodePtr doc = m_root->document ();
1461             n = new DarkNode (doc, tag.toUtf8 ());
1462         }
1463         //kDebug () << "Found tag " << tag;
1464         if (n->isElementNode ())
1465             convertNode <Element> (n)->setAttributes (attr);
1466         if (m_node == n && m_node == m_root)
1467             m_root_is_first = true;
1468         else
1469             m_node->appendChild (n);
1470         if (m_set_opener && m_node == m_root) {
1471             Mrl * mrl = n->mrl ();
1472             if (mrl)
1473                 mrl->opener = m_root;
1474         }
1475         n->opened ();
1476         m_node = n;
1477     }
1478     return true;
1479 }
1480 
1481 bool DocumentBuilder::endTag (const QString & tag) {
1482     if (m_ignore_depth) { // endtag to ignore
1483         m_ignore_depth--;
1484         kDebug () << "Warning: ignored end tag " << " ignore depth = " << m_ignore_depth;
1485     } else if (!m_node) {
1486         return false; // had underflow
1487     } else {  // endtag
1488         NodePtr n = m_node;
1489         while (n) {
1490             if (!strcasecmp (n->nodeName (), tag.toLocal8Bit ().constData ()) &&
1491                     (m_root_is_first || n != m_root)) {
1492                 while (n != m_node) {
1493                     kWarning() << m_node->nodeName () << " not closed";
1494                     if (m_root == m_node->parentNode ())
1495                         break;
1496                     m_node->closed ();
1497                     m_node = m_node->parentNode ();
1498                 }
1499                 break;
1500             }
1501             if (n == m_root) {
1502                 if (n == m_node) {
1503                     kError () << "m_node == m_doc, stack underflow " << endl;
1504                     return false;
1505                 }
1506                 kWarning () << "endtag: no match " << tag.toLocal8Bit ().constData();
1507                 break;
1508             } else
1509                  kWarning () << "tag " << tag << " not " << n->nodeName ();
1510             n = n ->parentNode ();
1511         }
1512         //kDebug () << "end tag " << tag;
1513         m_node->closed ();
1514         m_node = m_node->parentNode ();
1515     }
1516     return true;
1517 }
1518 
1519 bool DocumentBuilder::characterData (const QString & data) {
1520     if (!m_ignore_depth && m_node) {
1521 #ifdef KMPLAYER_WITH_EXPAT
1522         if (in_cdata)
1523             cdata += data;
1524         else
1525 #endif
1526             m_node->characterData (data);
1527     }
1528     //kDebug () << "characterData " << d.latin1();
1529     return !!m_node;
1530 }
1531 
1532 bool DocumentBuilder::cdataData (const QString & data) {
1533     if (!m_ignore_depth && m_node) {
1534         NodePtr d = m_node->document ();
1535         m_node->appendChild (new CData (d, data));
1536     }
1537     //kDebug () << "cdataData " << d.latin1();
1538     return !!m_node;
1539 }
1540 
1541 #ifdef KMPLAYER_WITH_EXPAT
1542 
1543 void DocumentBuilder::cdataStart () {
1544     cdata.truncate (0);
1545     in_cdata = true;
1546 }
1547 
1548 void DocumentBuilder::cdataEnd () {
1549     cdataData (cdata);
1550     cdata.truncate (0);
1551     in_cdata = false;
1552 }
1553 
1554 static void startTag (void *data, const char * tag, const char **attr) {
1555     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1556     AttributeList attributes;
1557     if (attr && attr [0]) {
1558         for (int i = 0; attr[i]; i += 2)
1559             attributes.append (new Attribute (
1560                         TrieString(),
1561                         QString::fromUtf8 (attr [i]),
1562                         QString::fromUtf8 (attr [i+1])));
1563     }
1564     builder->startTag (QString::fromUtf8 (tag), attributes);
1565 }
1566 
1567 static void endTag (void *data, const char * tag) {
1568     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1569     builder->endTag (QString::fromUtf8 (tag));
1570 }
1571 
1572 static void characterData (void *data, const char *s, int len) {
1573     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1574     char * buf = new char [len + 1];
1575     strncpy (buf, s, len);
1576     buf[len] = 0;
1577     builder->characterData (QString::fromUtf8 (buf));
1578     delete [] buf;
1579 }
1580 
1581 static void cdataStart (void *data) {
1582     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1583     builder->cdataStart ();
1584 }
1585 
1586 static void cdataEnd (void *data) {
1587     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1588     builder->cdataEnd ();
1589 }
1590 
1591 KMPLAYER_EXPORT
1592 void KMPlayer::readXML (NodePtr root, QTextStream & in, const QString & firstline, bool set_opener) {
1593     bool ok = true;
1594     DocumentBuilder builder (root, set_opener);
1595     XML_Parser parser = XML_ParserCreate (0L);
1596     XML_SetUserData (parser, &builder);
1597     XML_SetElementHandler (parser, startTag, endTag);
1598     XML_SetCharacterDataHandler (parser, characterData);
1599     XML_SetCdataSectionHandler (parser, cdataStart, cdataEnd);
1600     if (!firstline.isEmpty ()) {
1601         QString str (firstline + QChar ('\n'));
1602         QByteArray ba = str.toUtf8();
1603         char *buf = ba.data();
1604         ok = XML_Parse(parser, buf, strlen (buf), false) != XML_STATUS_ERROR;
1605         if (!ok)
1606             kWarning () << XML_ErrorString(XML_GetErrorCode(parser)) << " at " << XML_GetCurrentLineNumber(parser) << " col " << XML_GetCurrentColumnNumber(parser);
1607     }
1608     if (ok && !in.atEnd ()) {
1609         QByteArray ba = in.readAll().toUtf8();
1610         char *buf = ba.data();
1611         ok = XML_Parse(parser, buf, strlen (buf), true) != XML_STATUS_ERROR;
1612         if (!ok)
1613             kWarning () << XML_ErrorString(XML_GetErrorCode(parser)) << " at " << XML_GetCurrentLineNumber(parser) << " col " << XML_GetCurrentColumnNumber(parser);
1614     }
1615     XML_ParserFree(parser);
1616     root->normalize ();
1617     //return ok;
1618 }
1619 
1620 //-----------------------------------------------------------------------------
1621 #else // KMPLAYER_WITH_EXPAT
1622 
1623 namespace {
1624 
1625 class KMPLAYER_NO_EXPORT SimpleSAXParser {
1626     enum Token { tok_empty, tok_text, tok_white_space, tok_angle_open,
1627         tok_equal, tok_double_quote, tok_single_quote, tok_angle_close,
1628         tok_slash, tok_exclamation, tok_amp, tok_hash, tok_colon,
1629         tok_semi_colon, tok_question_mark, tok_cdata_start };
1630 public:
1631     struct TokenInfo {
1632         TokenInfo () : token (tok_empty) {}
1633         void *operator new (size_t);
1634         void operator delete (void *);
1635         Token token;
1636         QString string;
1637         SharedPtr <TokenInfo> next;
1638     };
1639     typedef SharedPtr <TokenInfo> TokenInfoPtr;
1640     SimpleSAXParser (DocumentBuilder & b) : builder (b), position (0), equal_seen (false), in_dbl_quote (false), in_sngl_quote (false), have_error (false), no_entitity_look_ahead (false), have_next_char (false) {}
1641     virtual ~SimpleSAXParser () {};
1642     bool parse (QTextStream & d);
1643 private:
1644     QTextStream * data;
1645     DocumentBuilder & builder;
1646     int position;
1647     QChar next_char;
1648     enum State {
1649         InTag, InStartTag, InPITag, InDTDTag, InEndTag, InAttributes, InContent, InCDATA, InComment
1650     };
1651     struct StateInfo {
1652         StateInfo (State s, SharedPtr <StateInfo> n) : state (s), next (n) {}
1653         State state;
1654         QString data;
1655         SharedPtr <StateInfo> next;
1656     };
1657     SharedPtr <StateInfo> m_state;
1658     TokenInfoPtr next_token, token, prev_token;
1659     // for element reading
1660     QString tagname;
1661     AttributeList m_attributes;
1662     QString attr_namespace, attr_name, attr_value;
1663     QString cdata;
1664     bool equal_seen;
1665     bool in_dbl_quote;
1666     bool in_sngl_quote;
1667     bool have_error;
1668     bool no_entitity_look_ahead;
1669     bool have_next_char;
1670 
1671     bool readTag ();
1672     bool readEndTag ();
1673     bool readAttributes ();
1674     bool readPI ();
1675     bool readDTD ();
1676     bool readCDATA ();
1677     bool readComment ();
1678     bool nextToken ();
1679     void push ();
1680     void push_attribute ();
1681 };
1682 
1683 } // namespace
1684 
1685 static CacheAllocator token_pool (sizeof (SimpleSAXParser::TokenInfo));
1686 
1687 inline void *SimpleSAXParser::TokenInfo::operator new (size_t) {
1688     return token_pool.alloc ();
1689 }
1690 
1691 inline void SimpleSAXParser::TokenInfo::operator delete (void *p) {
1692     token_pool.dealloc (p);
1693 }
1694 
1695 KMPLAYER_EXPORT
1696 void KMPlayer::readXML (NodePtr root, QTextStream & in, const QString & firstline, bool set_opener) {
1697     DocumentBuilder builder (root, set_opener);
1698     root->opened ();
1699     SimpleSAXParser parser (builder);
1700     if (!firstline.isEmpty ()) {
1701         QString str (firstline + QChar ('\n'));
1702         QTextStream fl_in (&str, QIODevice::ReadOnly);
1703         parser.parse (fl_in);
1704     }
1705     if (!in.atEnd ())
1706         parser.parse (in);
1707     if (root->open) // endTag may have closed it
1708         root->closed ();
1709     for (NodePtr e = root->parentNode (); e; e = e->parentNode ()) {
1710         if (e->open)
1711             break;
1712         e->closed ();
1713     }
1714     //doc->normalize ();
1715     //kDebug () << root->outerXML ();
1716 }
1717 
1718 void SimpleSAXParser::push () {
1719     if (next_token->string.size ()) {
1720         prev_token = token;
1721         token = next_token;
1722         if (prev_token)
1723             prev_token->next = token;
1724         next_token = TokenInfoPtr (new TokenInfo);
1725         //kDebug () << "push " << token->string;
1726     }
1727 }
1728 
1729 void SimpleSAXParser::push_attribute () {
1730     //kDebug () << "attribute " << attr_name.latin1 () << "=" << attr_value.latin1 ();
1731     m_attributes.append(new Attribute (attr_namespace, attr_name, attr_value));
1732     attr_namespace.clear ();
1733     attr_name.truncate (0);
1734     attr_value.truncate (0);
1735     equal_seen = in_sngl_quote = in_dbl_quote = false;
1736 }
1737 
1738 bool SimpleSAXParser::nextToken () {
1739     TokenInfoPtr cur_token = token;
1740     while (!data->atEnd () && cur_token == token && !(token && token->next)) {
1741         if (have_next_char)
1742             have_next_char = false;
1743         else
1744             *data >> next_char;
1745         bool append_char = true;
1746         if (next_char.isSpace ()) {
1747             if (next_token->token != tok_white_space)
1748                 push ();
1749             next_token->token = tok_white_space;
1750         } else if (!next_char.isLetterOrNumber ()) {
1751             if (next_char == QChar ('#')) {
1752                 //if (next_token->token == tok_empty) { // check last item on stack &
1753                     push ();
1754                     next_token->token = tok_hash;
1755                 //}
1756             } else if (next_char == QChar ('/')) {
1757                 push ();
1758                 next_token->token = tok_slash;
1759             } else if (next_char == QChar ('!')) {
1760                 push ();
1761                 next_token->token = tok_exclamation;
1762             } else if (next_char == QChar ('?')) {
1763                 push ();
1764                 next_token->token = tok_question_mark;
1765             } else if (next_char == QChar ('<')) {
1766                 push ();
1767                 next_token->token = tok_angle_open;
1768             } else if (next_char == QChar ('>')) {
1769                 push ();
1770                 next_token->token = tok_angle_close;
1771             } else if (InAttributes == m_state->state &&
1772                     next_char == QChar (':')) {
1773                 push ();
1774                 next_token->token = tok_colon;
1775             } else if (next_char == QChar (';')) {
1776                 push ();
1777                 next_token->token = tok_semi_colon;
1778             } else if (next_char == QChar ('=')) {
1779                 push ();
1780                 next_token->token = tok_equal;
1781             } else if (next_char == QChar ('"')) {
1782                 push ();
1783                 next_token->token = tok_double_quote;
1784             } else if (next_char == QChar ('\'')) {
1785                 push ();
1786                 next_token->token = tok_single_quote;
1787             } else if (next_char == QChar ('&')) {
1788                 push ();
1789                 if (no_entitity_look_ahead) {
1790                     have_next_char = true;
1791                     break;
1792                 }
1793                 append_char = false;
1794                 no_entitity_look_ahead = true;
1795                 TokenInfoPtr tmp = token;
1796                 TokenInfoPtr prev_tmp = prev_token;
1797                 if (nextToken () && token->token == tok_text &&
1798                         nextToken () && token->token == tok_semi_colon) {
1799                     if (prev_token->string == QString ("amp"))
1800                         token->string = QChar ('&');
1801                     else if (prev_token->string == QString ("lt"))
1802                         token->string = QChar ('<');
1803                     else if (prev_token->string == QString ("gt"))
1804                         token->string = QChar ('>');
1805                     else if (prev_token->string == QString ("quot"))
1806                         token->string = QChar ('"');
1807                     else if (prev_token->string == QString ("apos"))
1808                         token->string = QChar ('\'');
1809                     else if (prev_token->string == QString ("copy"))
1810                         token->string = QChar (169);
1811                     else
1812                         token->string = QChar ('?');// TODO lookup more ..
1813                     token->token = tok_text;
1814                     if (tmp) { // cut out the & xxx ; tokens
1815                         tmp->next = token;
1816                         token = tmp;
1817                     }
1818                     //kDebug () << "entity found "<<prev_token->string;
1819                 } else if (token->token == tok_hash &&
1820                         nextToken () && token->token == tok_text &&
1821                         nextToken () && token->token == tok_semi_colon) {
1822                     //kDebug () << "char entity found " << prev_token->string << prev_token->string.toInt (0L, 16);
1823                     token->token = tok_text;
1824                     if (!prev_token->string.startsWith (QChar ('x')))
1825                         token->string = QChar (prev_token->string.toInt ());
1826                     else
1827                         token->string = QChar (prev_token->string.mid (1).toInt (0L, 16));
1828                     if (tmp) { // cut out the '& # xxx ;' tokens
1829                         tmp->next = token;
1830                         token = tmp;
1831                     }
1832                 } else {
1833                     token = tmp; // restore and insert the lost & token
1834                     tmp = TokenInfoPtr (new TokenInfo);
1835                     tmp->token = tok_amp;
1836                     tmp->string += QChar ('&');
1837                     tmp->next = token->next;
1838                     if (token)
1839                         token->next = tmp;
1840                     else
1841                         token = tmp; // hmm
1842                 }
1843                 no_entitity_look_ahead = false;
1844                 prev_token = prev_tmp;
1845             } else if (next_token->token != tok_text) {
1846                 push ();
1847                 next_token->token = tok_text;
1848             }
1849         } else if (next_token->token != tok_text) {
1850             push ();
1851             next_token->token = tok_text;
1852         }
1853         if (append_char)
1854             next_token->string += next_char;
1855         if (next_token->token == tok_text &&
1856                 next_char == QChar ('[' ) && next_token->string == "[CDATA[") {
1857             next_token->token = tok_cdata_start;
1858             break;
1859         }
1860     }
1861     if (token == cur_token) {
1862         if (token && token->next) {
1863             prev_token = token;
1864             token = token->next;
1865         } else if (next_token->string.size ()) {
1866             push (); // last token
1867         } else
1868             return false;
1869         return true;
1870     }
1871     return true;
1872 }
1873 
1874 bool SimpleSAXParser::readAttributes () {
1875     bool closed = false;
1876     while (true) {
1877         if (!nextToken ()) return false;
1878         //kDebug () << "readAttributes " << token->string.latin1();
1879         if ((in_dbl_quote && token->token != tok_double_quote) ||
1880                     (in_sngl_quote && token->token != tok_single_quote)) {
1881             attr_value += token->string;
1882         } else if (token->token == tok_equal) {
1883             if (attr_name.isEmpty ())
1884                 return false;
1885             if (equal_seen)
1886                 attr_value += token->string; // EQ=a=2c ???
1887             //kDebug () << "equal_seen";
1888             equal_seen = true;
1889         } else if (token->token == tok_white_space) {
1890             if (!attr_value.isEmpty ())
1891                 push_attribute ();
1892         } else if (token->token == tok_single_quote) {
1893             if (!equal_seen)
1894                 attr_name += token->string; // D'OH=xxx ???
1895             else if (in_sngl_quote) { // found one
1896                 push_attribute ();
1897             } else if (attr_value.isEmpty ())
1898                 in_sngl_quote = true;
1899             else
1900                 attr_value += token->string;
1901         } else if (token->token == tok_colon) {
1902             if (equal_seen) {
1903                 attr_value += token->string;
1904             } else {
1905                 attr_namespace = attr_name;
1906                 attr_name.clear();
1907             }
1908         } else if (token->token == tok_double_quote) {
1909             if (!equal_seen)
1910                 attr_name += token->string; // hmm
1911             else if (in_dbl_quote) { // found one
1912                 push_attribute ();
1913             } else if (attr_value.isEmpty ())
1914                 in_dbl_quote = true;
1915             else
1916                 attr_value += token->string;
1917             //kDebug () << "in_dbl_quote:"<< in_dbl_quote;
1918         } else if (token->token == tok_slash) {
1919             TokenInfoPtr mark_token = token;
1920             if (nextToken () &&
1921                     (token->token != tok_white_space || nextToken()) &&//<e / >
1922                     token->token == tok_angle_close) {
1923             //kDebug () << "close mark:";
1924                 closed = true;
1925                 break;
1926             } else {
1927                 token = mark_token;
1928             //kDebug () << "not end mark:"<< equal_seen;
1929                 if (equal_seen)
1930                     attr_value += token->string; // ABBR=w/o ???
1931                 else
1932                     attr_name += token->string;
1933             }
1934         } else if (token->token == tok_angle_close) {
1935             if (!attr_name.isEmpty ())
1936                 push_attribute ();
1937             break;
1938         } else if (equal_seen) {
1939             attr_value += token->string;
1940         } else {
1941             attr_name += token->string;
1942         }
1943     }
1944     m_state = m_state->next;
1945     if (m_state->state == InPITag) {
1946         if (tagname == QString ("xml")) {
1947             /*const AttributeMap::const_iterator e = attr.end ();
1948             for (AttributeMap::const_iterator i = attr.begin (); i != e; ++i)
1949                 if (!strcasecmp (i.key ().latin1 (), "encoding"))
1950                   kDebug () << "encodeing " << i.data().latin1();*/
1951         }
1952     } else {
1953         have_error = !builder.startTag (tagname, m_attributes);
1954         if (closed)
1955             have_error &= !builder.endTag (tagname);
1956         //kDebug () << "readTag " << tagname << " closed:" << closed << " ok:" << have_error;
1957     }
1958     m_state = m_state->next; // pop Node or PI
1959     return !have_error;
1960 }
1961 
1962 bool SimpleSAXParser::readPI () {
1963     // TODO: <?xml .. encoding="ENC" .. ?>
1964     if (!nextToken ()) return false;
1965     if (token->token == tok_text && !token->string.compare ("xml")) {
1966         m_state = new StateInfo (InAttributes, m_state);
1967         return readAttributes ();
1968     } else {
1969         while (nextToken ())
1970             if (token->token == tok_angle_close) {
1971                 m_state = m_state->next;
1972                 return true;
1973             }
1974     }
1975     return false;
1976 }
1977 
1978 bool SimpleSAXParser::readDTD () {
1979     //TODO: <!ENTITY ..>
1980     if (!nextToken ()) return false;
1981     if (token->token == tok_text && token->string.startsWith (QString ("--"))) {
1982         m_state = new StateInfo (InComment, m_state->next); // note: pop DTD
1983         return readComment ();
1984     }
1985     //kDebug () << "readDTD: " << token->string.latin1 ();
1986     if (token->token == tok_cdata_start) {
1987         m_state = new StateInfo (InCDATA, m_state->next); // note: pop DTD
1988         if (token->next) {
1989             cdata = token->next->string;
1990             token->next = 0;
1991         } else {
1992             cdata = next_token->string;
1993             next_token->string.truncate (0);
1994             next_token->token = tok_empty;
1995         }
1996         return readCDATA ();
1997     }
1998     while (nextToken ())
1999         if (token->token == tok_angle_close) {
2000             m_state = m_state->next;
2001             return true;
2002         }
2003     return false;
2004 }
2005 
2006 bool SimpleSAXParser::readCDATA () {
2007     while (!data->atEnd ()) {
2008         *data >> next_char;
2009         if (next_char == QChar ('>') && cdata.endsWith (QString ("]]"))) {
2010             cdata.truncate (cdata.size () - 2);
2011             m_state = m_state->next;
2012             if (m_state->state == InContent)
2013                 have_error = !builder.cdataData (cdata);
2014             else if (m_state->state == InAttributes) {
2015                 if (equal_seen)
2016                     attr_value += cdata;
2017                 else
2018                     attr_name += cdata;
2019             }
2020             cdata.truncate (0);
2021             return true;
2022         }
2023         cdata += next_char;
2024     }
2025     return false;
2026 }
2027 
2028 bool SimpleSAXParser::readComment () {
2029     while (nextToken ()) {
2030         if (token->token == tok_angle_close && prev_token)
2031             if (prev_token->string.endsWith (QString ("--"))) {
2032                 m_state = m_state->next;
2033                 return true;
2034             }
2035     }
2036     return false;
2037 }
2038 
2039 bool SimpleSAXParser::readEndTag () {
2040     if (!nextToken ()) return false;
2041     if (token->token == tok_white_space)
2042         if (!nextToken ()) return false;
2043     tagname = token->string;
2044     if (!nextToken ()) return false;
2045     if (token->token == tok_white_space)
2046         if (!nextToken ()) return false;
2047     if (token->token != tok_angle_close)
2048         return false;
2049     have_error = !builder.endTag (tagname);
2050     m_state = m_state->next;
2051     return true;
2052 }
2053 
2054 // TODO: <!ENTITY ..> &#1234;
2055 bool SimpleSAXParser::readTag () {
2056     if (!nextToken ()) return false;
2057     if (token->token == tok_exclamation) {
2058         m_state = new StateInfo (InDTDTag, m_state->next);
2059     //kDebug () << "readTag: " << token->string.latin1 ();
2060         return readDTD ();
2061     }
2062     if (token->token == tok_white_space)
2063         if (!nextToken ()) return false; // allow '< / foo', '<  foo', '< ? foo'
2064     if (token->token == tok_question_mark) {
2065         m_state = new StateInfo (InPITag, m_state->next);
2066         return readPI ();
2067     }
2068     if (token->token == tok_slash) {
2069         m_state = new StateInfo (InEndTag, m_state->next);
2070         return readEndTag ();
2071     }
2072     if (token->token != tok_text)
2073         return false; // FIXME entities
2074     tagname = token->string;
2075     //kDebug () << "readTag " << tagname.latin1();
2076     m_state = new StateInfo (InAttributes, m_state);
2077     return readAttributes ();
2078 }
2079 
2080 bool SimpleSAXParser::parse (QTextStream & d) {
2081     data = &d;
2082     if (!next_token) {
2083         next_token = TokenInfoPtr (new TokenInfo);
2084         m_state = new StateInfo (InContent, m_state);
2085     }
2086     bool ok = true;
2087     bool in_character_data = false;
2088     QString white_space;
2089     while (ok) {
2090         switch (m_state->state) {
2091             case InTag:
2092                 ok = readTag ();
2093                 break;
2094             case InPITag:
2095                 ok = readPI ();
2096                 break;
2097             case InDTDTag:
2098                 ok = readDTD ();
2099                 break;
2100             case InEndTag:
2101                 ok = readEndTag ();
2102                 break;
2103             case InAttributes:
2104                 ok = readAttributes ();
2105                 break;
2106             case InCDATA:
2107                 ok = readCDATA ();
2108                 break;
2109             case InComment:
2110                 ok = readComment ();
2111                 break;
2112             default:
2113                 if ((ok = nextToken ())) {
2114                     if (token->token == tok_angle_open) {
2115                         attr_name.truncate (0);
2116                         attr_value.truncate (0);
2117                         m_attributes = AttributeList ();
2118                         equal_seen = in_sngl_quote = in_dbl_quote = false;
2119                         m_state = new StateInfo (InTag, m_state);
2120                         ok = readTag ();
2121                         in_character_data = false;
2122                         white_space.truncate (0);
2123                     } else if (token->token == tok_white_space) {
2124                         white_space += token->string;
2125                     } else {
2126                         if (!white_space.isEmpty ()) {
2127                             if (!in_character_data) {
2128                                 int pos = white_space.lastIndexOf (QChar ('\n'));
2129                                 if (pos > -1)
2130                                     white_space = white_space.mid (pos + 1);
2131                             }
2132                             have_error = !builder.characterData (white_space);
2133                             white_space.truncate (0);
2134                         }
2135                         have_error = !builder.characterData (token->string);
2136                         in_character_data = true;
2137                     }
2138                 }
2139         }
2140         if (!m_state)
2141             return true; // end document
2142     }
2143     return false; // need more data
2144 }
2145 
2146 #endif // KMPLAYER_WITH_EXPAT