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"
37 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
38 void** ptr
= *(void***)list_ptr
;
39 if(ptr
== NULL
) return;
40 if(free_func
!= NULL
) {
41 for( ; *ptr
!= NULL
; ptr
++)
44 free(*(void**)list_ptr
);
45 *(void**)list_ptr
= NULL
;
51 asx_get_attrib(const char* attrib
,char** attribs
) {
54 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
55 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
56 if(strcasecmp(ptr
[0],attrib
) == 0)
57 return strdup(ptr
[1]);
63 asx_attrib_to_enum(const char* val
,char** valid_vals
) {
67 if(valid_vals
== NULL
|| val
== NULL
) return -2;
68 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
69 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
76 #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)
77 #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)
80 asx_parser_new(struct MPOpts
*opts
)
82 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
88 asx_parser_free(ASX_Parser_t
* parser
) {
90 free(parser
->ret_stack
);
95 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
96 #define SPACE " \n\t\r"
99 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
100 char *ptr1
, *ptr2
, *ptr3
;
102 char **attribs
= NULL
;
107 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
108 if(*ptr1
== '\0') break;
110 ptr3
= strchr(ptr1
,'=');
111 if(ptr3
== NULL
) break;
112 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
114 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
118 attrib
= malloc(ptr2
-ptr1
+2);
119 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
120 attrib
[ptr2
-ptr1
+1] = '\0';
122 ptr1
= strchr(ptr3
,'"');
123 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
124 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
125 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
129 ptr2
= strchr(ptr1
+1,ptr1
[0]);
131 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
136 val
= malloc(ptr2
-ptr1
+1);
137 strncpy(val
,ptr1
,ptr2
-ptr1
);
138 val
[ptr2
-ptr1
] = '\0';
141 attribs
= realloc(attribs
, (2 * n_attrib
+ 1) * sizeof(char*));
142 attribs
[n_attrib
*2-2] = attrib
;
143 attribs
[n_attrib
*2-1] = val
;
149 attribs
[n_attrib
*2] = NULL
;
157 * Return -1 on error, 0 when nothing is found, 1 on sucess
160 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
161 char** _element
,char** _body
,char*** _attribs
) {
162 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
163 char *attribs
= NULL
;
164 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
166 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
169 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
170 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
174 *_body
= *_element
= NULL
;
178 if(buffer
== NULL
) return 0;
180 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
181 ASX_LineSave_t
* ls
= parser
->ret_stack
;
183 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
184 if(buffer
== ls
[i
].buffer
) {
185 parser
->line
= ls
[i
].line
;
190 if( i
< parser
->ret_stack_size
) {
192 if( i
< parser
->ret_stack_size
)
193 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
194 parser
->ret_stack_size
-= i
;
195 if(parser
->ret_stack_size
> 0)
196 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
198 free(parser
->ret_stack
);
199 parser
->ret_stack
= NULL
;
206 for( ; ptr1
[0] != '<' ; ptr1
++) {
207 if(ptr1
[0] == '\0') {
211 if(ptr1
[0] == '\n') parser
->line
++;
213 //ptr1 = strchr(ptr1,'<');
214 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
216 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
217 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
218 if(ptr1
[0] == '\0') {
222 if(ptr1
[0] == '\n') parser
->line
++;
224 //ptr1 = strstr(ptr1,"-->");
226 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
234 // Is this space skip very useful ??
235 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
236 if(ptr1
[0] == '\0') {
237 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
240 if(ptr1
[0] == '\n') parser
->line
++;
243 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
245 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
248 if(ptr2
[0] == '\n') parser
->line
++;
251 element
= malloc(ptr2
-ptr1
+1);
252 strncpy(element
,ptr1
,ptr2
-ptr1
);
253 element
[ptr2
-ptr1
] = '\0';
255 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
256 if(ptr2
[0] == '\0') {
257 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
261 if(ptr2
[0] == '\n') parser
->line
++;
263 attrib_line
= parser
->line
;
267 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
268 if(ptr3
[0] == '"') quotes
^= 1;
269 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
271 if(ptr3
[0] == '\n') parser
->line
++;
273 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
274 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
279 // Save attribs string
281 attribs
= malloc(ptr3
-ptr2
+1);
282 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
283 attribs
[ptr3
-ptr2
] = '\0';
285 //bs_line = parser->line;
286 if(ptr3
[0] != '/') { // Not Self closed element
288 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
290 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
295 if(ptr3
[0] == '\n') parser
->line
++;
298 body_line
= parser
->line
;
299 while(1) { // Find closing element
300 for( ; ptr4
[0] != '<' ; ptr4
++) {
301 if(ptr4
[0] == '\0') {
305 if(ptr4
[0] == '\n') parser
->line
++;
307 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
308 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
309 if(ptr4
[0] == '\0') {
313 if(ptr1
[0] == '\n') parser
->line
++;
317 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
318 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
323 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
327 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
330 ptr4
+= 2+strlen(element
);
333 ret
= ptr4
+strlen(element
)+3;
336 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
337 // if(ptr4[0] == '\0') parser->line--;
340 body
= malloc(ptr4
-ptr3
+1);
341 strncpy(body
,ptr3
,ptr4
-ptr3
);
342 body
[ptr4
-ptr3
] = '\0';
350 ret
= ptr3
+ 2; // 2 is for />
353 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
354 if(ret
[0] == '\n') parser
->line
++;
357 ret_line
= parser
->line
;
360 parser
->line
= attrib_line
;
361 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
364 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
375 parser
->last_body
= body
;
376 parser
->ret_stack_size
++;
377 parser
->ret_stack
= realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
378 if(parser
->ret_stack_size
> 1)
379 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
380 parser
->ret_stack
[0].buffer
= ret
;
381 parser
->ret_stack
[0].line
= ret_line
;
382 parser
->line
= body
? body_line
: ret_line
;
390 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
393 href
= asx_get_attrib("HREF",attribs
);
395 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
399 // replace http my mmshttp to avoid infinite loops
400 // disabled since some playlists for e.g. WinAMP use asx as well
401 // "-user-agent NSPlayer/4.1.0.3856" is a possible workaround
402 if (strncmp(href
, "http://", 7) == 0) {
403 char *newref
= malloc(3 + strlen(href
) + 1);
404 strcpy(newref
, "mms");
405 strcpy(newref
+ 3, href
);
411 play_tree_add_file(pt
,href
);
413 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
420 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
424 play_tree_parser_t
* ptp
;
425 int f
=DEMUXER_TYPE_UNKNOWN
;
430 href
= asx_get_attrib("HREF",_attribs
);
432 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
435 stream
=open_stream(href
, parser
->opts
, &f
);
437 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
442 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
444 ptp
= play_tree_parser_new(stream
, parser
->opts
, parser
->deep
+1);
446 pt
= play_tree_parser_get_play_tree(ptp
, 1);
448 play_tree_parser_free(ptp
);
451 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
457 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
458 char *element
,*body
,**attribs
;
462 ref
= play_tree_new();
464 while(buffer
&& buffer
[0] != '\0') {
465 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
467 asx_warning_body_parse_error(parser
,"ENTRY");
469 } else if (r
== 0) { // No more element
472 if(strcasecmp(element
,"REF") == 0) {
473 asx_parse_ref(parser
,attribs
,ref
);
474 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
477 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
479 asx_free_attribs(attribs
);
483 play_tree_free(ref
,1);
492 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
493 char *element
,*body
,**attribs
;
494 play_tree_t
*repeat
, *list
=NULL
, *entry
;
498 repeat
= play_tree_new();
500 count
= asx_get_attrib("COUNT",_attribs
);
502 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
503 repeat
->loop
= -1; // Infinit
505 repeat
->loop
= atoi(count
);
507 if(repeat
->loop
== 0) repeat
->loop
= 1;
508 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
511 while(buffer
&& buffer
[0] != '\0') {
512 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
514 asx_warning_body_parse_error(parser
,"REPEAT");
516 } else if (r
== 0) { // No more element
519 if(strcasecmp(element
,"ENTRY") == 0) {
520 entry
= asx_parse_entry(parser
,body
,attribs
);
522 if(!list
) list
= entry
;
523 else play_tree_append_entry(list
,entry
);
524 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
526 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
527 entry
= asx_parse_entryref(parser
,body
,attribs
);
529 if(!list
) list
= entry
;
530 else play_tree_append_entry(list
,entry
);
531 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
533 } else if(strcasecmp(element
,"REPEAT") == 0) {
534 entry
= asx_parse_repeat(parser
,body
,attribs
);
536 if(!list
) list
= entry
;
537 else play_tree_append_entry(list
,entry
);
538 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
541 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
543 asx_free_attribs(attribs
);
547 play_tree_free(repeat
,1);
550 play_tree_set_child(repeat
,list
);
559 asx_parser_build_tree(struct MPOpts
*opts
, char* buffer
,int deep
) {
560 char *element
,*asx_body
,**asx_attribs
,*body
= NULL
, **attribs
;
562 play_tree_t
*asx
,*entry
,*list
= NULL
;
563 ASX_Parser_t
* parser
= asx_parser_new(opts
);
568 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
570 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
571 asx_parser_free(parser
);
573 } else if(r
== 0) { // No contents
574 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
575 asx_parser_free(parser
);
579 if(strcasecmp(element
,"ASX") != 0) {
580 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
581 asx_free_attribs(asx_attribs
);
582 asx_parser_free(parser
);
587 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
588 asx_free_attribs(asx_attribs
);
589 asx_parser_free(parser
);
593 asx
= play_tree_new();
595 while(buffer
&& buffer
[0] != '\0') {
596 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
598 asx_warning_body_parse_error(parser
,"ASX");
599 asx_parser_free(parser
);
601 } else if (r
== 0) { // No more element
604 if(strcasecmp(element
,"ENTRY") == 0) {
605 entry
= asx_parse_entry(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 asx\n",element
);
611 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
612 entry
= asx_parse_entryref(parser
,body
,attribs
);
614 if(!list
) list
= entry
;
615 else play_tree_append_entry(list
,entry
);
616 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
618 } else if(strcasecmp(element
,"REPEAT") == 0) {
619 entry
= asx_parse_repeat(parser
,body
,attribs
);
621 if(!list
) list
= entry
;
622 else play_tree_append_entry(list
,entry
);
623 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
626 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
628 asx_free_attribs(attribs
);
632 asx_free_attribs(asx_attribs
);
633 asx_parser_free(parser
);
637 play_tree_free(asx
,1);
642 play_tree_set_child(asx
,list
);