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 << "<";
79 } else if (txt.str [i] == QChar ('>')) {
80 out << ">";
81 } else if (txt.str [i] == QChar ('"')) {
82 out << """;
83 } else if (txt.str [i] == QChar ('&')) {
84 out << "&";
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 ..> Ӓ
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