]> git.sesse.net Git - vlc/blob - modules/demux/playlist/xspf.c
* modules/demux/playlist/xspf.c: fix warnings that are bugs on amd64.
[vlc] / modules / demux / playlist / xspf.c
1 /*******************************************************************************
2  * xspf.c : XSPF playlist import functions
3  *******************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Daniel Stränger <vlc at schmaller dot de>
8  *          Yoann Peronneau <yoann@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *******************************************************************************/
24 /**
25  * \file modules/demux/playlist/xspf.c
26  * \brief XSPF playlist import functions
27  */
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31 #include <vlc/intf.h>
32
33 #include "playlist.h"
34 #include "vlc_xml.h"
35 #include "vlc_strings.h"
36 #include "vlc_url.h"
37 #include "xspf.h"
38
39 struct demux_sys_t
40 {
41     playlist_item_t *p_item_in_category;
42     int i_parent_id;
43     input_item_t **pp_tracklist;
44     int i_tracklist_entries;
45     int i_identifier;
46 };
47
48 static int Control( demux_t *, int, va_list );
49 static int Demux( demux_t * );
50
51 /**
52  * \brief XSPF submodule initialization function
53  */
54 int E_(xspf_import_Activate)( vlc_object_t *p_this )
55 {
56     DEMUX_BY_EXTENSION_OR_FORCED_MSG( ".xspf", "xspf-open", 
57                                       "using XSPF playlist reader" );
58     return VLC_SUCCESS;
59 }
60
61 /**
62  * \brief demuxer function for XSPF parsing
63  */
64 int Demux( demux_t *p_demux )
65 {
66     int i_ret = VLC_SUCCESS;
67     xml_t *p_xml = NULL;
68     xml_reader_t *p_xml_reader = NULL;
69     char *psz_name = NULL;
70     INIT_PLAYLIST_STUFF;
71     p_demux->p_sys->p_item_in_category = p_item_in_category;
72     p_demux->p_sys->i_parent_id = i_parent_id;
73     p_demux->p_sys->pp_tracklist = NULL;
74     p_demux->p_sys->i_tracklist_entries = 0;
75     p_demux->p_sys->i_identifier = -1;
76
77     /* create new xml parser from stream */
78     p_xml = xml_Create( p_demux );
79     if( !p_xml )
80         i_ret = VLC_ENOMOD;
81     else
82     {
83         p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
84         if( !p_xml_reader )
85             i_ret = VLC_EGENERIC;
86     }
87
88     /* locating the root node */
89     if( i_ret == VLC_SUCCESS )
90     {
91         do
92         {
93             if( xml_ReaderRead( p_xml_reader ) != 1 )
94             {
95                 msg_Err( p_demux, "can't read xml stream" );
96                 i_ret = VLC_EGENERIC;
97             }
98         } while( i_ret == VLC_SUCCESS &&
99                  xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM );
100     }
101     /* checking root node name */
102     if( i_ret == VLC_SUCCESS )
103     {
104         psz_name = xml_ReaderName( p_xml_reader );
105         if( !psz_name || strcmp( psz_name, "playlist" ) )
106         {
107             msg_Err( p_demux, "invalid root node name: %s", psz_name );
108             i_ret = VLC_EGENERIC;
109         }
110         FREE_NAME();
111     }
112
113     i_ret = parse_playlist_node( p_demux, p_playlist, p_current, NULL,
114                                  p_xml_reader, "playlist" );
115     HANDLE_PLAY_AND_RELEASE;
116     if( p_xml_reader )
117         xml_ReaderDelete( p_xml, p_xml_reader );
118     if( p_xml )
119         xml_Delete( p_xml );
120
121     return i_ret;
122     return 0;
123 }
124
125 /** \brief dummy function for demux callback interface */
126 static int Control( demux_t *p_demux, int i_query, va_list args )
127 {
128     return VLC_EGENERIC;
129 }
130
131 /**
132  * \brief parse the root node of a XSPF playlist
133  * \param p_demux demuxer instance
134  * \param p_playlist playlist instance
135  * \param p_item current playlist item
136  * \param p_input current input item
137  * \param p_xml_reader xml reader instance
138  * \param psz_element name of element to parse
139  */
140 static vlc_bool_t parse_playlist_node COMPLEX_INTERFACE
141 {
142     char *psz_name=NULL;
143     char *psz_value=NULL;
144     vlc_bool_t b_version_found = VLC_FALSE;
145     int i_node;
146     xml_elem_hnd_t *p_handler=NULL;
147
148     xml_elem_hnd_t pl_elements[] =
149         { {"title",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
150           {"creator",      SIMPLE_CONTENT,  {.smpl = set_item_info} },
151           {"annotation",   SIMPLE_CONTENT,  {NULL} },
152           {"info",         SIMPLE_CONTENT,  {NULL} },
153           {"location",     SIMPLE_CONTENT,  {NULL} },
154           {"identifier",   SIMPLE_CONTENT,  {NULL} },
155           {"image",        SIMPLE_CONTENT,  {NULL} },
156           {"date",         SIMPLE_CONTENT,  {NULL} },
157           {"license",      SIMPLE_CONTENT,  {NULL} },
158           {"attribution",  COMPLEX_CONTENT, {.cmplx = skip_element} },
159           {"link",         SIMPLE_CONTENT,  {NULL} },
160           {"meta",         SIMPLE_CONTENT,  {NULL} },
161           {"extension",    COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
162           {"trackList",    COMPLEX_CONTENT, {.cmplx = parse_tracklist_node} },
163           {NULL,           UNKNOWN_CONTENT, {NULL} }
164         };
165
166     /* read all playlist attributes */
167     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
168     {
169         psz_name = xml_ReaderName( p_xml_reader );
170         psz_value = xml_ReaderValue( p_xml_reader );
171         if( !psz_name || !psz_value )
172         {
173             msg_Err( p_demux, "invalid xml stream @ <playlist>" );
174             FREE_ATT();
175             return VLC_FALSE;
176         }
177         /* attribute: version */
178         if( !strcmp( psz_name, "version" ) )
179        {
180             b_version_found = VLC_TRUE;
181             if( strcmp( psz_value, "0" ) && strcmp( psz_value, "1" ) )
182                 msg_Warn( p_demux, "unsupported XSPF version" );
183         }
184         /* attribute: xmlns */
185         else if( !strcmp( psz_name, "xmlns" ) )
186             ;
187         /* unknown attribute */
188         else
189             msg_Warn( p_demux, "invalid <playlist> attribute:\"%s\"", psz_name);
190
191         FREE_ATT();
192     }
193     /* attribute version is mandatory !!! */
194     if( !b_version_found )
195         msg_Warn( p_demux, "<playlist> requires \"version\" attribute" );
196
197     /* parse the child elements - we only take care of <trackList> */
198     while( xml_ReaderRead( p_xml_reader ) == 1 )
199     {
200         i_node = xml_ReaderNodeType( p_xml_reader );
201         switch( i_node )
202         {
203             case XML_READER_NONE:
204                 break;
205             case XML_READER_STARTELEM:
206                 /*  element start tag  */
207                 psz_name = xml_ReaderName( p_xml_reader );
208                 if( !psz_name || !*psz_name )
209                 {
210                     msg_Err( p_demux, "invalid xml stream" );
211                     FREE_ATT();
212                     return VLC_FALSE;
213                 }
214                 /* choose handler */
215                 for( p_handler = pl_elements;
216                      p_handler->name && strcmp( psz_name, p_handler->name );
217                      p_handler++ );
218                 if( !p_handler->name )
219                 {
220                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
221                     FREE_ATT();
222                     return VLC_FALSE;
223                 }
224                 FREE_NAME();
225                 /* complex content is parsed in a separate function */
226                 if( p_handler->type == COMPLEX_CONTENT )
227                 {
228                     if( p_handler->pf_handler.cmplx( p_demux,
229                                                      p_playlist,
230                                                      p_item,NULL,
231                                                      p_xml_reader,
232                                                      p_handler->name ) )
233                     {
234                         p_handler = NULL;
235                         FREE_ATT();
236                     }
237                     else
238                     {
239                         FREE_ATT();
240                         return VLC_FALSE;
241                     }
242                 }
243                 break;
244
245             case XML_READER_TEXT:
246                 /* simple element content */
247                 FREE_ATT();
248                 psz_value = xml_ReaderValue( p_xml_reader );
249                 if( !psz_value )
250                 {
251                     msg_Err( p_demux, "invalid xml stream" );
252                     FREE_ATT();
253                     return VLC_FALSE;
254                 }
255                 break;
256
257             case XML_READER_ENDELEM:
258                 /* element end tag */
259                 psz_name = xml_ReaderName( p_xml_reader );
260                 if( !psz_name )
261                 {
262                     msg_Err( p_demux, "invalid xml stream" );
263                     FREE_ATT();
264                     return VLC_FALSE;
265                 }
266                 /* leave if the current parent node <playlist> is terminated */
267                 if( !strcmp( psz_name, psz_element ) )
268                 {
269                     FREE_ATT();
270                     return VLC_TRUE;
271                 }
272                 /* there MUST have been a start tag for that element name */
273                 if( !p_handler || !p_handler->name
274                     || strcmp( p_handler->name, psz_name ))
275                 {
276                     msg_Err( p_demux, "there's no open element left for <%s>",
277                              psz_name );
278                     FREE_ATT();
279                     return VLC_FALSE;
280                 }
281
282                 if( p_handler->pf_handler.smpl )
283                 {
284                     p_handler->pf_handler.smpl( p_item, NULL, p_handler->name,
285                                                 psz_value );
286                 }
287                 FREE_ATT();
288                 p_handler = NULL;
289                 break;
290
291             default:
292                 /* unknown/unexpected xml node */
293                 msg_Err( p_demux, "unexpected xml node %i", i_node );
294                 FREE_ATT();
295                 return VLC_FALSE;
296         }
297         FREE_NAME();
298     }
299     return VLC_FALSE;
300 }
301
302 /**
303  * \brief parses the tracklist node which only may contain <track>s
304  */
305 static vlc_bool_t parse_tracklist_node COMPLEX_INTERFACE
306 {
307     char *psz_name=NULL;
308     int i_node;
309     int i_ntracks = 0;
310
311     /* now parse the <track>s */
312     while( xml_ReaderRead( p_xml_reader ) == 1 )
313     {
314         i_node = xml_ReaderNodeType( p_xml_reader );
315         if( i_node == XML_READER_STARTELEM )
316         {
317             psz_name = xml_ReaderName( p_xml_reader );
318             if( !psz_name )
319             {
320                 msg_Err( p_demux, "unexpected end of xml data" );
321                 FREE_NAME();
322                 return VLC_FALSE;
323             }
324             if( strcmp( psz_name, "track") )
325             {
326                 msg_Err( p_demux, "unexpected child of <trackList>: <%s>",
327                          psz_name );
328                 FREE_NAME();
329                 return VLC_FALSE;
330             }
331             FREE_NAME();
332
333             /* parse the track data in a separate function */
334             if( parse_track_node( p_demux, p_playlist, p_item, NULL,
335                                    p_xml_reader,"track" ) == VLC_TRUE )
336                 i_ntracks++;
337         }
338         else if( i_node == XML_READER_ENDELEM )
339             break;
340     }
341
342     /* the <trackList> has to be terminated */
343     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_ENDELEM )
344     {
345         msg_Err( p_demux, "there's a missing </trackList>" );
346         FREE_NAME();
347         return VLC_FALSE;
348     }
349     psz_name = xml_ReaderName( p_xml_reader );
350     if( !psz_name || strcmp( psz_name, "trackList" ) )
351     {
352         msg_Err( p_demux, "expected: </trackList>, found: </%s>", psz_name );
353         FREE_NAME();
354         return VLC_FALSE;
355     }
356     FREE_NAME();
357
358     msg_Dbg( p_demux, "parsed %i tracks successfully", i_ntracks );
359
360     return VLC_TRUE;
361 }
362
363 /**
364  * \brief parse one track element
365  * \param COMPLEX_INTERFACE
366  */
367 static vlc_bool_t parse_track_node COMPLEX_INTERFACE
368 {
369     input_item_t *p_new_input = NULL;
370     int i_node;
371     char *psz_name=NULL;
372     char *psz_value=NULL;
373     xml_elem_hnd_t *p_handler=NULL;
374
375     xml_elem_hnd_t track_elements[] =
376         { {"location",     SIMPLE_CONTENT,  {NULL} },
377           {"identifier",   SIMPLE_CONTENT,  {NULL} },
378           {"title",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
379           {"creator",      SIMPLE_CONTENT,  {.smpl = set_item_info} },
380           {"annotation",   SIMPLE_CONTENT,  {NULL} },
381           {"info",         SIMPLE_CONTENT,  {NULL} },
382           {"image",        SIMPLE_CONTENT,  {NULL} },
383           {"album",        SIMPLE_CONTENT,  {.smpl = set_item_info} },
384           {"trackNum",     SIMPLE_CONTENT,  {.smpl = set_item_info} },
385           {"duration",     SIMPLE_CONTENT,  {.smpl = set_item_info} },
386           {"link",         SIMPLE_CONTENT,  {NULL} },
387           {"meta",         SIMPLE_CONTENT,  {NULL} },
388           {"extension",    COMPLEX_CONTENT, {.cmplx = skip_element} },
389           {NULL,           UNKNOWN_CONTENT, {NULL} }
390         };
391
392     while( xml_ReaderRead( p_xml_reader ) == 1 )
393     {
394         i_node = xml_ReaderNodeType( p_xml_reader );
395         switch( i_node )
396         {
397             case XML_READER_NONE:
398                 break;
399
400             case XML_READER_STARTELEM:
401                 /*  element start tag  */
402                 psz_name = xml_ReaderName( p_xml_reader );
403                 if( !psz_name || !*psz_name )
404                 {
405                     msg_Err( p_demux, "invalid xml stream" );
406                     FREE_ATT();
407                     return VLC_FALSE;
408                 }
409                 /* choose handler */
410                 for( p_handler = track_elements;
411                      p_handler->name && strcmp( psz_name, p_handler->name );
412                      p_handler++ );
413                 if( !p_handler->name )
414                 {
415                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
416                     FREE_ATT();
417                     return VLC_FALSE;
418                 }
419                 FREE_NAME();
420                 /* complex content is parsed in a separate function */
421                 if( p_handler->type == COMPLEX_CONTENT )
422                 {
423                     if( !p_new_input )
424                     {
425                         msg_Err( p_demux,
426                                  "at <%s> level no new item has been allocated",
427                                  p_handler->name );
428                         FREE_ATT();
429                         return VLC_FALSE;
430                     }
431                     if( p_handler->pf_handler.cmplx( p_demux,
432                                                      p_playlist,
433                                                      NULL, p_new_input,
434                                                      p_xml_reader,
435                                                      p_handler->name ) )
436                     {
437                         p_handler = NULL;
438                         FREE_ATT();
439                     }
440                     else
441                     {
442                         FREE_ATT();
443                         return VLC_FALSE;
444                     }
445                 }
446                 break;
447
448             case XML_READER_TEXT:
449                 /* simple element content */
450                 FREE_ATT();
451                 psz_value = xml_ReaderValue( p_xml_reader );
452                 if( !psz_value )
453                 {
454                     msg_Err( p_demux, "invalid xml stream" );
455                     FREE_ATT();
456                     return VLC_FALSE;
457                 }
458                 break;
459
460             case XML_READER_ENDELEM:
461                 /* element end tag */
462                 psz_name = xml_ReaderName( p_xml_reader );
463                 if( !psz_name )
464                 {
465                     msg_Err( p_demux, "invalid xml stream" );
466                     FREE_ATT();
467                     return VLC_FALSE;
468                 }
469                 /* leave if the current parent node <track> is terminated */
470                 if( !strcmp( psz_name, psz_element ) )
471                 {
472                     FREE_ATT();
473                     /* Add it */
474                     playlist_AddWhereverNeeded( p_playlist, p_new_input,
475                               p_item, p_demux->p_sys->p_item_in_category,
476                               (p_demux->p_sys->i_parent_id >0 ) ? VLC_TRUE:
477                               VLC_FALSE, PLAYLIST_APPEND );
478                     if( p_demux->p_sys->i_identifier <
479                         p_demux->p_sys->i_tracklist_entries )
480                     {
481                         p_demux->p_sys->pp_tracklist[
482                             p_demux->p_sys->i_identifier ] = p_new_input;
483                     }
484                     else
485                     {
486                         if( p_demux->p_sys->i_identifier >
487                             p_demux->p_sys->i_tracklist_entries )
488                         {
489                             p_demux->p_sys->i_tracklist_entries =
490                                 p_demux->p_sys->i_identifier;
491                         }
492                         INSERT_ELEM( p_demux->p_sys->pp_tracklist,
493                                      p_demux->p_sys->i_tracklist_entries,
494                                      p_demux->p_sys->i_tracklist_entries,
495                                      p_new_input );
496                     }
497                     return VLC_TRUE;
498                 }
499                 /* there MUST have been a start tag for that element name */
500                 if( !p_handler || !p_handler->name
501                     || strcmp( p_handler->name, psz_name ))
502                 {
503                     msg_Err( p_demux, "there's no open element left for <%s>",
504                              psz_name );
505                     FREE_ATT();
506                     return VLC_FALSE;
507                 }
508
509                 /* special case: location */
510                 if( !strcmp( p_handler->name, "location" ) )
511                 {
512                     char *psz_uri=NULL;
513                     /* there MUST NOT be an item */
514                     if( p_new_input )
515                     {
516                         msg_Err( p_demux, "item <%s> already created",
517                                  psz_name );
518                         FREE_ATT();
519                         return VLC_FALSE;
520                     }
521                     psz_uri = unescape_URI_duplicate( psz_value );
522
523                     if( psz_uri )
524                     {
525                         p_new_input = input_ItemNewExt( p_playlist, psz_uri,
526                                                         NULL, 0, NULL, -1 );
527                         p_new_input->p_meta = vlc_meta_New();
528                         free( psz_uri );
529                         vlc_input_item_CopyOptions( p_item->p_input, p_new_input );
530                         psz_uri = NULL;
531                         FREE_ATT();
532                         p_handler = NULL;
533                     }
534                     else
535                     {
536                         FREE_ATT();
537                         return VLC_FALSE;
538                     }
539                 }
540                 else if( !strcmp( p_handler->name, "identifier" ) )
541                 {
542                     p_demux->p_sys->i_identifier = atoi( psz_value );
543                 }
544                 else
545                 {
546                     /* there MUST be an item */
547                     if( !p_new_input )
548                     {
549                         msg_Err( p_demux, "item not yet created at <%s>",
550                                  psz_name );
551                         FREE_ATT();
552                         return VLC_FALSE;
553                     }
554                     if( p_handler->pf_handler.smpl )
555                     {
556                         p_handler->pf_handler.smpl( NULL, p_input,
557                                                     p_handler->name,
558                                                     psz_value );
559                         FREE_ATT();
560                     }
561                 }
562                 FREE_ATT();
563                 p_handler = NULL;
564                 break;
565
566             default:
567                 /* unknown/unexpected xml node */
568                 msg_Err( p_demux, "unexpected xml node %i", i_node );
569                 FREE_ATT();
570                 return VLC_FALSE;
571         }
572         FREE_NAME();
573     }
574     msg_Err( p_demux, "unexpected end of xml data" );
575     FREE_ATT();
576     return VLC_FALSE;
577 }
578
579 /**
580  * \brief handles the supported <track> sub-elements
581  */
582 static vlc_bool_t set_item_info SIMPLE_INTERFACE
583 {
584     /* exit if setting is impossible */
585     if( !psz_name || !psz_value || !p_input )
586         return VLC_FALSE;
587
588     /* re-convert xml special characters inside psz_value */
589     resolve_xml_special_chars( psz_value );
590
591     /* handle each info element in a separate "if" clause */
592     if( !strcmp( psz_name, "title" ) )
593     {
594         p_input->psz_name = strdup( (char*)psz_value );
595     }
596     else if( !strcmp( psz_name, "creator" ) )
597     {
598         vlc_meta_SetArtist( p_input->p_meta, psz_value );
599     }
600     else if( !strcmp( psz_name, "album" ) )
601     {
602         vlc_meta_SetAlbum( p_input->p_meta, psz_value );
603
604     }
605     else if( !strcmp( psz_name, "trackNum" ) )
606     {
607         vlc_meta_SetTracknum( p_input->p_meta, psz_value );
608     }
609     else if( !strcmp( psz_name, "duration" ) )
610     {
611         long i_num = atol( psz_value );
612         p_input->i_duration = i_num*1000;
613     }
614     return VLC_TRUE;
615 }
616
617
618 /**
619  * \brief parse the extension node of a XSPF playlist
620  */
621 static vlc_bool_t parse_extension_node COMPLEX_INTERFACE
622 {
623     char *psz_name = NULL;
624     char *psz_value = NULL;
625     char *psz_title = NULL;
626     int i_node;
627     xml_elem_hnd_t *p_handler = NULL;
628
629     xml_elem_hnd_t pl_elements[] =
630         { {"node",  COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
631           {"item",  COMPLEX_CONTENT, {.cmplx = parse_extitem_node} },
632           {NULL,    UNKNOWN_CONTENT, {NULL} }
633         };
634
635     /* read all extension node attributes */
636     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
637     {
638         psz_name = xml_ReaderName( p_xml_reader );
639         psz_value = xml_ReaderValue( p_xml_reader );
640         if( !psz_name || !psz_value )
641         {
642             msg_Err( p_demux, "invalid xml stream @ <node>" );
643             FREE_ATT();
644             return VLC_FALSE;
645         }
646         /* attribute: title */
647         if( !strcmp( psz_name, "title" ) )
648         {
649             psz_title = unescape_URI_duplicate( psz_value );
650         }
651         /* unknown attribute */
652         else
653             msg_Warn( p_demux, "invalid <node> attribute:\"%s\"", psz_name);
654
655         FREE_ATT();
656     }
657
658     /* attribute title is mandatory except for <extension> */
659     if( !strcmp( psz_element, "node" ) && !psz_title )
660     {
661         msg_Warn( p_demux, "<node> requires \"title\" attribute" );
662         return VLC_FALSE;
663     }
664
665     if( !strcmp( psz_element, "node" ) )
666     {
667         fprintf( stderr, "  node: %s\n", psz_title );
668     }
669     if( psz_title ) free( psz_title );
670
671     /* parse the child elements */
672     while( xml_ReaderRead( p_xml_reader ) == 1 )
673     {
674         i_node = xml_ReaderNodeType( p_xml_reader );
675         switch( i_node )
676         {
677             case XML_READER_NONE:
678                 break;
679             case XML_READER_STARTELEM:
680                 /*  element start tag  */
681                 psz_name = xml_ReaderName( p_xml_reader );
682                 if( !psz_name || !*psz_name )
683                 {
684                     msg_Err( p_demux, "invalid xml stream" );
685                     FREE_ATT();
686                     return VLC_FALSE;
687                 }
688                 /* choose handler */
689                 for( p_handler = pl_elements;
690                      p_handler->name && strcmp( psz_name, p_handler->name );
691                      p_handler++ );
692                 if( !p_handler->name )
693                 {
694                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
695                     FREE_ATT();
696                     return VLC_FALSE;
697                 }
698                 FREE_NAME();
699                 /* complex content is parsed in a separate function */
700                 if( p_handler->type == COMPLEX_CONTENT )
701                 {
702                     if( p_handler->pf_handler.cmplx( p_demux,
703                                                      p_playlist,
704                                                      p_item, NULL,
705                                                      p_xml_reader,
706                                                      p_handler->name ) )
707                     {
708                         p_handler = NULL;
709                         FREE_ATT();
710                     }
711                     else
712                     {
713                         FREE_ATT();
714                         return VLC_FALSE;
715                     }
716                 }
717                 break;
718
719             case XML_READER_TEXT:
720                 /* simple element content */
721                 FREE_ATT();
722                 psz_value = xml_ReaderValue( p_xml_reader );
723                 if( !psz_value )
724                 {
725                     msg_Err( p_demux, "invalid xml stream" );
726                     FREE_ATT();
727                     return VLC_FALSE;
728                 }
729                 break;
730
731             case XML_READER_ENDELEM:
732                 /* element end tag */
733                 psz_name = xml_ReaderName( p_xml_reader );
734                 if( !psz_name )
735                 {
736                     msg_Err( p_demux, "invalid xml stream" );
737                     FREE_ATT();
738                     return VLC_FALSE;
739                 }
740                 /* leave if the current parent node is terminated */
741                 if( !strcmp( psz_name, psz_element ) )
742                 {
743                     fprintf( stderr, "  </node>\n" );
744                     FREE_ATT();
745                     return VLC_TRUE;
746                 }
747                 /* there MUST have been a start tag for that element name */
748                 if( !p_handler || !p_handler->name
749                     || strcmp( p_handler->name, psz_name ))
750                 {
751                     msg_Err( p_demux, "there's no open element left for <%s>",
752                              psz_name );
753                     FREE_ATT();
754                     return VLC_FALSE;
755                 }
756
757                 if( p_handler->pf_handler.smpl )
758                 {
759                     p_handler->pf_handler.smpl( p_item, NULL, p_handler->name,
760                                                 psz_value );
761                 }
762                 FREE_ATT();
763                 p_handler = NULL;
764                 break;
765
766             default:
767                 /* unknown/unexpected xml node */
768                 msg_Err( p_demux, "unexpected xml node %i", i_node );
769                 FREE_ATT();
770                 return VLC_FALSE;
771         }
772         FREE_NAME();
773     }
774     return VLC_FALSE;
775 }
776
777 /**
778  * \brief parse the extension item node of a XSPF playlist
779  */
780 static vlc_bool_t parse_extitem_node COMPLEX_INTERFACE
781 {
782     char *psz_name = NULL;
783     char *psz_value = NULL;
784     int i_href = -1;
785
786     /* read all extension item attributes */
787     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
788     {
789         psz_name = xml_ReaderName( p_xml_reader );
790         psz_value = xml_ReaderValue( p_xml_reader );
791         if( !psz_name || !psz_value )
792         {
793             msg_Err( p_demux, "invalid xml stream @ <item>" );
794             FREE_ATT();
795             return VLC_FALSE;
796         }
797         /* attribute: href */
798         if( !strcmp( psz_name, "href" ) )
799         {
800             i_href = atoi( psz_value );
801         }
802         /* unknown attribute */
803         else
804             msg_Warn( p_demux, "invalid <item> attribute:\"%s\"", psz_name);
805
806         FREE_ATT();
807     }
808
809     /* attribute href is mandatory */
810     if( i_href < 0 )
811     {
812         msg_Warn( p_demux, "<item> requires \"href\" attribute" );
813         return VLC_FALSE;
814     }
815
816     if( i_href > p_demux->p_sys->i_tracklist_entries )
817     {
818         msg_Warn( p_demux, "invalid \"href\" attribute" );
819         return VLC_FALSE;
820     }
821
822     fprintf( stderr, "    %s\n", p_demux->p_sys->pp_tracklist[i_href]->psz_name );
823
824     return VLC_TRUE;
825 }
826
827 /**
828  * \brief skips complex element content that we can't manage
829  */
830 static vlc_bool_t skip_element COMPLEX_INTERFACE
831 {
832     char *psz_endname;
833
834     while( xml_ReaderRead( p_xml_reader ) == 1 )
835     {
836         if( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM )
837         {
838             psz_endname = xml_ReaderName( p_xml_reader );
839             if( !psz_endname )
840                 return VLC_FALSE;
841             if( !strcmp( psz_element, psz_endname ) )
842             {
843                 free( psz_endname );
844                 return VLC_TRUE;
845             }
846             else
847                 free( psz_endname );
848         }
849     }
850     return VLC_FALSE;
851 }