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"
38 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
39 void** ptr
= *(void***)list_ptr
;
40 if(ptr
== NULL
) return;
41 if(free_func
!= NULL
) {
42 for( ; *ptr
!= NULL
; ptr
++)
45 free(*(void**)list_ptr
);
46 *(void**)list_ptr
= NULL
;
52 asx_get_attrib(const char* attrib
,char** attribs
) {
55 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
56 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
57 if(strcasecmp(ptr
[0],attrib
) == 0)
58 return strdup(ptr
[1]);
64 asx_attrib_to_enum(const char* val
,char** valid_vals
) {
68 if(valid_vals
== NULL
|| val
== NULL
) return -2;
69 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
70 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
77 #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)
78 #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)
81 asx_parser_new(struct m_config
*mconfig
)
83 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
84 parser
->mconfig
= mconfig
;
89 asx_parser_free(ASX_Parser_t
* parser
) {
91 free(parser
->ret_stack
);
96 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
97 #define SPACE " \n\t\r"
100 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
101 char *ptr1
, *ptr2
, *ptr3
;
103 char **attribs
= NULL
;
108 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
109 if(*ptr1
== '\0') break;
111 ptr3
= strchr(ptr1
,'=');
112 if(ptr3
== NULL
) break;
113 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
115 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
119 attrib
= malloc(ptr2
-ptr1
+2);
120 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
121 attrib
[ptr2
-ptr1
+1] = '\0';
123 ptr1
= strchr(ptr3
,'"');
124 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
125 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
126 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
130 ptr2
= strchr(ptr1
+1,ptr1
[0]);
132 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
137 val
= malloc(ptr2
-ptr1
+1);
138 strncpy(val
,ptr1
,ptr2
-ptr1
);
139 val
[ptr2
-ptr1
] = '\0';
142 attribs
= realloc(attribs
, (2 * n_attrib
+ 1) * sizeof(char*));
143 attribs
[n_attrib
*2-2] = attrib
;
144 attribs
[n_attrib
*2-1] = val
;
150 attribs
[n_attrib
*2] = NULL
;
158 * Return -1 on error, 0 when nothing is found, 1 on sucess
161 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
162 char** _element
,char** _body
,char*** _attribs
) {
163 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
164 char *attribs
= NULL
;
165 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
167 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
170 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
171 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
175 *_body
= *_element
= NULL
;
179 if(buffer
== NULL
) return 0;
181 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
182 ASX_LineSave_t
* ls
= parser
->ret_stack
;
184 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
185 if(buffer
== ls
[i
].buffer
) {
186 parser
->line
= ls
[i
].line
;
191 if( i
< parser
->ret_stack_size
) {
193 if( i
< parser
->ret_stack_size
)
194 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
195 parser
->ret_stack_size
-= i
;
196 if(parser
->ret_stack_size
> 0)
197 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
199 free(parser
->ret_stack
);
200 parser
->ret_stack
= NULL
;
207 for( ; ptr1
[0] != '<' ; ptr1
++) {
208 if(ptr1
[0] == '\0') {
212 if(ptr1
[0] == '\n') parser
->line
++;
214 //ptr1 = strchr(ptr1,'<');
215 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
217 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
218 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
219 if(ptr1
[0] == '\0') {
223 if(ptr1
[0] == '\n') parser
->line
++;
225 //ptr1 = strstr(ptr1,"-->");
227 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
235 // Is this space skip very useful ??
236 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
237 if(ptr1
[0] == '\0') {
238 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
241 if(ptr1
[0] == '\n') parser
->line
++;
244 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
246 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
249 if(ptr2
[0] == '\n') parser
->line
++;
252 element
= malloc(ptr2
-ptr1
+1);
253 strncpy(element
,ptr1
,ptr2
-ptr1
);
254 element
[ptr2
-ptr1
] = '\0';
256 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
257 if(ptr2
[0] == '\0') {
258 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
262 if(ptr2
[0] == '\n') parser
->line
++;
264 attrib_line
= parser
->line
;
268 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
269 if(ptr3
[0] == '"') quotes
^= 1;
270 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
272 if(ptr3
[0] == '\n') parser
->line
++;
274 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
275 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
280 // Save attribs string
282 attribs
= malloc(ptr3
-ptr2
+1);
283 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
284 attribs
[ptr3
-ptr2
] = '\0';
286 //bs_line = parser->line;
287 if(ptr3
[0] != '/') { // Not Self closed element
289 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
291 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
296 if(ptr3
[0] == '\n') parser
->line
++;
299 body_line
= parser
->line
;
300 while(1) { // Find closing element
301 for( ; ptr4
[0] != '<' ; ptr4
++) {
302 if(ptr4
[0] == '\0') {
306 if(ptr4
[0] == '\n') parser
->line
++;
308 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
309 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
310 if(ptr4
[0] == '\0') {
314 if(ptr1
[0] == '\n') parser
->line
++;
318 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
319 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
324 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
328 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
331 ptr4
+= 2+strlen(element
);
334 ret
= ptr4
+strlen(element
)+3;
337 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
338 // if(ptr4[0] == '\0') parser->line--;
341 body
= malloc(ptr4
-ptr3
+1);
342 strncpy(body
,ptr3
,ptr4
-ptr3
);
343 body
[ptr4
-ptr3
] = '\0';
351 ret
= ptr3
+ 2; // 2 is for />
354 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
355 if(ret
[0] == '\n') parser
->line
++;
358 ret_line
= parser
->line
;
361 parser
->line
= attrib_line
;
362 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
365 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
376 parser
->last_body
= body
;
377 parser
->ret_stack_size
++;
378 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
379 if(parser
->ret_stack_size
> 1)
380 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
381 parser
->ret_stack
[0].buffer
= ret
;
382 parser
->ret_stack
[0].line
= ret_line
;
383 parser
->line
= body
? body_line
: ret_line
;
391 asx_parse_param(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
394 name
= asx_get_attrib("NAME",attribs
);
396 asx_warning_attrib_required(parser
,"PARAM" ,"NAME" );
399 val
= asx_get_attrib("VALUE",attribs
);
400 if(m_config_get_option(parser
->mconfig
,name
) == NULL
) {
401 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Found unknown param in asx: %s",name
);
403 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"=%s\n",val
);
405 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"\n");
408 play_tree_set_param(pt
,name
,val
);
414 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
417 href
= asx_get_attrib("HREF",attribs
);
419 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
423 // replace http my mmshttp to avoid infinite loops
424 // disabled since some playlists for e.g. WinAMP use asx as well
425 // "-user-agent NSPlayer/4.1.0.3856" is a possible workaround
426 if (strncmp(href
, "http://", 7) == 0) {
427 char *newref
= malloc(3 + strlen(href
) + 1);
428 strcpy(newref
, "mms");
429 strcpy(newref
+ 3, href
);
435 play_tree_add_file(pt
,href
);
437 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
444 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
448 play_tree_parser_t
* ptp
;
449 int f
=DEMUXER_TYPE_UNKNOWN
;
454 href
= asx_get_attrib("HREF",_attribs
);
456 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
459 stream
=open_stream(href
,0,&f
);
461 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
466 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
468 ptp
= play_tree_parser_new(stream
, parser
->mconfig
, parser
->deep
+1);
470 pt
= play_tree_parser_get_play_tree(ptp
, 1);
472 play_tree_parser_free(ptp
);
475 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
481 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
482 char *element
,*body
,**attribs
;
486 ref
= play_tree_new();
488 while(buffer
&& buffer
[0] != '\0') {
489 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
491 asx_warning_body_parse_error(parser
,"ENTRY");
493 } else if (r
== 0) { // No more element
496 if(strcasecmp(element
,"REF") == 0) {
497 asx_parse_ref(parser
,attribs
,ref
);
498 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
501 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
503 asx_free_attribs(attribs
);
507 play_tree_free(ref
,1);
516 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
517 char *element
,*body
,**attribs
;
518 play_tree_t
*repeat
, *list
=NULL
, *entry
;
522 repeat
= play_tree_new();
524 count
= asx_get_attrib("COUNT",_attribs
);
526 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
527 repeat
->loop
= -1; // Infinit
529 repeat
->loop
= atoi(count
);
531 if(repeat
->loop
== 0) repeat
->loop
= 1;
532 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
535 while(buffer
&& buffer
[0] != '\0') {
536 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
538 asx_warning_body_parse_error(parser
,"REPEAT");
540 } else if (r
== 0) { // No more element
543 if(strcasecmp(element
,"ENTRY") == 0) {
544 entry
= asx_parse_entry(parser
,body
,attribs
);
546 if(!list
) list
= entry
;
547 else play_tree_append_entry(list
,entry
);
548 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
550 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
551 entry
= asx_parse_entryref(parser
,body
,attribs
);
553 if(!list
) list
= entry
;
554 else play_tree_append_entry(list
,entry
);
555 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
557 } else if(strcasecmp(element
,"REPEAT") == 0) {
558 entry
= asx_parse_repeat(parser
,body
,attribs
);
560 if(!list
) list
= entry
;
561 else play_tree_append_entry(list
,entry
);
562 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
564 } else if(strcasecmp(element
,"PARAM") == 0) {
565 asx_parse_param(parser
,attribs
,repeat
);
567 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
569 asx_free_attribs(attribs
);
573 play_tree_free(repeat
,1);
576 play_tree_set_child(repeat
,list
);
585 asx_parser_build_tree(struct m_config
*mconfig
, char* buffer
,int deep
) {
586 char *element
,*asx_body
,**asx_attribs
,*body
= NULL
, **attribs
;
588 play_tree_t
*asx
,*entry
,*list
= NULL
;
589 ASX_Parser_t
* parser
= asx_parser_new(mconfig
);
594 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
596 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
597 asx_parser_free(parser
);
599 } else if(r
== 0) { // No contents
600 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
601 asx_parser_free(parser
);
605 if(strcasecmp(element
,"ASX") != 0) {
606 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
607 asx_free_attribs(asx_attribs
);
608 asx_parser_free(parser
);
613 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
614 asx_free_attribs(asx_attribs
);
615 asx_parser_free(parser
);
619 asx
= play_tree_new();
621 while(buffer
&& buffer
[0] != '\0') {
622 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
624 asx_warning_body_parse_error(parser
,"ASX");
625 asx_parser_free(parser
);
627 } else if (r
== 0) { // No more element
630 if(strcasecmp(element
,"ENTRY") == 0) {
631 entry
= asx_parse_entry(parser
,body
,attribs
);
633 if(!list
) list
= entry
;
634 else play_tree_append_entry(list
,entry
);
635 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
637 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
638 entry
= asx_parse_entryref(parser
,body
,attribs
);
640 if(!list
) list
= entry
;
641 else play_tree_append_entry(list
,entry
);
642 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
644 } else if(strcasecmp(element
,"REPEAT") == 0) {
645 entry
= asx_parse_repeat(parser
,body
,attribs
);
647 if(!list
) list
= entry
;
648 else play_tree_append_entry(list
,entry
);
649 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
652 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
654 asx_free_attribs(attribs
);
658 asx_free_attribs(asx_attribs
);
659 asx_parser_free(parser
);
663 play_tree_free(asx
,1);
668 play_tree_set_child(asx
,list
);