1 /**
2  * Copyright (C) 2004 by Koos Vriezen <koos.vriezen@gmail.com>
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License version 2 as published by the Free Software Foundation.
7  *
8  *  This library is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *  Library General Public License for more details.
12  *
13  *  You should have received a copy of the GNU Library General Public License
14  *  along with this library; see the file COPYING.LIB.  If not, write to
15  *  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
16  *  Boston, MA 02110-1301, USA.
17  **/
18 
19 #include "config-kmplayer.h"
20 #include <time.h>
21 
22 #include <qtextstream.h>
23 #include <kdebug.h>
24 #ifdef KMPLAYER_WITH_EXPAT
25 #include <expat.h>
26 #endif
27 #include "kmplayerplaylist.h"
28 #include "kmplayer_asx.h"
29 #include "kmplayer_atom.h"
30 #include "kmplayer_opml.h"
31 #include "kmplayer_rp.h"
32 #include "kmplayer_rss.h"
33 #include "kmplayer_smil.h"
34 #include "kmplayer_xspf.h"
35 #include "mediaobject.h"
36 
37 #include <kurl.h>
38 
39 #ifdef SHAREDPTR_DEBUG
40 KMPLAYER_EXPORT int shared_data_count;
41 #endif
42 
43 using namespace KMPlayer;
44 
45 //-----------------------------------------------------------------------------
46 
fromXMLDocumentTag(NodePtr & d,const QString & tag)47 Node *KMPlayer::fromXMLDocumentTag (NodePtr & d, const QString & tag) {
48     const QByteArray ba = tag.toAscii ();
49     const char * const name = ba.constData();
50     if (!strcmp (name, "smil"))
51         return new SMIL::Smil (d);
52     else if (!strcasecmp (name, "asx"))
53         return new ASX::Asx (d);
54     else if (!strcasecmp (name, "imfl"))
55         return new RP::Imfl (d);
56     else if (!strcasecmp (name, "rss"))
57         return new RSS::Rss (d);
58     else if (!strcasecmp (name, "feed"))
59         return new ATOM::Feed (d);
60     else if (!strcasecmp (name, "playlist"))
61         return new XSPF::Playlist (d);
62     else if (!strcasecmp (name, "opml"))
63         return new OPML::Opml (d);
64     else if (!strcasecmp (name, "url"))
65         return new GenericURL (d, QString ());
66     else if (!strcasecmp (name, "mrl") ||
67             !strcasecmp (name, "document"))
68         return new GenericMrl (d);
69     return 0L;
70 }
71 
72 //-----------------------------------------------------------------------------
73 
operator <<(QTextStream & out,const XMLStringlet & txt)74 QTextStream &KMPlayer::operator << (QTextStream &out, const XMLStringlet &txt) {
75     int len = int (txt.str.size ());
76     for (int i = 0; i < len; ++i) {
77         if (txt.str [i] == QChar ('<')) {
78             out <<  "&lt;";
79         } else if (txt.str [i] == QChar ('>')) {
80             out <<  "&gt;";
81         } else if (txt.str [i] == QChar ('"')) {
82             out <<  "&quot;";
83         } else if (txt.str [i] == QChar ('&')) {
84             out <<  "&amp;";
85         } else
86             out << txt.str [i];
87     }
88     return out;
89 }
90 
91 //-----------------------------------------------------------------------------
92 
Connection(Node * invoker,Node * receiver,VirtualVoid * pl)93 Connection::Connection (Node *invoker, Node *receiver, VirtualVoid *pl)
94  : connectee (invoker), connecter (receiver), payload (pl) {
95 #ifdef KMPLAYER_TEST_CONNECTION
96     connection_counter++;
97 #endif
98 }
99 
ConnectionLink()100 ConnectionLink::ConnectionLink () : connection (NULL) {}
101 
~ConnectionLink()102 ConnectionLink::~ConnectionLink () {
103     disconnect ();
104 }
105 
connect(Node * send,MessageType msg,Node * rec,VirtualVoid * payload)106 bool ConnectionLink::connect (Node *send, MessageType msg, Node *rec,
107         VirtualVoid *payload) {
108     disconnect ();
109     ConnectionList *list = nodeMessageReceivers (send, msg);
110     if (list) {
111         connection = new Connection (send, rec, payload);
112         connection->list = list;
113         connection->link = &connection;
114         connection->prev = list->link_last;
115         connection->next = NULL;
116         if (list->link_last)
117             list->link_last->next = connection;
118         else
119             list->link_first = connection;
120         list->link_last = connection;
121     }
122     return list;
123 }
124 
disconnect() const125 void ConnectionLink::disconnect () const {
126     if (connection) {
127         Connection *tmp = connection;
128         if (tmp->prev)
129             tmp->prev->next = tmp->next;
130         else
131             tmp->list->link_first = tmp->next;
132         if (tmp->next)
133             tmp->next->prev = tmp->prev;
134         else
135             tmp->list->link_last = tmp->prev;
136         *tmp->link = NULL;
137         if (tmp->list->link_next == tmp)
138             tmp->list->link_next = tmp->next;
139         delete tmp;
140     }
141     ASSERT (!connection);
142 }
143 
assign(const ConnectionLink * link) const144 void ConnectionLink::assign (const ConnectionLink *link) const {
145     disconnect ();
146     connection = link->connection;
147     link->connection = NULL;
148     if (connection)
149         connection->link = &connection;
150 }
151 
signaler() const152 Node *ConnectionLink::signaler () const {
153     return connection ? connection->connectee.ptr () : NULL;
154 }
155 
ConnectionList()156 ConnectionList::ConnectionList ()
157     : link_first (NULL), link_last (NULL), link_next (NULL) {}
158 
~ConnectionList()159 ConnectionList::~ConnectionList () {
160     clear ();
161 }
162 
clear()163 void ConnectionList::clear () {
164     while (link_first) {
165         Connection *tmp = link_first;
166         link_first = tmp->next;
167         *tmp->link = NULL;
168         delete tmp;
169     }
170     link_last = link_next = NULL;
171 }
172 
173 //-----------------------------------------------------------------------------
174 
TimerPosting(int ms,unsigned eid)175 KDE_NO_CDTOR_EXPORT TimerPosting::TimerPosting (int ms, unsigned eid)
176  : Posting (NULL, MsgEventTimer),
177    event_id (eid),
178    milli_sec (ms),
179    interval (false) {}
180 
181 //-----------------------------------------------------------------------------
182 
Matrix()183 Matrix::Matrix () : a (1.0), b (0.0), c (0.0), d (1.0), tx (0), ty (0) {}
184 
Matrix(const Matrix & m)185 Matrix::Matrix (const Matrix & m)
186  : a (m.a), b (m.b), c (m.c), d (m.d), tx (m.tx), ty (m.ty) {}
187 
Matrix(Single xoff,Single yoff,float xscale,float yscale)188 Matrix::Matrix (Single xoff, Single yoff, float xscale, float yscale)
189  : a (xscale), b (0.0), c (0.0), d (yscale), tx (xoff), ty (yoff) {}
190 
getXY(Single & x,Single & y) const191 void Matrix::getXY (Single & x, Single & y) const {
192     x = Single (x * a) + tx;
193     y = Single (y * d) + ty;
194 }
195 
getWH(Single & w,Single & h) const196 void Matrix::getWH (Single &w, Single &h) const {
197     w *= a;
198     h *= d;
199 }
200 
toScreen(const SRect & rect) const201 IRect Matrix::toScreen (const SRect &rect) const {
202     return IRect (
203             (int) (Single (rect.x () * a) + tx),
204             (int) (Single (rect.y () * d) + ty),
205             (int) (rect.width () * a),
206             (int) (rect.height () * d));
207 }
208 
toUser(const IRect & rect) const209 SRect Matrix::toUser (const IRect &rect) const {
210     if (a > 0.00001 && d > 0.00001) {
211         return SRect (
212                 Single ((Single (rect.x ()) - tx) / a),
213                 Single ((Single (rect.y ()) - ty) / d),
214                 rect.width () / a,
215                 rect.height () / d);
216     } else {
217         kWarning () << "Not invering " << a << ", " << d << " scale";
218         return SRect ();
219     }
220 }
221 
transform(const Matrix & matrix)222 void Matrix::transform (const Matrix & matrix) {
223     // TODO: rotate
224     a *= matrix.a;
225     d *= matrix.d;
226     tx = Single (tx * matrix.a) + matrix.tx;
227     ty = Single (ty * matrix.d) + matrix.ty;
228 }
229 
scale(float sx,float sy)230 void Matrix::scale (float sx, float sy) {
231     a *= sx;
232     d *= sy;
233     tx *= sx;
234     ty *= sy;
235 }
236 
translate(Single x,Single y)237 void Matrix::translate (Single x, Single y) {
238     tx += x;
239     ty += y;
240 }
241 
242 //-----------------------------------------------------------------------------
243 
Node(NodePtr & d,short _id)244 KDE_NO_CDTOR_EXPORT Node::Node (NodePtr & d, short _id)
245  : m_doc (d), state (state_init), id (_id),
246    auxiliary_node (false), open (false) {}
247 
~Node()248 Node::~Node () {
249     clear ();
250 }
251 
document()252 Document * Node::document () {
253     return convertNode <Document> (m_doc);
254 }
255 
mrl()256 Mrl * Node::mrl () {
257     return 0L;
258 }
259 
nodeName() const260 const char * Node::nodeName () const {
261     return "node";
262 }
263 
setState(State nstate)264 void Node::setState (State nstate) {
265     if (state != nstate && (state_init == nstate || state != state_resetting)) {
266         State old = state;
267         state = nstate;
268         if (document ()->notify_listener)
269             document()->notify_listener->stateElementChanged (this, old, state);
270     }
271 }
272 
activate()273 void Node::activate () {
274     //kDebug () << nodeName () << " Node::activate";
275     setState (state_activated);
276     if (firstChild ())
277         firstChild ()->activate (); // activate only the first
278     else
279         finish (); // a quicky :-)
280 }
281 
begin()282 void Node::begin () {
283     if (active ()) {
284         setState (state_began);
285     } else
286         kError () << nodeName() << " begin call on not active element" << endl;
287 }
288 
defer()289 void Node::defer () {
290     if (active ()) {
291         setState (state_deferred);
292     } else
293         kError () << "Node::defer () call on not activated element" << endl;
294 }
295 
undefer()296 void Node::undefer () {
297     if (state == state_deferred) {
298         if (firstChild () && firstChild ()->state > state_init) {
299             state = state_began;
300         } else {
301             setState (state_activated);
302             activate ();
303         }
304     } else
305         kWarning () << nodeName () << " call on not deferred element";
306 }
307 
finish()308 void Node::finish () {
309     if (active ()) {
310         setState (state_finished);
311         if (m_parent)
312             document ()->post (m_parent, new Posting (this, MsgChildFinished));
313         else
314             deactivate (); // document deactivates itself on finish
315     } else
316         kWarning () <<"Node::finish () call on not active element";
317 }
318 
deactivate()319 void Node::deactivate () {
320     //kDebug () << nodeName () << " Node::deactivate";
321     bool need_finish (unfinished ());
322     if (state_resetting != state)
323         setState (state_deactivated);
324     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
325         if (e->state > state_init && e->state < state_deactivated)
326             e->deactivate ();
327         else
328             break; // remaining not yet activated
329     }
330     if (need_finish && m_parent && m_parent->active ())
331         document ()->post (m_parent, new Posting (this, MsgChildFinished));
332 }
333 
reset()334 void Node::reset () {
335     //kDebug () << nodeName () << " Node::reset";
336     if (active ()) {
337         setState (state_resetting);
338         deactivate ();
339     }
340     setState (state_init);
341     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
342         if (e->state != state_init)
343             e->reset ();
344         // else
345         //    break; // rest not activated yet
346     }
347 }
348 
clear()349 void Node::clear () {
350     clearChildren ();
351 }
352 
clearChildren()353 void Node::clearChildren () {
354     if (m_doc)
355         document()->m_tree_version++;
356     while (m_first_child != m_last_child) {
357         // avoid stack abuse with 10k children derefing each other
358         m_last_child->m_parent = 0L;
359         m_last_child = m_last_child->m_prev;
360         m_last_child->m_next = 0L;
361     }
362     if (m_first_child)
363         m_first_child->m_parent = 0L;
364     m_first_child = m_last_child = 0L;
365 }
366 
367 template <>
appendChild(Node * c)368 void TreeNode<Node>::appendChild (Node *c) {
369     static_cast <Node *> (this)->document()->m_tree_version++;
370     ASSERT (!c->parentNode ());
371     appendChildImpl (c);
372 }
373 
374 template <>
insertBefore(Node * c,Node * b)375 void TreeNode<Node>::insertBefore (Node *c, Node *b) {
376     ASSERT (!c->parentNode ());
377     static_cast <Node *> (this)->document()->m_tree_version++;
378     insertBeforeImpl (c, b);
379 }
380 
381 template <>
removeChild(NodePtr c)382 void TreeNode<Node>::removeChild (NodePtr c) {
383     static_cast <Node *> (this)->document()->m_tree_version++;
384     removeChildImpl (c);
385 }
386 
replaceChild(NodePtr _new,NodePtr old)387 KDE_NO_EXPORT void Node::replaceChild (NodePtr _new, NodePtr old) {
388     document()->m_tree_version++;
389     if (old->m_prev) {
390         old->m_prev->m_next = _new;
391         _new->m_prev = old->m_prev;
392         old->m_prev = 0L;
393     } else {
394         _new->m_prev = 0L;
395         m_first_child = _new;
396     }
397     if (old->m_next) {
398         old->m_next->m_prev = _new;
399         _new->m_next = old->m_next;
400         old->m_next = 0L;
401     } else {
402         _new->m_next = 0L;
403         m_last_child = _new;
404     }
405     _new->m_parent = this;
406     old->m_parent = 0L;
407 }
408 
childFromTag(const QString &)409 Node *Node::childFromTag (const QString &) {
410     return NULL;
411 }
412 
characterData(const QString & s)413 KDE_NO_EXPORT void Node::characterData (const QString & s) {
414     document()->m_tree_version++;
415     if (!m_last_child || m_last_child->id != id_node_text)
416         appendChild (new TextNode (m_doc, s));
417     else
418         convertNode <TextNode> (m_last_child)->appendText (s);
419 }
420 
normalize()421 void Node::normalize () {
422     Node *e = firstChild ();
423     while (e) {
424         Node *tmp = e->nextSibling ();
425         if (!e->isElementNode () && e->id == id_node_text) {
426             QString val = e->nodeValue ().simplified ();
427             if (val.isEmpty ())
428                 removeChild (e);
429             else
430                 static_cast <TextNode *> (e)->setText (val);
431         } else
432             e->normalize ();
433         e = tmp;
434     }
435 }
436 
getInnerText(const Node * p,QTextStream & out)437 static void getInnerText (const Node *p, QTextStream & out) {
438     for (Node *e = p->firstChild (); e; e = e->nextSibling ()) {
439         if (e->id == id_node_text || e->id == id_node_cdata)
440             out << e->nodeValue ();
441         else
442             getInnerText (e, out);
443     }
444 }
445 
innerText() const446 QString Node::innerText () const {
447     QString buf;
448     QTextStream out (&buf, QIODevice::WriteOnly);
449     getInnerText (this, out);
450     return buf;
451 }
452 
getOuterXML(const Node * p,QTextStream & out,int depth)453 static void getOuterXML (const Node *p, QTextStream & out, int depth) {
454     if (!p->isElementNode ()) { // #text or #cdata
455         if (p->id == id_node_cdata)
456             out << "<![CDATA[" << p->nodeValue () << "]]>" << QChar ('\n');
457         else
458             out << XMLStringlet (p->nodeValue ()) << QChar ('\n');
459     } else {
460         const Element *e = static_cast <const Element *> (p);
461         QString indent (QString ().fill (QChar (' '), depth));
462         out << indent << QChar ('<') << XMLStringlet (e->nodeName ());
463         for (Attribute *a = e->attributes().first(); a; a = a->nextSibling())
464             out << " " << XMLStringlet (a->name ().toString ()) <<
465                 "=\"" << XMLStringlet (a->value ()) << "\"";
466         if (e->hasChildNodes ()) {
467             out << QChar ('>') << QChar ('\n');
468             for (Node *c = e->firstChild (); c; c = c->nextSibling ())
469                 getOuterXML (c, out, depth + 1);
470             out << indent << QString("</") << XMLStringlet (e->nodeName()) <<
471                 QChar ('>') << QChar ('\n');
472         } else
473             out << QString ("/>") << QChar ('\n');
474     }
475 }
476 
innerXML() const477 QString Node::innerXML () const {
478     QString buf;
479     QTextStream out (&buf, QIODevice::WriteOnly);
480     for (Node *e = firstChild (); e; e = e->nextSibling ())
481         getOuterXML (e, out, 0);
482     return buf;
483 }
484 
outerXML() const485 QString Node::outerXML () const {
486     QString buf;
487     QTextStream out (&buf, QIODevice::WriteOnly);
488     getOuterXML (this, out, 0);
489     return buf;
490 }
491 
playType()492 Node::PlayType Node::playType () {
493     return play_type_none;
494 }
495 
opened()496 void Node::opened () {
497     open = true;
498 }
499 
closed()500 void Node::closed () {
501     open = false;
502 }
503 
message(MessageType msg,void * content)504 void Node::message (MessageType msg, void *content) {
505     switch (msg) {
506 
507     case MsgChildFinished: {
508         Posting *post = (Posting *) content;
509         if (unfinished ()) {
510             if (post->source && post->source->state == state_finished)
511                 post->source->deactivate ();
512             if (post->source && post->source->nextSibling ())
513                 post->source->nextSibling ()->activate ();
514             else
515                 finish (); // we're done
516         }
517         break;
518     }
519 
520     default:
521         break;
522     }
523 }
524 
role(RoleType msg,void *)525 void *Node::role (RoleType msg, void *) {
526     switch (msg) {
527     case RoleReady:
528         return MsgBool (true);
529     default:
530         break;
531     }
532     return NULL;
533 }
534 
deliver(MessageType msg,void * content)535 KDE_NO_EXPORT void Node::deliver (MessageType msg, void *content) {
536     ConnectionList *nl = nodeMessageReceivers (this, msg);
537     if (nl)
538         for (Connection *c = nl->first(); c; c = nl->next ())
539             if (c->connecter)
540                 c->connecter->message (msg, content);
541 }
542 
accept(Visitor * v)543 void Node::accept (Visitor * v) {
544     v->visit (this);
545 }
546 
nodeValue() const547 QString Node::nodeValue () const {
548     return innerText ().trimmed ();
549 }
550 
551 //-----------------------------------------------------------------------------
552 
553 namespace {
554     struct KMPLAYER_NO_EXPORT ParamValue {
555         QString val;
556         QStringList  * modifications;
ParamValue__anone5724f990111::ParamValue557         ParamValue (const QString & v) : val (v), modifications (0L) {}
~ParamValue__anone5724f990111::ParamValue558         ~ParamValue () { delete modifications; }
559         QString value ();
setValue__anone5724f990111::ParamValue560         void setValue (const QString & v) { val = v; }
561     };
562     typedef QMap <TrieString, ParamValue *> ParamMap;
563 }
564 
565 namespace KMPlayer {
566     class KMPLAYER_NO_EXPORT ElementPrivate {
567     public:
568         ~ElementPrivate ();
569         ParamMap params;
570         void clear ();
571     };
572 }
573 
value()574 QString ParamValue::value() {
575     return modifications && modifications->size ()
576         ? modifications->back () : val;
577 }
578 
~ElementPrivate()579 KDE_NO_CDTOR_EXPORT ElementPrivate::~ElementPrivate () {
580     clear ();
581 }
582 
clear()583 KDE_NO_EXPORT void ElementPrivate::clear () {
584     const ParamMap::iterator e = params.end ();
585     for (ParamMap::iterator i = params.begin (); i != e; ++i)
586         delete i.value ();
587     params.clear ();
588 }
589 
Element(NodePtr & d,short id)590 Element::Element (NodePtr & d, short id)
591     : Node (d, id), d (new ElementPrivate) {}
592 
~Element()593 Element::~Element () {
594     delete d;
595 }
596 
setParam(const TrieString & name,const QString & val,int * mid)597 void Element::setParam (const TrieString &name, const QString &val, int *mid) {
598     ParamValue * pv = d->params [name];
599     if (!pv) {
600         pv = new ParamValue (mid ? getAttribute (name) : val);
601         d->params.insert (name, pv);
602     }
603     if (mid) {
604         if (!pv->modifications)
605             pv->modifications = new QStringList;
606         if (*mid >= 0 && *mid < int (pv->modifications->size ())) {
607             (*pv->modifications) [*mid] = val;
608         } else {
609             *mid = pv->modifications->size ();
610             pv->modifications->push_back (val);
611         }
612     } else {
613         pv->setValue (val);
614     }
615     parseParam (name, val);
616 }
617 
param(const TrieString & name)618 QString Element::param (const TrieString & name) {
619     ParamValue * pv = d->params [name];
620     if (pv)
621         return pv->value ();
622     return getAttribute (name);
623 }
624 
resetParam(const TrieString & name,int mid)625 void Element::resetParam (const TrieString &name, int mid) {
626     ParamValue * pv = d->params [name];
627     if (pv && pv->modifications) {
628         if (int (pv->modifications->size ()) > mid && mid > -1) {
629             (*pv->modifications) [mid] = QString ();
630             while (pv->modifications->size () > 0 &&
631                     pv->modifications->back ().isNull ())
632                 pv->modifications->pop_back ();
633         }
634         QString val = pv->value ();
635         if (pv->modifications->size () == 0) {
636             delete pv->modifications;
637             pv->modifications = 0L;
638             if (val.isNull ()) {
639                 delete pv;
640                 d->params.remove (name);
641             }
642         }
643         parseParam (name, val);
644     } else
645         kError () << "resetting " << name.toString() << " that doesn't exists" << endl;
646 }
647 
setAttribute(const TrieString & name,const QString & value)648 void Element::setAttribute (const TrieString & name, const QString & value) {
649     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
650         if (name == a->name ()) {
651             if (value.isNull ())
652                 m_attributes.remove (a);
653             else
654                 a->setValue (value);
655             return;
656         }
657     if (!value.isNull ())
658         m_attributes.append (new Attribute (TrieString (), name, value));
659 }
660 
getAttribute(const TrieString & name)661 QString Element::getAttribute (const TrieString & name) {
662     for (Attribute *a = m_attributes.first (); a; a = a->nextSibling ())
663         if (name == a->name ())
664             return a->value ();
665     return QString ();
666 }
667 
init()668 void Element::init () {
669     d->clear();
670     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
671         QString v = a->value ();
672         int p = v.indexOf ('{');
673         if (p > -1) {
674             int q = v.indexOf ('}', p + 1);
675             if (q > -1)
676                 continue;
677         }
678         parseParam (a->name (), v);
679     }
680 }
681 
reset()682 void Element::reset () {
683     d->clear();
684     Node::reset ();
685 }
686 
clear()687 void Element::clear () {
688     m_attributes = AttributeList (); // remove attributes
689     d->clear();
690     Node::clear ();
691 }
692 
setAttributes(const AttributeList & attrs)693 void Element::setAttributes (const AttributeList &attrs) {
694     m_attributes = attrs;
695 }
696 
accept(Visitor * v)697 void Element::accept (Visitor * v) {
698     v->visit (this);
699 }
700 
701 //-----------------------------------------------------------------------------
702 
Attribute(const TrieString & ns,const TrieString & n,const QString & v)703 Attribute::Attribute (const TrieString &ns, const TrieString &n, const QString &v)
704   : m_namespace (ns), m_name (n), m_value (v) {}
705 
setName(const TrieString & n)706 void Attribute::setName (const TrieString & n) {
707     m_name = n;
708 }
709 
setValue(const QString & v)710 void Attribute::setValue (const QString & v) {
711     m_value = v;
712 }
713 
714 //-----------------------------------------------------------------------------
715 
caption() const716 QString PlaylistRole::caption () const {
717     return title;
718 }
719 
setCaption(const QString & s)720 void PlaylistRole::setCaption (const QString &s) {
721     title = s;
722 }
723 
724 //-----------------------------------------------------------------------------
725 
hasMrlChildren(const NodePtr & e)726 static bool hasMrlChildren (const NodePtr & e) {
727     for (Node *c = e->firstChild (); c; c = c->nextSibling ())
728         if (c->isPlayable () || hasMrlChildren (c))
729             return true;
730     return false;
731 }
732 
Mrl(NodePtr & d,short id)733 Mrl::Mrl (NodePtr & d, short id)
734     : Element (d, id), cached_ismrl_version (~0),
735       media_info (NULL),
736       aspect (0), repeat (0),
737       view_mode (SingleMode),
738       resolved (false), bookmarkable (true), access_granted (false) {}
739 
~Mrl()740 Mrl::~Mrl () {
741     delete media_info;
742 }
743 
playType()744 Node::PlayType Mrl::playType () {
745     if (cached_ismrl_version != document()->m_tree_version) {
746         bool ismrl = !hasMrlChildren (this);
747         cached_play_type = ismrl ? play_type_unknown : play_type_none;
748         cached_ismrl_version = document()->m_tree_version;
749     }
750     return cached_play_type;
751 }
752 
absolutePath()753 QString Mrl::absolutePath () {
754     QString path = src;
755     if (!path.isEmpty() && !path.startsWith ("tv:/")) {
756         for (Node *e = parentNode (); e; e = e->parentNode ()) {
757             Mrl * mrl = e->mrl ();
758             if (mrl && !mrl->src.isEmpty () && mrl->src != src) {
759                 path = KURL (mrl->absolutePath (), src).url ();
760                 break;
761             }
762         }
763     }
764     return path;
765 }
766 
childFromTag(const QString & tag)767 Node *Mrl::childFromTag (const QString & tag) {
768     Node * elm = fromXMLDocumentTag (m_doc, tag);
769     if (elm)
770         return elm;
771     return NULL;
772 }
773 
mrl()774 Mrl * Mrl::mrl () {
775     return this;
776 }
777 
message(MessageType msg,void * content)778 void Mrl::message (MessageType msg, void *content) {
779     switch (msg) {
780     case MsgMediaReady:
781         resolved = true;
782         if (state == state_deferred) {
783             if (isPlayable ()) {
784                 setState (state_activated);
785                 begin ();
786             } else {
787                 Element::activate ();
788             }
789         }
790         break;
791 
792     case MsgMediaFinished:
793         if (state == state_deferred &&
794                 !isPlayable () && firstChild ()) {//if backend added child links
795             state = state_activated;
796             firstChild ()->activate ();
797         } else if (unfinished ()) {
798             finish ();
799         }
800         break;
801 
802     default:
803         break;
804     }
805     Node::message (msg, content);
806 }
807 
role(RoleType msg,void * content)808 void *Mrl::role (RoleType msg, void *content) {
809     switch (msg) {
810 
811     case RoleChildDisplay:
812         for (Node *p = parentNode (); p; p = p->parentNode ())
813             if (p->mrl ())
814                 return p->role (msg, content);
815         return NULL;
816 
817     case RolePlaylist:
818         if (title.isEmpty ())
819             title = src;
820         return !title.isEmpty () ? (PlaylistRole *) this : NULL;
821 
822     default:
823         break;
824     }
825     return Node::role (msg, content);
826 }
827 
activate()828 void Mrl::activate () {
829     if (!resolved && isPlayable ()) {
830         setState (state_deferred);
831         media_info = new MediaInfo (this, MediaManager::AudioVideo);
832         resolved = media_info->wget (absolutePath ());
833         if (resolved && isPlayable ()) {
834             // ignore the MsgMediaReady message redirection
835             setState (state_activated);
836             begin ();
837         }
838     } else if (isPlayable ()) {
839         setState (state_activated);
840         begin ();
841     } else {
842         Element::activate ();
843     }
844 }
845 
begin()846 void Mrl::begin () {
847     kDebug () << nodeName () << src << this;
848     if (!src.isEmpty ()) {
849         if (!media_info)
850             media_info = new MediaInfo (this, MediaManager::AudioVideo);
851         if (!media_info->media)
852             media_info->create ();
853         if (media_info->media->play ())
854             setState (state_began);
855         else
856             deactivate ();
857     } else {
858         deactivate (); // nothing to activate
859     }
860 }
861 
defer()862 void Mrl::defer () {
863     if (media_info && media_info->media)
864         media_info->media->pause ();
865     Node::defer ();
866 }
867 
undefer()868 void Mrl::undefer () {
869     if (media_info && media_info->media) {
870         media_info->media->unpause ();
871         setState (state_began);
872     } else {
873         Node::undefer ();
874     }
875 }
876 
deactivate()877 void Mrl::deactivate () {
878     delete media_info;
879     media_info = NULL;
880     Node::deactivate ();
881 }
882 
parseParam(const TrieString & para,const QString & val)883 void Mrl::parseParam (const TrieString & para, const QString & val) {
884     if (para == Ids::attr_src && !src.startsWith ("#")) {
885         QString abs = absolutePath ();
886         if (abs != src)
887             src = val;
888         else
889             src = KURL (abs, val).url ();
890         for (NodePtr c = firstChild (); c; c = c->nextSibling ())
891             if (c->mrl () && c->mrl ()->opener.ptr () == this) {
892                 removeChild (c);
893                 c->reset();
894             }
895         resolved = false;
896     }
897 }
898 
parseTimeString(const QString & ts)899 unsigned int Mrl::parseTimeString (const QString &ts) {
900     QString s (ts);
901     int multiply[] = { 1, 60, 60 * 60, 24 * 60 * 60, 0 };
902     int mpos = 0;
903     double d = 0;
904     while (!s.isEmpty () && multiply[mpos]) {
905         int p = s.lastIndexOf (QChar (':'));
906         QString t = p >= 0 ? s.mid (p + 1) : s;
907         d += multiply[mpos++] * t.toDouble();
908         s = p >= 0 ? s.left (p) : QString ();
909     }
910     if (d > 0.01)
911         return (unsigned int) (d * 100);
912     return 0;
913 }
914 
915 //----------------------%<-----------------------------------------------------
916 
EventData(Node * t,Posting * e,EventData * n)917 EventData::EventData (Node *t, Posting *e, EventData *n)
918  : target (t), event (e), next (n) {}
919 
~EventData()920 EventData::~EventData () {
921     delete event;
922 }
923 //-----------------------------------------------------------------------------
924 
Postpone(NodePtr doc)925 Postpone::Postpone (NodePtr doc) : m_doc (doc) {
926     if (m_doc)
927         m_doc->document ()->timeOfDay (postponed_time);
928 }
929 
~Postpone()930 Postpone::~Postpone () {
931     if (m_doc)
932         m_doc->document ()->proceed (postponed_time);
933 }
934 
935 //-----------------------------------------------------------------------------
936 
937 static NodePtr dummy_element;
938 
Document(const QString & s,PlayListNotify * n)939 Document::Document (const QString & s, PlayListNotify * n)
940  : Mrl (dummy_element, id_node_document),
941    notify_listener (n),
942    m_tree_version (0),
943    event_queue (NULL),
944    paused_queue (NULL),
945    cur_event (NULL),
946    cur_timeout (-1) {
947     m_doc = m_self; // just-in-time setting fragile m_self to m_doc
948     src = s;
949 }
950 
~Document()951 Document::~Document () {
952     kDebug () << "~Document " << src;
953 }
954 
getElementByIdImpl(Node * n,const QString & id,bool inter)955 static Node *getElementByIdImpl (Node *n, const QString & id, bool inter) {
956     NodePtr elm;
957     if (!n->isElementNode ())
958         return NULL;
959     Element *e = static_cast <Element *> (n);
960     if (e->getAttribute (Ids::attr_id) == id)
961         return n;
962     for (Node *c = e->firstChild (); c; c = c->nextSibling ()) {
963         if (!inter && c->mrl () && c->mrl ()->opener.ptr () == n)
964             continue;
965         if ((elm = getElementByIdImpl (c, id, inter)))
966             break;
967     }
968     return elm;
969 }
970 
getElementById(const QString & id)971 Node *Document::getElementById (const QString & id) {
972     return getElementByIdImpl (this, id, true);
973 }
974 
getElementById(Node * n,const QString & id,bool inter)975 Node *Document::getElementById (Node *n, const QString & id, bool inter) {
976     return getElementByIdImpl (n, id, inter);
977 }
978 
childFromTag(const QString & tag)979 Node *Document::childFromTag (const QString & tag) {
980     Node * elm = fromXMLDocumentTag (m_doc, tag);
981     if (elm)
982         return elm;
983     return NULL;
984 }
985 
dispose()986 void Document::dispose () {
987     clear ();
988     m_doc = 0L;
989 }
990 
activate()991 void Document::activate () {
992     first_event_time.tv_sec = 0;
993     last_event_time = 0;
994     Mrl::activate ();
995 }
996 
defer()997 void Document::defer () {
998     if (resolved)
999         postpone_lock = postpone ();
1000     Mrl::defer ();
1001 }
1002 
undefer()1003 void Document::undefer () {
1004     postpone_lock = 0L;
1005     Mrl::undefer ();
1006 }
1007 
reset()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
diffTime(const struct timeval & tv1,const struct timeval & tv2)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 
addTime(struct timeval & tv,int ms)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 
UpdateEvent(Document * doc,unsigned int skip)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 
timeOfDay(struct timeval & tv)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 
postponedSensible(MessageType msg)1068 static bool postponedSensible (MessageType msg) {
1069     return msg == MsgEventTimer ||
1070         msg == MsgEventStarted ||
1071         msg == MsgEventStopped;
1072 }
1073 
insertPosting(Node * n,Posting * e,const struct timeval & tv)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 
setNextTimeout(const struct timeval & now)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 
updateTimeout()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 
post(Node * n,Posting * e)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 
findPosting(EventData * queue,EventData ** prev,const Posting * e)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 
cancelPosting(Posting * e)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 
pausePosting(Posting * e)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 
unpausePosting(Posting * e,int ms)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 
timer()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 
postpone()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 
proceed(const struct timeval & postponed_time)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 
role(RoleType msg,void * content)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 
TextNode(NodePtr & d,const QString & s,short i)1312 KDE_NO_CDTOR_EXPORT TextNode::TextNode (NodePtr & d, const QString & s, short i)
1313  : Node (d, i), text (s) {}
1314 
appendText(const QString & s)1315 void TextNode::appendText (const QString & s) {
1316     text += s;
1317 }
1318 
nodeValue() const1319 QString TextNode::nodeValue () const {
1320     return text;
1321 }
1322 
1323 //-----------------------------------------------------------------------------
1324 
CData(NodePtr & d,const QString & s)1325 KDE_NO_CDTOR_EXPORT CData::CData (NodePtr & d, const QString & s)
1326  : TextNode (d, s, id_node_cdata) {}
1327 
1328 //-----------------------------------------------------------------------------
1329 
DarkNode(NodePtr & d,const QByteArray & n,short id)1330 DarkNode::DarkNode (NodePtr & d, const QByteArray &n, short id)
1331  : Element (d, id), name (n) {
1332 }
1333 
childFromTag(const QString & tag)1334 Node *DarkNode::childFromTag (const QString & tag) {
1335     return new DarkNode (m_doc, tag.toUtf8 ());
1336 }
1337 
1338 //-----------------------------------------------------------------------------
1339 
GenericURL(NodePtr & d,const QString & s,const QString & name)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 
closed()1348 KDE_NO_EXPORT void GenericURL::closed () {
1349     if (src.isEmpty ())
1350         src = getAttribute (Ids::attr_src);
1351     Mrl::closed ();
1352 }
1353 
1354 //-----------------------------------------------------------------------------
1355 
GenericMrl(NodePtr & d,const QString & s,const QString & name,const QByteArray & tag)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 
closed()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 
role(RoleType msg,void * content)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 
visit(Element * elm)1388 void Visitor::visit (Element *elm) {
1389     visit (static_cast <Node *> (elm));
1390 }
1391 
visit(TextNode * text)1392 void Visitor::visit (TextNode *text) {
1393     visit (static_cast <Node *> (text));
1394 }
1395 
1396 //-----------------------------------------------------------------------------
1397 
CacheAllocator(size_t s)1398 CacheAllocator::CacheAllocator (size_t s)
1399     : pool ((void**) malloc (10 * sizeof (void *))), size (s), count (0) {}
1400 
alloc()1401 void *CacheAllocator::alloc () {
1402     return count ? pool[--count] : malloc (size);
1403 }
1404 
dealloc(void * p)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);
~DocumentBuilder()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 
DocumentBuilder(NodePtr d,bool set_opener)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 
startTag(const QString & tag,const AttributeList & attr)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 
endTag(const QString & tag)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 
characterData(const QString & data)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 
cdataData(const QString & data)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 
cdataStart()1543 void DocumentBuilder::cdataStart () {
1544     cdata.truncate (0);
1545     in_cdata = true;
1546 }
1547 
cdataEnd()1548 void DocumentBuilder::cdataEnd () {
1549     cdataData (cdata);
1550     cdata.truncate (0);
1551     in_cdata = false;
1552 }
1553 
startTag(void * data,const char * tag,const char ** attr)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 
endTag(void * data,const char * tag)1567 static void endTag (void *data, const char * tag) {
1568     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1569     builder->endTag (QString::fromUtf8 (tag));
1570 }
1571 
characterData(void * data,const char * s,int len)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 
cdataStart(void * data)1581 static void cdataStart (void *data) {
1582     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1583     builder->cdataStart ();
1584 }
1585 
cdataEnd(void * data)1586 static void cdataEnd (void *data) {
1587     DocumentBuilder * builder = static_cast <DocumentBuilder *> (data);
1588     builder->cdataEnd ();
1589 }
1590 
1591 KMPLAYER_EXPORT
readXML(NodePtr root,QTextStream & in,const QString & firstline,bool set_opener)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.utf8 ();
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.read ().utf8 ();
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 {
TokenInfo__anone5724f990211::SimpleSAXParser::TokenInfo1632         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;
SimpleSAXParser(DocumentBuilder & b)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) {}
~SimpleSAXParser()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 {
StateInfo__anone5724f990211::SimpleSAXParser::StateInfo1652         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 
operator new(size_t)1687 inline void *SimpleSAXParser::TokenInfo::operator new (size_t) {
1688     return token_pool.alloc ();
1689 }
1690 
operator delete(void * p)1691 inline void SimpleSAXParser::TokenInfo::operator delete (void *p) {
1692     token_pool.dealloc (p);
1693 }
1694 
1695 KMPLAYER_EXPORT
readXML(NodePtr root,QTextStream & in,const QString & firstline,bool set_opener)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 
push()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 
push_attribute()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 
nextToken()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 
readAttributes()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 
readPI()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 
readDTD()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 
readCDATA()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 
readComment()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 
readEndTag()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;
readTag()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 
parse(QTextStream & d)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
2147