2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "playtreeparser.h"
29 #include "stream/stream.h"
30 #include "libmpdemux/demuxer.h"
31 #include "asxparser.h"
35 extern m_config_t
* mconfig
;
40 asx_list_add(void* list_ptr
,void* entry
){
41 void** list
= *(void***)list_ptr
;
45 for( ; list
[c
] != NULL
; c
++) ;
47 list
= realloc(list
, sizeof(void*) * (c
+ 2));
52 *(void***)list_ptr
= list
;
57 asx_list_remove(void* list_ptr
,void* entry
,ASX_FreeFunc free_func
) {
58 void** list
= *(void***)list_ptr
;
61 if(list
== NULL
) return;
63 for(c
= 0 ; list
[c
] != NULL
; c
++){
64 if(list
[c
] == entry
) e
= c
;
67 if(e
== -1) return; // Not found
69 if(free_func
!= NULL
) free_func(list
[e
]);
71 if(c
== 1) { // Only one entry, we drop all
73 *(void**)list_ptr
= NULL
;
77 if(c
> e
) // If c==e the memmove is not needed
78 memmove(list
+e
,list
+e
+1,(c
-e
)*sizeof(void*));
80 list
= realloc(list
, (c
- 1) * sizeof(void*));
83 *(void***)list_ptr
= list
;
87 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
88 void** ptr
= *(void***)list_ptr
;
89 if(ptr
== NULL
) return;
90 if(free_func
!= NULL
) {
91 for( ; *ptr
!= NULL
; ptr
++)
94 free(*(void**)list_ptr
);
95 *(void**)list_ptr
= NULL
;
101 asx_get_attrib(const char* attrib
,char** attribs
) {
104 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
105 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
106 if(strcasecmp(ptr
[0],attrib
) == 0)
107 return strdup(ptr
[1]);
113 asx_attrib_to_enum(const char* val
,char** valid_vals
) {
117 if(valid_vals
== NULL
|| val
== NULL
) return -2;
118 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
119 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
126 #define asx_warning_attrib_required(p,e,a) mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : element %s don't have the required attribute %s",p->line,e,a)
127 #define asx_warning_body_parse_error(p,e) mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : error while parsing %s body",p->line,e)
130 asx_parser_new(void) {
131 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
136 asx_parser_free(ASX_Parser_t
* parser
) {
138 if(parser
->ret_stack
) free(parser
->ret_stack
);
143 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
144 #define SPACE " \n\t\r"
147 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
148 char *ptr1
, *ptr2
, *ptr3
;
150 char **attribs
= NULL
;
155 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
156 if(*ptr1
== '\0') break;
158 ptr3
= strchr(ptr1
,'=');
159 if(ptr3
== NULL
) break;
160 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
162 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
166 attrib
= malloc(ptr2
-ptr1
+2);
167 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
168 attrib
[ptr2
-ptr1
+1] = '\0';
170 ptr1
= strchr(ptr3
,'"');
171 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
172 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
173 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
177 ptr2
= strchr(ptr1
+1,ptr1
[0]);
179 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
184 val
= malloc(ptr2
-ptr1
+1);
185 strncpy(val
,ptr1
,ptr2
-ptr1
);
186 val
[ptr2
-ptr1
] = '\0';
189 attribs
= realloc(attribs
, (2 * n_attrib
+ 1) * sizeof(char*));
190 attribs
[n_attrib
*2-2] = attrib
;
191 attribs
[n_attrib
*2-1] = val
;
197 attribs
[n_attrib
*2] = NULL
;
205 * Return -1 on error, 0 when nothing is found, 1 on sucess
208 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
209 char** _element
,char** _body
,char*** _attribs
) {
210 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
211 char *attribs
= NULL
;
212 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
214 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
217 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
218 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
222 *_body
= *_element
= NULL
;
226 if(buffer
== NULL
) return 0;
228 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
229 ASX_LineSave_t
* ls
= parser
->ret_stack
;
231 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
232 if(buffer
== ls
[i
].buffer
) {
233 parser
->line
= ls
[i
].line
;
238 if( i
< parser
->ret_stack_size
) {
240 if( i
< parser
->ret_stack_size
)
241 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
242 parser
->ret_stack_size
-= i
;
243 if(parser
->ret_stack_size
> 0)
244 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
246 free(parser
->ret_stack
);
247 parser
->ret_stack
= NULL
;
254 for( ; ptr1
[0] != '<' ; ptr1
++) {
255 if(ptr1
[0] == '\0') {
259 if(ptr1
[0] == '\n') parser
->line
++;
261 //ptr1 = strchr(ptr1,'<');
262 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
264 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
265 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
266 if(ptr1
[0] == '\0') {
270 if(ptr1
[0] == '\n') parser
->line
++;
272 //ptr1 = strstr(ptr1,"-->");
274 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
282 // Is this space skip very useful ??
283 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
284 if(ptr1
[0] == '\0') {
285 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
288 if(ptr1
[0] == '\n') parser
->line
++;
291 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
293 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
296 if(ptr2
[0] == '\n') parser
->line
++;
299 element
= malloc(ptr2
-ptr1
+1);
300 strncpy(element
,ptr1
,ptr2
-ptr1
);
301 element
[ptr2
-ptr1
] = '\0';
303 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
304 if(ptr2
[0] == '\0') {
305 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
309 if(ptr2
[0] == '\n') parser
->line
++;
311 attrib_line
= parser
->line
;
315 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
316 if(ptr3
[0] == '"') quotes
^= 1;
317 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
319 if(ptr3
[0] == '\n') parser
->line
++;
321 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
322 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
327 // Save attribs string
329 attribs
= malloc(ptr3
-ptr2
+1);
330 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
331 attribs
[ptr3
-ptr2
] = '\0';
333 //bs_line = parser->line;
334 if(ptr3
[0] != '/') { // Not Self closed element
336 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
338 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
340 if(attribs
) free(attribs
);
343 if(ptr3
[0] == '\n') parser
->line
++;
346 body_line
= parser
->line
;
347 while(1) { // Find closing element
348 for( ; ptr4
[0] != '<' ; ptr4
++) {
349 if(ptr4
[0] == '\0') {
353 if(ptr4
[0] == '\n') parser
->line
++;
355 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
356 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
357 if(ptr4
[0] == '\0') {
361 if(ptr1
[0] == '\n') parser
->line
++;
365 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
366 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
368 if(attribs
) free(attribs
);
371 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
375 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
378 ptr4
+= 2+strlen(element
);
381 ret
= ptr4
+strlen(element
)+3;
384 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
385 // if(ptr4[0] == '\0') parser->line--;
388 body
= malloc(ptr4
-ptr3
+1);
389 strncpy(body
,ptr3
,ptr4
-ptr3
);
390 body
[ptr4
-ptr3
] = '\0';
398 ret
= ptr3
+ 2; // 2 is for />
401 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
402 if(ret
[0] == '\n') parser
->line
++;
405 ret_line
= parser
->line
;
408 parser
->line
= attrib_line
;
409 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
412 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
423 parser
->last_body
= body
;
424 parser
->ret_stack_size
++;
425 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
426 if(parser
->ret_stack_size
> 1)
427 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
428 parser
->ret_stack
[0].buffer
= ret
;
429 parser
->ret_stack
[0].line
= ret_line
;
430 parser
->line
= body
? body_line
: ret_line
;
438 asx_parse_param(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
441 name
= asx_get_attrib("NAME",attribs
);
443 asx_warning_attrib_required(parser
,"PARAM" ,"NAME" );
446 val
= asx_get_attrib("VALUE",attribs
);
447 if(m_config_get_option(mconfig
,name
) == NULL
) {
448 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Found unknown param in asx: %s",name
);
450 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"=%s\n",val
);
452 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"\n");
455 play_tree_set_param(pt
,name
,val
);
461 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
464 href
= asx_get_attrib("HREF",attribs
);
466 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
470 // replace http my mmshttp to avoid infinite loops
471 // disabled since some playlists for e.g. WinAMP use asx as well
472 // "-user-agent NSPlayer/4.1.0.3856" is a possible workaround
473 if (strncmp(href
, "http://", 7) == 0) {
474 char *newref
= malloc(3 + strlen(href
) + 1);
475 strcpy(newref
, "mms");
476 strcpy(newref
+ 3, href
);
482 play_tree_add_file(pt
,href
);
484 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
491 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
495 play_tree_parser_t
* ptp
;
496 int f
=DEMUXER_TYPE_UNKNOWN
;
501 href
= asx_get_attrib("HREF",_attribs
);
503 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
506 stream
=open_stream(href
,0,&f
);
508 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
513 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
515 ptp
= play_tree_parser_new(stream
,parser
->deep
+1);
517 pt
= play_tree_parser_get_play_tree(ptp
, 1);
519 play_tree_parser_free(ptp
);
522 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
528 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
529 char *element
,*body
,**attribs
;
533 ref
= play_tree_new();
535 while(buffer
&& buffer
[0] != '\0') {
536 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
538 asx_warning_body_parse_error(parser
,"ENTRY");
540 } else if (r
== 0) { // No more element
543 if(strcasecmp(element
,"REF") == 0) {
544 asx_parse_ref(parser
,attribs
,ref
);
545 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
548 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
550 asx_free_attribs(attribs
);
554 play_tree_free(ref
,1);
563 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
564 char *element
,*body
,**attribs
;
565 play_tree_t
*repeat
, *list
=NULL
, *entry
;
569 repeat
= play_tree_new();
571 count
= asx_get_attrib("COUNT",_attribs
);
573 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
574 repeat
->loop
= -1; // Infinit
576 repeat
->loop
= atoi(count
);
578 if(repeat
->loop
== 0) repeat
->loop
= 1;
579 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
582 while(buffer
&& buffer
[0] != '\0') {
583 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
585 asx_warning_body_parse_error(parser
,"REPEAT");
587 } else if (r
== 0) { // No more element
590 if(strcasecmp(element
,"ENTRY") == 0) {
591 entry
= asx_parse_entry(parser
,body
,attribs
);
593 if(!list
) list
= entry
;
594 else play_tree_append_entry(list
,entry
);
595 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
597 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
598 entry
= asx_parse_entryref(parser
,body
,attribs
);
600 if(!list
) list
= entry
;
601 else play_tree_append_entry(list
,entry
);
602 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
604 } else if(strcasecmp(element
,"REPEAT") == 0) {
605 entry
= asx_parse_repeat(parser
,body
,attribs
);
607 if(!list
) list
= entry
;
608 else play_tree_append_entry(list
,entry
);
609 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
611 } else if(strcasecmp(element
,"PARAM") == 0) {
612 asx_parse_param(parser
,attribs
,repeat
);
614 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
616 asx_free_attribs(attribs
);
620 play_tree_free(repeat
,1);
623 play_tree_set_child(repeat
,list
);
632 asx_parser_build_tree(char* buffer
,int deep
) {
633 char *element
,*asx_body
,**asx_attribs
,*body
= NULL
, **attribs
;
635 play_tree_t
*asx
,*entry
,*list
= NULL
;
636 ASX_Parser_t
* parser
= asx_parser_new();
641 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
643 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
644 asx_parser_free(parser
);
646 } else if(r
== 0) { // No contents
647 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
648 asx_parser_free(parser
);
652 if(strcasecmp(element
,"ASX") != 0) {
653 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
654 asx_free_attribs(asx_attribs
);
655 asx_parser_free(parser
);
660 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
661 asx_free_attribs(asx_attribs
);
662 asx_parser_free(parser
);
666 asx
= play_tree_new();
668 while(buffer
&& buffer
[0] != '\0') {
669 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
671 asx_warning_body_parse_error(parser
,"ASX");
672 asx_parser_free(parser
);
674 } else if (r
== 0) { // No more element
677 if(strcasecmp(element
,"ENTRY") == 0) {
678 entry
= asx_parse_entry(parser
,body
,attribs
);
680 if(!list
) list
= entry
;
681 else play_tree_append_entry(list
,entry
);
682 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
684 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
685 entry
= asx_parse_entryref(parser
,body
,attribs
);
687 if(!list
) list
= entry
;
688 else play_tree_append_entry(list
,entry
);
689 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
691 } else if(strcasecmp(element
,"REPEAT") == 0) {
692 entry
= asx_parse_repeat(parser
,body
,attribs
);
694 if(!list
) list
= entry
;
695 else play_tree_append_entry(list
,entry
);
696 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
699 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
701 asx_free_attribs(attribs
);
705 asx_free_attribs(asx_attribs
);
706 asx_parser_free(parser
);
710 play_tree_free(asx
,1);
715 play_tree_set_child(asx
,list
);