File indexing completed on 2024-04-14 04:49:03

0001 /*
0002     SPDX-FileCopyrightText: 2004 Koos Vriezen <koos.vriezen@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "config-kmplayer.h"
0008 #include <ctime>
0009 
0010 #include <QTextStream>
0011 #ifdef KMPLAYER_WITH_EXPAT
0012 #include <expat.h>
0013 #endif
0014 #include "kmplayercommon_log.h"
0015 #include "kmplayerplaylist.h"
0016 #include "kmplayer_asx.h"
0017 #include "kmplayer_atom.h"
0018 #include "kmplayer_opml.h"
0019 #include "kmplayer_rp.h"
0020 #include "kmplayer_rss.h"
0021 #include "kmplayer_smil.h"
0022 #include "kmplayer_xspf.h"
0023 #include "mediaobject.h"
0024 
0025 #ifdef SHAREDPTR_DEBUG
0026 KMPLAYERCOMMON_EXPORT int shared_data_count;
0027 #endif
0028 
0029 using namespace KMPlayer;
0030 
0031 //-----------------------------------------------------------------------------
0032 
0033 Node *KMPlayer::fromXMLDocumentTag (NodePtr & d, const QString & tag) {
0034     const QByteArray ba = tag.toLatin1 ();
0035     const char * const name = ba.constData();
0036     if (!strcmp (name, "smil"))
0037         return new SMIL::Smil (d);
0038     else if (!strcasecmp (name, "asx"))
0039         return new ASX::Asx (d);
0040     else if (!strcasecmp (name, "imfl"))
0041         return new RP::Imfl (d);
0042     else if (!strcasecmp (name, "rss"))
0043         return new RSS::Rss (d);
0044     else if (!strcasecmp (name, "feed"))
0045         return new ATOM::Feed (d);
0046     else if (!strcasecmp (name, "playlist"))
0047         return new XSPF::Playlist (d);
0048     else if (!strcasecmp (name, "opml"))
0049         return new OPML::Opml (d);
0050     else if (!strcasecmp (name, "url"))
0051         return new GenericURL (d, QString ());
0052     else if (!strcasecmp (name, "mrl") ||
0053             !strcasecmp (name, "document"))
0054         return new GenericMrl (d);
0055     return nullptr;
0056 }
0057 
0058 //-----------------------------------------------------------------------------
0059 
0060 QTextStream &KMPlayer::operator << (QTextStream &out, const XMLStringlet &txt) {
0061     int len = int (txt.str.size ());
0062     for (int i = 0; i < len; ++i) {
0063         if (txt.str [i] == QChar ('<')) {
0064             out <<  "&lt;";
0065         } else if (txt.str [i] == QChar ('>')) {
0066             out <<  "&gt;";
0067         } else if (txt.str [i] == QChar ('"')) {
0068             out <<  "&quot;";
0069         } else if (txt.str [i] == QChar ('&')) {
0070             out <<  "&amp;";
0071         } else
0072             out << txt.str [i];
0073     }
0074     return out;
0075 }
0076 
0077 //-----------------------------------------------------------------------------
0078 
0079 Connection::Connection (Node *invoker, Node *receiver, VirtualVoid *pl)
0080  : connectee (invoker), connecter (receiver), payload (pl) {
0081 #ifdef KMPLAYER_TEST_CONNECTION
0082     connection_counter++;
0083 #endif
0084 }
0085 
0086 ConnectionLink::ConnectionLink () : connection (nullptr) {}
0087 
0088 ConnectionLink::~ConnectionLink () {
0089     disconnect ();
0090 }
0091 
0092 bool ConnectionLink::connect (Node *send, MessageType msg, Node *rec,
0093         VirtualVoid *payload) {
0094     disconnect ();
0095     ConnectionList *list = nodeMessageReceivers (send, msg);
0096     if (list) {
0097         connection = new Connection (send, rec, payload);
0098         connection->list = list;
0099         connection->link = &connection;
0100         connection->prev = list->link_last;
0101         connection->next = nullptr;
0102         if (list->link_last)
0103             list->link_last->next = connection;
0104         else
0105             list->link_first = connection;
0106         list->link_last = connection;
0107     }
0108     return list;
0109 }
0110 
0111 void ConnectionLink::disconnect () const {
0112     if (connection) {
0113         Connection *tmp = connection;
0114         if (tmp->prev)
0115             tmp->prev->next = tmp->next;
0116         else
0117             tmp->list->link_first = tmp->next;
0118         if (tmp->next)
0119             tmp->next->prev = tmp->prev;
0120         else
0121             tmp->list->link_last = tmp->prev;
0122         *tmp->link = nullptr;
0123         if (tmp->list->link_next == tmp)
0124             tmp->list->link_next = tmp->next;
0125         delete tmp;
0126     }
0127     Q_ASSERT (!connection);
0128 }
0129 
0130 void ConnectionLink::assign (const ConnectionLink *link) const {
0131     disconnect ();
0132     connection = link->connection;
0133     link->connection = nullptr;
0134     if (connection)
0135         connection->link = &connection;
0136 }
0137 
0138 Node *ConnectionLink::signaler () const {
0139     return connection ? connection->connectee.ptr () : nullptr;
0140 }
0141 
0142 ConnectionList::ConnectionList ()
0143     : link_first (nullptr), link_last (nullptr), link_next (nullptr) {}
0144 
0145 ConnectionList::~ConnectionList () {
0146     clear ();
0147 }
0148 
0149 void ConnectionList::clear () {
0150     while (link_first) {
0151         Connection *tmp = link_first;
0152         link_first = tmp->next;
0153         *tmp->link = nullptr;
0154         delete tmp;
0155     }
0156     link_last = link_next = nullptr;
0157 }
0158 
0159 //-----------------------------------------------------------------------------
0160 
0161 TimerPosting::TimerPosting (int ms, unsigned eid)
0162  : Posting (nullptr, MsgEventTimer),
0163    event_id (eid),
0164    milli_sec (ms),
0165    interval (false) {}
0166 
0167 //-----------------------------------------------------------------------------
0168 
0169 Matrix::Matrix () : a (1.0), b (0.0), c (0.0), d (1.0), tx (0), ty (0) {}
0170 
0171 Matrix::Matrix (const Matrix & m)
0172  : a (m.a), b (m.b), c (m.c), d (m.d), tx (m.tx), ty (m.ty) {}
0173 
0174 Matrix::Matrix (Single xoff, Single yoff, float xscale, float yscale)
0175  : a (xscale), b (0.0), c (0.0), d (yscale), tx (xoff), ty (yoff) {}
0176 
0177 Matrix & Matrix::operator = (const Matrix& m)
0178 {
0179     a = m.a;
0180     b = m.b;
0181     c = m.c;
0182     d = m.d;
0183     tx = m.tx;
0184     ty = m.ty;
0185 
0186     return *this;
0187 }
0188 
0189 void Matrix::getXY (Single & x, Single & y) const {
0190     x = Single (x * a) + tx;
0191     y = Single (y * d) + ty;
0192 }
0193 
0194 void Matrix::getWH (Single &w, Single &h) const {
0195     w *= a;
0196     h *= d;
0197 }
0198 
0199 IRect Matrix::toScreen (const SRect &rect) const {
0200     return IRect (
0201             (int) (Single (rect.x () * a) + tx),
0202             (int) (Single (rect.y () * d) + ty),
0203             (int) (rect.width () * a),
0204             (int) (rect.height () * d));
0205 }
0206 
0207 SRect Matrix::toUser (const IRect &rect) const {
0208     if (a > 0.00001 && d > 0.00001) {
0209         return SRect (
0210                 Single ((Single (rect.x ()) - tx) / a),
0211                 Single ((Single (rect.y ()) - ty) / d),
0212                 rect.width () / a,
0213                 rect.height () / d);
0214     } else {
0215         qCWarning(LOG_KMPLAYER_COMMON) << "Not invering " << a << ", " << d << " scale";
0216         return SRect ();
0217     }
0218 }
0219 
0220 void Matrix::transform (const Matrix & matrix) {
0221     // TODO: rotate
0222     a *= matrix.a;
0223     d *= matrix.d;
0224     tx = Single (tx * matrix.a) + matrix.tx;
0225     ty = Single (ty * matrix.d) + matrix.ty;
0226 }
0227 
0228 void Matrix::scale (float sx, float sy) {
0229     a *= sx;
0230     d *= sy;
0231     tx *= sx;
0232     ty *= sy;
0233 }
0234 
0235 void Matrix::translate (Single x, Single y) {
0236     tx += x;
0237     ty += y;
0238 }
0239 
0240 //-----------------------------------------------------------------------------
0241 
0242 Node::Node (NodePtr & d, short _id)
0243  : m_doc (d), state (state_init), id (_id),
0244    auxiliary_node (false), open (false) {}
0245 
0246 Node::~Node () {
0247     clear ();
0248 }
0249 
0250 Document * Node::document () {
0251     return convertNode <Document> (m_doc);
0252 }
0253 
0254 Mrl * Node::mrl () {
0255     return nullptr;
0256 }
0257 
0258 const char * Node::nodeName () const {
0259     return "node";
0260 }
0261 
0262 void Node::setState (State nstate) {
0263     if (state != nstate && (state_init == nstate || state != state_resetting)) {
0264         State old = state;
0265         state = nstate;
0266         if (document ()->notify_listener)
0267             document()->notify_listener->stateElementChanged (this, old, state);
0268     }
0269 }
0270 
0271 void Node::activate () {
0272     //qCDebug(LOG_KMPLAYER_COMMON) << nodeName () << " Node::activate";
0273     setState (state_activated);
0274     if (firstChild ())
0275         firstChild ()->activate (); // activate only the first
0276     else
0277         finish (); // a quicky :-)
0278 }
0279 
0280 void Node::begin () {
0281     if (active ()) {
0282         setState (state_began);
0283     } else
0284         qCCritical(LOG_KMPLAYER_COMMON) << nodeName() << " begin call on not active element" << endl;
0285 }
0286 
0287 void Node::defer () {
0288     if (active ()) {
0289         setState (state_deferred);
0290     } else
0291         qCCritical(LOG_KMPLAYER_COMMON) << "Node::defer () call on not activated element" << endl;
0292 }
0293 
0294 void Node::undefer () {
0295     if (state == state_deferred) {
0296         if (firstChild () && firstChild ()->state > state_init) {
0297             state = state_began;
0298         } else {
0299             setState (state_activated);
0300             activate ();
0301         }
0302     } else
0303         qCWarning(LOG_KMPLAYER_COMMON) << nodeName () << " call on not deferred element";
0304 }
0305 
0306 void Node::finish () {
0307     if (active ()) {
0308         setState (state_finished);
0309         if (m_parent)
0310             document ()->post (m_parent, new Posting (this, MsgChildFinished));
0311         else
0312             deactivate (); // document deactivates itself on finish
0313     } else
0314         qCWarning(LOG_KMPLAYER_COMMON) <<"Node::finish () call on not active element";
0315 }
0316 
0317 void Node::deactivate () {
0318     //qCDebug(LOG_KMPLAYER_COMMON) << nodeName () << " Node::deactivate";
0319     bool need_finish (unfinished ());
0320     if (state_resetting != state)
0321         setState (state_deactivated);
0322     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
0323         if (e->state > state_init && e->state < state_deactivated)
0324             e->deactivate ();
0325         else
0326             break; // remaining not yet activated
0327     }
0328     if (need_finish && m_parent && m_parent->active ())
0329         document ()->post (m_parent, new Posting (this, MsgChildFinished));
0330 }
0331 
0332 void Node::reset () {
0333     //qCDebug(LOG_KMPLAYER_COMMON) << nodeName () << " Node::reset";
0334     if (active ()) {
0335         setState (state_resetting);
0336         deactivate ();
0337     }
0338     setState (state_init);
0339     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
0340         if (e->state != state_init)
0341             e->reset ();
0342         // else
0343         //    break; // rest not activated yet
0344     }
0345 }
0346 
0347 void Node::clear () {
0348     clearChildren ();
0349 }
0350 
0351 void Node::clearChildren () {
0352     if (m_doc)
0353         document()->m_tree_version++;
0354     while (m_first_child != m_last_child) {
0355         // avoid stack abuse with 10k children derefing each other
0356         m_last_child->m_parent = nullptr;
0357         m_last_child = m_last_child->m_prev;
0358         m_last_child->m_next = nullptr;
0359     }
0360     if (m_first_child)
0361         m_first_child->m_parent = nullptr;
0362     m_first_child = m_last_child = nullptr;
0363 }
0364 
0365 template <>
0366 void TreeNode<Node>::appendChild (Node *c) {
0367     static_cast <Node *> (this)->document()->m_tree_version++;
0368     Q_ASSERT (!c->parentNode ());
0369     appendChildImpl (c);
0370 }
0371 
0372 template <>
0373 void TreeNode<Node>::insertBefore (Node *c, Node *b) {
0374     Q_ASSERT (!c->parentNode ());
0375     static_cast <Node *> (this)->document()->m_tree_version++;
0376     insertBeforeImpl (c, b);
0377 }
0378 
0379 template <>
0380 void TreeNode<Node>::removeChild (NodePtr c) {
0381     static_cast <Node *> (this)->document()->m_tree_version++;
0382     removeChildImpl (c);
0383 }
0384 
0385 void Node::replaceChild (NodePtr _new, NodePtr old) {
0386     document()->m_tree_version++;
0387     if (old->m_prev) {
0388         old->m_prev->m_next = _new;
0389         _new->m_prev = old->m_prev;
0390         old->m_prev = nullptr;
0391     } else {
0392         _new->m_prev = nullptr;
0393         m_first_child = _new;
0394     }
0395     if (old->m_next) {
0396         old->m_next->m_prev = _new;
0397         _new->m_next = old->m_next;
0398         old->m_next = nullptr;
0399     } else {
0400         _new->m_next = nullptr;
0401         m_last_child = _new;
0402     }
0403     _new->m_parent = this;
0404     old->m_parent = nullptr;
0405 }
0406 
0407 Node *Node::childFromTag (const QString &) {
0408     return nullptr;
0409 }
0410 
0411 void Node::characterData (const QString & s) {
0412     document()->m_tree_version++;
0413     if (!m_last_child || m_last_child->id != id_node_text)
0414         appendChild (new TextNode (m_doc, s));
0415     else
0416         convertNode <TextNode> (m_last_child)->appendText (s);
0417 }
0418 
0419 void Node::normalize () {
0420     Node *e = firstChild ();
0421     while (e) {
0422         Node *tmp = e->nextSibling ();
0423         if (!e->isElementNode () && e->id == id_node_text) {
0424             QString val = e->nodeValue ().simplified ();
0425             if (val.isEmpty ())
0426                 removeChild (e);
0427             else
0428                 static_cast <TextNode *> (e)->setText (val);
0429         } else
0430             e->normalize ();
0431         e = tmp;
0432     }
0433 }
0434 
0435 static void getInnerText (const Node *p, QTextStream & out) {
0436     for (Node *e = p->firstChild (); e; e = e->nextSibling ()) {
0437         if (e->id == id_node_text || e->id == id_node_cdata)
0438             out << e->nodeValue ();
0439         else
0440             getInnerText (e, out);
0441     }
0442 }
0443 
0444 QString Node::innerText () const {
0445     QString buf;
0446     QTextStream out (&buf, QIODevice::WriteOnly);
0447     getInnerText (this, out);
0448     return buf;
0449 }
0450 
0451 static void getOuterXML (const Node *p, QTextStream & out, int depth) {
0452     if (!p->isElementNode ()) { // #text or #cdata
0453         if (p->id == id_node_cdata)
0454             out << "<![CDATA[" << p->nodeValue () << "]]>" << QChar ('\n');
0455         else
0456             out << XMLStringlet (p->nodeValue ()) << QChar ('\n');
0457     } else {
0458         const Element *e = static_cast <const Element *> (p);
0459         QString indent (QString ().fill (QChar (' '), depth));
0460         out << indent << QChar ('<') << XMLStringlet (e->nodeName ());
0461         for (Attribute *a = e->attributes().first(); a; a = a->nextSibling())
0462             out << " " << XMLStringlet (a->name ().toString ()) <<
0463                 "=\"" << XMLStringlet (a->value ()) << "\"";
0464         if (e->hasChildNodes ()) {
0465             out << QChar ('>') << QChar ('\n');
0466             for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0467                 getOuterXML (c, out, depth + 1);
0468             out << indent << QString("</") << XMLStringlet (e->nodeName()) <<
0469                 QChar ('>') << QChar ('\n');
0470         } else
0471             out << QString ("/>") << QChar ('\n');
0472     }
0473 }
0474 
0475 QString Node::innerXML () const {
0476     QString buf;
0477     QTextStream out (&buf, QIODevice::WriteOnly);
0478     for (Node *e = firstChild (); e; e = e->nextSibling ())
0479         getOuterXML (e, out, 0);
0480     return buf;
0481 }
0482 
0483 QString Node::outerXML () const {
0484     QString buf;
0485     QTextStream out (&buf, QIODevice::WriteOnly);
0486     getOuterXML (this, out, 0);
0487     return buf;
0488 }
0489 
0490 Node::PlayType Node::playType () {
0491     return play_type_none;
0492 }
0493 
0494 void Node::opened () {
0495     open = true;
0496 }
0497 
0498 void Node::closed () {
0499     open = false;
0500 }
0501 
0502 void Node::message (MessageType msg, void *content) {
0503     switch (msg) {
0504 
0505     case MsgChildFinished: {
0506         Posting *post = (Posting *) content;
0507         if (unfinished ()) {
0508             if (post->source && post->source->state == state_finished)
0509                 post->source->deactivate ();
0510             if (post->source && post->source->nextSibling ())
0511                 post->source->nextSibling ()->activate ();
0512             else
0513                 finish (); // we're done
0514         }
0515         break;
0516     }
0517 
0518     default:
0519         break;
0520     }
0521 }
0522 
0523 void *Node::role (RoleType msg, void *) {
0524     switch (msg) {
0525     case RoleReady:
0526         return MsgBool (true);
0527     default:
0528         break;
0529     }
0530     return nullptr;
0531 }
0532 
0533 void Node::deliver (MessageType msg, void *content) {
0534     ConnectionList *nl = nodeMessageReceivers (this, msg);
0535     if (nl)
0536         for (Connection *c = nl->first(); c; c = nl->next ())
0537             if (c->connecter)
0538                 c->connecter->message (msg, content);
0539 }
0540 
0541 void Node::accept (Visitor * v) {
0542     v->visit (this);
0543 }
0544 
0545 QString Node::nodeValue () const {
0546     return innerText ().trimmed ();
0547 }
0548 
0549 //-----------------------------------------------------------------------------
0550 
0551 namespace {
0552     struct ParamValue {
0553         QString val;
0554         QStringList  * modifications;
0555         ParamValue (const QString & v) : val (v), modifications (nullptr) {}
0556         ~ParamValue () { delete modifications; }
0557         QString value ();
0558         void setValue (const QString & v) { val = v; }
0559     };
0560     typedef QMap <TrieString, ParamValue *> ParamMap;
0561 }
0562 
0563 namespace KMPlayer {
0564     class ElementPrivate
0565     {
0566     public:
0567         ~ElementPrivate ();
0568         ParamMap params;
0569         void clear ();
0570     };
0571 }
0572 
0573 QString ParamValue::value() {
0574     return modifications && modifications->size ()
0575         ? modifications->back () : val;
0576 }
0577 
0578 ElementPrivate::~ElementPrivate () {
0579     clear ();
0580 }
0581 
0582 void ElementPrivate::clear () {
0583     const ParamMap::iterator e = params.end ();
0584     for (ParamMap::iterator i = params.begin (); i != e; ++i)
0585         delete i.value ();
0586     params.clear ();
0587 }
0588 
0589 Element::Element (NodePtr & d, short id)
0590     : Node (d, id), d (new ElementPrivate) {}
0591 
0592 Element::~Element () {
0593     delete d;
0594 }
0595 
0596 void Element::setParam (const TrieString &name, const QString &val, int *mid) {
0597     ParamValue * pv = d->params [name];
0598     if (!pv) {
0599         pv = new ParamValue (mid ? getAttribute (name) : val);
0600         d->params.insert (name, pv);
0601     }
0602     if (mid) {
0603         if (!pv->modifications)
0604             pv->modifications = new QStringList;
0605         if (*mid >= 0 && *mid < int (pv->modifications->size ())) {
0606             (*pv->modifications) [*mid] = val;
0607         } else {
0608             *mid = pv->modifications->size ();
0609             pv->modifications->push_back (val);
0610         }
0611     } else {
0612         pv->setValue (val);
0613     }
0614     parseParam (name, val);
0615 }
0616 
0617 QString Element::param (const TrieString & name) {
0618     ParamValue * pv = d->params [name];
0619     if (pv)
0620         return pv->value ();
0621     return getAttribute (name);
0622 }
0623 
0624 void Element::resetParam (const TrieString &name, int mid) {
0625     ParamValue * pv = d->params [name];
0626     if (pv && pv->modifications) {
0627         if (int (pv->modifications->size ()) > mid && mid > -1) {
0628             (*pv->modifications) [mid] = QString ();
0629             while (pv->modifications->size () > 0 &&
0630                     pv->modifications->back ().isNull ())
0631                 pv->modifications->pop_back ();
0632         }
0633         QString val = pv->value ();
0634         if (pv->modifications->size () == 0) {
0635             delete pv->modifications;
0636             pv->modifications = nullptr;
0637             if (val.isNull ()) {
0638                 delete pv;
0639                 d->params.remove (name);
0640             }
0641         }
0642         parseParam (name, val);
0643     } else
0644         qCCritical(LOG_KMPLAYER_COMMON) << "resetting " << name.toString() << " that doesn't exists" << endl;
0645 }
0646 
0647 void Element::setAttribute (const TrieString & name, const QString & value) {
0648     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
0649         if (name == a->name ()) {
0650             if (value.isNull ())
0651                 m_attributes.remove (a);
0652             else
0653                 a->setValue (value);
0654             return;
0655         }
0656     if (!value.isNull ())
0657         m_attributes.append (new Attribute (TrieString (), name, value));
0658 }
0659 
0660 QString Element::getAttribute (const TrieString & name) {
0661     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
0662         if (name == a->name ())
0663             return a->value ();
0664     return QString ();
0665 }
0666 
0667 void Element::init () {
0668     d->clear();
0669     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
0670         QString v = a->value ();
0671         int p = v.indexOf ('{');
0672         if (p > -1) {
0673             int q = v.indexOf ('}', p + 1);
0674             if (q > -1)
0675                 continue;
0676         }
0677         parseParam (a->name (), v);
0678     }
0679 }
0680 
0681 void Element::reset () {
0682     d->clear();
0683     Node::reset ();
0684 }
0685 
0686 void Element::clear () {
0687     m_attributes = AttributeList (); // remove attributes
0688     d->clear();
0689     Node::clear ();
0690 }
0691 
0692 void Element::setAttributes (const AttributeList &attrs) {
0693     m_attributes = attrs;
0694 }
0695 
0696 void Element::accept (Visitor * v) {
0697     v->visit (this);
0698 }
0699 
0700 //-----------------------------------------------------------------------------
0701 
0702 Attribute::Attribute (const TrieString &ns, const TrieString &n, const QString &v)
0703   : m_namespace (ns), m_name (n), m_value (v) {}
0704 
0705 void Attribute::setName (const TrieString & n) {
0706     m_name = n;
0707 }
0708 
0709 void Attribute::setValue (const QString & v) {
0710     m_value = v;
0711 }
0712 
0713 //-----------------------------------------------------------------------------
0714 
0715 QString PlaylistRole::caption () const {
0716     return title;
0717 }
0718 
0719 void PlaylistRole::setCaption (const QString &s) {
0720     title = s;
0721 }
0722 
0723 //-----------------------------------------------------------------------------
0724 
0725 static bool hasMrlChildren (const NodePtr & e) {
0726     for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0727         if (c->isPlayable () || hasMrlChildren (c))
0728             return true;
0729     return false;
0730 }
0731 
0732 Mrl::Mrl (NodePtr & d, short id)
0733     : Element (d, id), cached_ismrl_version (~0),
0734       media_info (nullptr),
0735       aspect (0), repeat (0),
0736       view_mode (SingleMode),
0737       resolved (false), bookmarkable (true), access_granted (false) {}
0738 
0739 Mrl::~Mrl () {
0740     delete media_info;
0741 }
0742 
0743 Node::PlayType Mrl::playType () {
0744     if (cached_ismrl_version != document()->m_tree_version) {
0745         bool ismrl = !hasMrlChildren (this);
0746         cached_play_type = ismrl ? play_type_unknown : play_type_none;
0747         cached_ismrl_version = document()->m_tree_version;
0748     }
0749     return cached_play_type;
0750 }
0751 
0752 QString Mrl::absolutePath () const
0753 {
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 = QUrl(mrl->absolutePath ()).resolved(QUrl(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 nullptr;
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 nullptr;
0816 
0817     case RolePlaylist:
0818         if (title.isEmpty ())
0819             title = src;
0820         return !title.isEmpty () ? (PlaylistRole *) this : nullptr;
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     qCDebug(LOG_KMPLAYER_COMMON) << 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 = nullptr;
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 = QUrl(abs).resolved(QUrl(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 (nullptr),
0944    paused_queue (nullptr),
0945    cur_event (nullptr),
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     qCDebug(LOG_KMPLAYER_COMMON) << "~Document " << src;
0953 }
0954 
0955 static Node *getElementByIdImpl (Node *n, const QString & id, bool inter) {
0956     NodePtr elm;
0957     if (!n->isElementNode ())
0958         return nullptr;
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 nullptr;
0984 }
0985 
0986 void Document::dispose () {
0987     clear ();
0988     m_doc = nullptr;
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 = nullptr;
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 = nullptr;
1021 }
1022 
1023 static inline
1024 int diffTime (const struct timeval & tv1, const struct timeval & tv2) {
1025     //qCDebug(LOG_KMPLAYER_COMMON) << "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 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, nullptr);
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 = nullptr;
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     //qCDebug(LOG_KMPLAYER_COMMON) << "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 = nullptr;
1140     for (EventData *ed = queue; ed; ed = ed->next) {
1141         if (e == ed->event)
1142             return ed;
1143         *prev = ed;
1144     }
1145     return nullptr;
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 = nullptr;
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             qCCritical(LOG_KMPLAYER_COMMON) << "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 = nullptr;
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             qCCritical(LOG_KMPLAYER_COMMON) << "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 = nullptr;
1211         delete ed;
1212     } else {
1213         qCCritical(LOG_KMPLAYER_COMMON) << "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                 qCCritical(LOG_KMPLAYER_COMMON) << "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 = nullptr;
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 = nullptr;
1260     }
1261     setNextTimeout (now);
1262 }
1263 
1264 PostponePtr Document::postpone () {
1265     if (postpone_ref)
1266         return postpone_ref;
1267     qCDebug(LOG_KMPLAYER_COMMON) << "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     qCDebug(LOG_KMPLAYER_COMMON) << "proceed";
1285     postpone_ref = nullptr;
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 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 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 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 : nullptr;
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 KMPLAYERCOMMON_EXPORT CacheAllocator *KMPlayer::shared_data_cache_allocator = nullptr;
1413 
1414 //-----------------------------------------------------------------------------
1415 
1416 namespace KMPlayer {
1417 
1418 class DocumentBuilder
1419 {
1420     int m_ignore_depth;
1421     bool m_set_opener;
1422     bool m_root_is_first;
1423     NodePtr m_node;
1424     NodePtr m_root;
1425 public:
1426     DocumentBuilder (NodePtr d, bool set_opener);
1427     ~DocumentBuilder () {}
1428     bool startTag (const QString & tag, const AttributeList &attr);
1429     bool endTag (const QString & tag);
1430     bool characterData (const QString & data);
1431     bool cdataData (const QString & data);
1432 #ifdef KMPLAYER_WITH_EXPAT
1433     void cdataStart ();
1434     void cdataEnd ();
1435 private:
1436     bool in_cdata;
1437     QString cdata;
1438 #endif
1439 };
1440 
1441 } // namespace KMPlayer
1442 
1443 DocumentBuilder::DocumentBuilder (NodePtr d, bool set_opener)
1444  : m_ignore_depth (0), m_set_opener (set_opener), m_root_is_first (false)
1445  , m_node (d), m_root (d)
1446 #ifdef KMPLAYER_WITH_EXPAT
1447  , in_cdata (false)
1448 #endif
1449 {}
1450 
1451 bool DocumentBuilder::startTag(const QString &tag, const AttributeList &attr) {
1452     if (m_ignore_depth) {
1453         m_ignore_depth++;
1454         //qCDebug(LOG_KMPLAYER_COMMON) << "Warning: ignored tag " << tag.latin1 () << " ignore depth = " << m_ignore_depth;
1455     } else if (!m_node) {
1456         return false; // had underflow
1457     } else {
1458         NodePtr n = m_node->childFromTag (tag);
1459         if (!n) {
1460             qCDebug(LOG_KMPLAYER_COMMON) << "Warning: unknown tag " << tag.toLocal8Bit ().constData();
1461             NodePtr doc = m_root->document ();
1462             n = new DarkNode (doc, tag.toUtf8 ());
1463         }
1464         //qCDebug(LOG_KMPLAYER_COMMON) << "Found tag " << tag;
1465         if (n->isElementNode ())
1466             convertNode <Element> (n)->setAttributes (attr);
1467         if (m_node == n && m_node == m_root)
1468             m_root_is_first = true;
1469         else
1470             m_node->appendChild (n);
1471         if (m_set_opener && m_node == m_root) {
1472             Mrl * mrl = n->mrl ();
1473             if (mrl)
1474                 mrl->opener = m_root;
1475         }
1476         n->opened ();
1477         m_node = n;
1478     }
1479     return true;
1480 }
1481 
1482 bool DocumentBuilder::endTag (const QString & tag) {
1483     if (m_ignore_depth) { // endtag to ignore
1484         m_ignore_depth--;
1485         qCDebug(LOG_KMPLAYER_COMMON) << "Warning: ignored end tag " << " ignore depth = " << m_ignore_depth;
1486     } else if (!m_node) {
1487         return false; // had underflow
1488     } else {  // endtag
1489         NodePtr n = m_node;
1490         while (n) {
1491             if (!strcasecmp (n->nodeName (), tag.toLocal8Bit ().constData ()) &&
1492                     (m_root_is_first || n != m_root)) {
1493                 while (n != m_node) {
1494                     qCWarning(LOG_KMPLAYER_COMMON) << m_node->nodeName () << " not closed";
1495                     if (m_root == m_node->parentNode ())
1496                         break;
1497                     m_node->closed ();
1498                     m_node = m_node->parentNode ();
1499                 }
1500                 break;
1501             }
1502             if (n == m_root) {
1503                 if (n == m_node) {
1504                     qCCritical(LOG_KMPLAYER_COMMON) << "m_node == m_doc, stack underflow " << endl;
1505                     return false;
1506                 }
1507                 qCWarning(LOG_KMPLAYER_COMMON) << "endtag: no match " << tag.toLocal8Bit ().constData();
1508                 break;
1509             } else
1510                  qCWarning(LOG_KMPLAYER_COMMON) << "tag " << tag << " not " << n->nodeName ();
1511             n = n ->parentNode ();
1512         }
1513         //qCDebug(LOG_KMPLAYER_COMMON) << "end tag " << tag;
1514         m_node->closed ();
1515         m_node = m_node->parentNode ();
1516     }
1517     return true;
1518 }
1519 
1520 bool DocumentBuilder::characterData (const QString & data) {
1521     if (!m_ignore_depth && m_node) {
1522 #ifdef KMPLAYER_WITH_EXPAT
1523         if (in_cdata)
1524             cdata += data;
1525         else
1526 #endif
1527             m_node->characterData (data);
1528     }
1529     //qCDebug(LOG_KMPLAYER_COMMON) << "characterData " << d.latin1();
1530     return !!m_node;
1531 }
1532 
1533 bool DocumentBuilder::cdataData (const QString & data) {
1534     if (!m_ignore_depth && m_node) {
1535         NodePtr d = m_node->document ();
1536         m_node->appendChild (new CData (d, data));
1537     }
1538     //qCDebug(LOG_KMPLAYER_COMMON) << "cdataData " << d.latin1();
1539     return !!m_node;
1540 }
1541 
1542 #ifdef KMPLAYER_WITH_EXPAT
1543 
1544 void DocumentBuilder::cdataStart () {
1545     cdata.truncate (0);
1546     in_cdata = true;
1547 }
1548 
1549 void DocumentBuilder::cdataEnd () {
1550     cdataData (cdata);
1551     cdata.truncate (0);
1552     in_cdata = false;
1553 }
1554 
1555 static void startTag (void *data, const char * tag, const char **attr) {
1556     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1557     AttributeList attributes;
1558     if (attr && attr [0]) {
1559         for (int i = 0; attr[i]; i += 2)
1560             attributes.append (new Attribute (
1561                         TrieString(),
1562                         QString::fromUtf8 (attr [i]),
1563                         QString::fromUtf8 (attr [i+1])));
1564     }
1565     builder->startTag (QString::fromUtf8 (tag), attributes);
1566 }
1567 
1568 static void endTag (void *data, const char * tag) {
1569     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1570     builder->endTag (QString::fromUtf8 (tag));
1571 }
1572 
1573 static void characterData (void *data, const char *s, int len) {
1574     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1575     char * buf = new char [len + 1];
1576     strncpy (buf, s, len);
1577     buf[len] = 0;
1578     builder->characterData (QString::fromUtf8 (buf));
1579     delete [] buf;
1580 }
1581 
1582 static void cdataStart (void *data) {
1583     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1584     builder->cdataStart ();
1585 }
1586 
1587 static void cdataEnd (void *data) {
1588     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1589     builder->cdataEnd ();
1590 }
1591 
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             qCWarning(LOG_KMPLAYER_COMMON) << 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             qCWarning(LOG_KMPLAYER_COMMON) << 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 SimpleSAXParser
1626 {
1627     enum Token { tok_empty, tok_text, tok_white_space, tok_angle_open,
1628         tok_equal, tok_double_quote, tok_single_quote, tok_angle_close,
1629         tok_slash, tok_exclamation, tok_amp, tok_hash, tok_colon,
1630         tok_semi_colon, tok_question_mark, tok_cdata_start };
1631 public:
1632     struct TokenInfo {
1633         TokenInfo () : token (tok_empty) {}
1634         void *operator new (size_t);
1635         void operator delete (void *);
1636         Token token;
1637         QString string;
1638         SharedPtr <TokenInfo> next;
1639     };
1640     typedef SharedPtr <TokenInfo> TokenInfoPtr;
1641     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) {}
1642     virtual ~SimpleSAXParser () {};
1643     bool parse (QTextStream & d);
1644 private:
1645     QTextStream * data;
1646     DocumentBuilder & builder;
1647     int position;
1648     QChar next_char;
1649     enum State {
1650         InTag, InStartTag, InPITag, InDTDTag, InEndTag, InAttributes, InContent, InCDATA, InComment
1651     };
1652     struct StateInfo {
1653         StateInfo (State s, SharedPtr <StateInfo> n) : state (s), next (n) {}
1654         State state;
1655         QString data;
1656         SharedPtr <StateInfo> next;
1657     };
1658     SharedPtr <StateInfo> m_state;
1659     TokenInfoPtr next_token, token, prev_token;
1660     // for element reading
1661     QString tagname;
1662     AttributeList m_attributes;
1663     QString attr_namespace, attr_name, attr_value;
1664     QString cdata;
1665     bool equal_seen;
1666     bool in_dbl_quote;
1667     bool in_sngl_quote;
1668     bool have_error;
1669     bool no_entitity_look_ahead;
1670     bool have_next_char;
1671 
1672     bool readTag ();
1673     bool readEndTag ();
1674     bool readAttributes ();
1675     bool readPI ();
1676     bool readDTD ();
1677     bool readCDATA ();
1678     bool readComment ();
1679     bool nextToken ();
1680     void push ();
1681     void push_attribute ();
1682 };
1683 
1684 } // namespace
1685 
1686 static CacheAllocator token_pool (sizeof (SimpleSAXParser::TokenInfo));
1687 
1688 inline void *SimpleSAXParser::TokenInfo::operator new (size_t) {
1689     return token_pool.alloc ();
1690 }
1691 
1692 inline void SimpleSAXParser::TokenInfo::operator delete (void *p) {
1693     token_pool.dealloc (p);
1694 }
1695 
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     //qCDebug(LOG_KMPLAYER_COMMON) << 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         //qCDebug(LOG_KMPLAYER_COMMON) << "push " << token->string;
1726     }
1727 }
1728 
1729 void SimpleSAXParser::push_attribute () {
1730     //qCDebug(LOG_KMPLAYER_COMMON) << "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                     //qCDebug(LOG_KMPLAYER_COMMON) << "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                     //qCDebug(LOG_KMPLAYER_COMMON) << "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 (nullptr, 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         //qCDebug(LOG_KMPLAYER_COMMON) << "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             //qCDebug(LOG_KMPLAYER_COMMON) << "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             //qCDebug(LOG_KMPLAYER_COMMON) << "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             //qCDebug(LOG_KMPLAYER_COMMON) << "close mark:";
1924                 closed = true;
1925                 break;
1926             } else {
1927                 token = mark_token;
1928             //qCDebug(LOG_KMPLAYER_COMMON) << "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                   qCDebug(LOG_KMPLAYER_COMMON) << "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         //qCDebug(LOG_KMPLAYER_COMMON) << "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     //qCDebug(LOG_KMPLAYER_COMMON) << "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 = nullptr;
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     //qCDebug(LOG_KMPLAYER_COMMON) << "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     //qCDebug(LOG_KMPLAYER_COMMON) << "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