10 #include "playtreeparser.h"
11 #include "stream/stream.h"
12 #include "libmpdemux/demuxer.h"
13 #include "asxparser.h"
20 asx_list_add(void* list_ptr
,void* entry
){
21 void** list
= *(void***)list_ptr
;
25 for( ; list
[c
] != NULL
; c
++) ;
27 list
= (void*)realloc(list
,sizeof(void*)*(c
+2));
32 *(void***)list_ptr
= list
;
37 asx_list_remove(void* list_ptr
,void* entry
,ASX_FreeFunc free_func
) {
38 void** list
= *(void***)list_ptr
;
41 if(list
== NULL
) return;
43 for(c
= 0 ; list
[c
] != NULL
; c
++){
44 if(list
[c
] == entry
) e
= c
;
47 if(e
== -1) return; // Not found
49 if(free_func
!= NULL
) free_func(list
[e
]);
51 if(c
== 1) { // Only one entry, we drop all
53 *(void**)list_ptr
= NULL
;
57 if(c
> e
) // If c==e the memmove is not needed
58 memmove(list
+e
,list
+e
+1,(c
-e
)*sizeof(void*));
60 list
= (void*)realloc(list
,(c
-1)*sizeof(void*));
63 *(void***)list_ptr
= list
;
67 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
68 void** ptr
= *(void***)list_ptr
;
69 if(ptr
== NULL
) return;
70 if(free_func
!= NULL
) {
71 for( ; *ptr
!= NULL
; ptr
++)
74 free(*(void**)list_ptr
);
75 *(void**)list_ptr
= NULL
;
81 asx_get_attrib(const char* attrib
,char** attribs
) {
84 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
85 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
86 if(strcasecmp(ptr
[0],attrib
) == 0)
87 return strdup(ptr
[1]);
93 asx_attrib_to_enum(const char* val
,char** valid_vals
) {
97 if(valid_vals
== NULL
|| val
== NULL
) return -2;
98 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
99 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
106 #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)
107 #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)
110 asx_parser_new(struct m_config
*mconfig
)
112 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
113 parser
->mconfig
= mconfig
;
118 asx_parser_free(ASX_Parser_t
* parser
) {
120 if(parser
->ret_stack
) free(parser
->ret_stack
);
125 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
126 #define SPACE " \n\t\r"
129 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
130 char *ptr1
, *ptr2
, *ptr3
;
132 char **attribs
= NULL
;
137 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
138 if(*ptr1
== '\0') break;
140 ptr3
= strchr(ptr1
,'=');
141 if(ptr3
== NULL
) break;
142 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
144 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
148 attrib
= malloc(ptr2
-ptr1
+2);
149 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
150 attrib
[ptr2
-ptr1
+1] = '\0';
152 ptr1
= strchr(ptr3
,'"');
153 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
154 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
155 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
159 ptr2
= strchr(ptr1
+1,ptr1
[0]);
161 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
166 val
= malloc(ptr2
-ptr1
+1);
167 strncpy(val
,ptr1
,ptr2
-ptr1
);
168 val
[ptr2
-ptr1
] = '\0';
171 attribs
= (char**)realloc(attribs
,(2*n_attrib
+1)*sizeof(char*));
172 attribs
[n_attrib
*2-2] = attrib
;
173 attribs
[n_attrib
*2-1] = val
;
179 attribs
[n_attrib
*2] = NULL
;
187 * Return -1 on error, 0 when nothing is found, 1 on sucess
190 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
191 char** _element
,char** _body
,char*** _attribs
) {
192 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
193 char *attribs
= NULL
;
194 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
196 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
199 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
200 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
204 *_body
= *_element
= NULL
;
208 if(buffer
== NULL
) return 0;
210 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
211 ASX_LineSave_t
* ls
= parser
->ret_stack
;
213 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
214 if(buffer
== ls
[i
].buffer
) {
215 parser
->line
= ls
[i
].line
;
220 if( i
< parser
->ret_stack_size
) {
222 if( i
< parser
->ret_stack_size
)
223 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
224 parser
->ret_stack_size
-= i
;
225 if(parser
->ret_stack_size
> 0)
226 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
228 free(parser
->ret_stack
);
229 parser
->ret_stack
= NULL
;
236 for( ; ptr1
[0] != '<' ; ptr1
++) {
237 if(ptr1
[0] == '\0') {
241 if(ptr1
[0] == '\n') parser
->line
++;
243 //ptr1 = strchr(ptr1,'<');
244 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
246 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
247 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
248 if(ptr1
[0] == '\0') {
252 if(ptr1
[0] == '\n') parser
->line
++;
254 //ptr1 = strstr(ptr1,"-->");
256 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
264 // Is this space skip very useful ??
265 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
266 if(ptr1
[0] == '\0') {
267 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
270 if(ptr1
[0] == '\n') parser
->line
++;
273 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
275 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
278 if(ptr2
[0] == '\n') parser
->line
++;
281 element
= malloc(ptr2
-ptr1
+1);
282 strncpy(element
,ptr1
,ptr2
-ptr1
);
283 element
[ptr2
-ptr1
] = '\0';
285 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
286 if(ptr2
[0] == '\0') {
287 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
291 if(ptr2
[0] == '\n') parser
->line
++;
293 attrib_line
= parser
->line
;
297 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
298 if(ptr3
[0] == '"') quotes
^= 1;
299 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
301 if(ptr3
[0] == '\n') parser
->line
++;
303 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
304 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
309 // Save attribs string
311 attribs
= malloc(ptr3
-ptr2
+1);
312 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
313 attribs
[ptr3
-ptr2
] = '\0';
315 //bs_line = parser->line;
316 if(ptr3
[0] != '/') { // Not Self closed element
318 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
320 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
322 if(attribs
) free(attribs
);
325 if(ptr3
[0] == '\n') parser
->line
++;
328 body_line
= parser
->line
;
329 while(1) { // Find closing element
330 for( ; ptr4
[0] != '<' ; ptr4
++) {
331 if(ptr4
[0] == '\0') {
335 if(ptr4
[0] == '\n') parser
->line
++;
337 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
338 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
339 if(ptr4
[0] == '\0') {
343 if(ptr1
[0] == '\n') parser
->line
++;
347 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
348 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
350 if(attribs
) free(attribs
);
353 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
357 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
360 ptr4
+= 2+strlen(element
);
363 ret
= ptr4
+strlen(element
)+3;
366 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
367 // if(ptr4[0] == '\0') parser->line--;
370 body
= malloc(ptr4
-ptr3
+1);
371 strncpy(body
,ptr3
,ptr4
-ptr3
);
372 body
[ptr4
-ptr3
] = '\0';
380 ret
= ptr3
+ 2; // 2 is for />
383 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
384 if(ret
[0] == '\n') parser
->line
++;
387 ret_line
= parser
->line
;
390 parser
->line
= attrib_line
;
391 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
394 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
405 parser
->last_body
= body
;
406 parser
->ret_stack_size
++;
407 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
408 if(parser
->ret_stack_size
> 1)
409 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
410 parser
->ret_stack
[0].buffer
= ret
;
411 parser
->ret_stack
[0].line
= ret_line
;
412 parser
->line
= body
? body_line
: ret_line
;
420 asx_parse_param(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
423 name
= asx_get_attrib("NAME",attribs
);
425 asx_warning_attrib_required(parser
,"PARAM" ,"NAME" );
428 val
= asx_get_attrib("VALUE",attribs
);
429 if(m_config_get_option(parser
->mconfig
,name
) == NULL
) {
430 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Found unknown param in asx: %s",name
);
432 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"=%s\n",val
);
434 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"\n");
437 play_tree_set_param(pt
,name
,val
);
443 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
446 href
= asx_get_attrib("HREF",attribs
);
448 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
452 // replace http my mmshttp to avoid infinite loops
453 // disabled since some playlists for e.g. WinAMP use asx as well
454 // "-user-agent NSPlayer/4.1.0.3856" is a possible workaround
455 if (strncmp(href
, "http://", 7) == 0) {
456 char *newref
= malloc(3 + strlen(href
) + 1);
457 strcpy(newref
, "mms");
458 strcpy(newref
+ 3, href
);
464 play_tree_add_file(pt
,href
);
466 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
473 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
477 play_tree_parser_t
* ptp
;
478 int f
=DEMUXER_TYPE_UNKNOWN
;
483 href
= asx_get_attrib("HREF",_attribs
);
485 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
488 stream
=open_stream(href
,0,&f
);
490 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
495 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
497 ptp
= play_tree_parser_new(stream
, parser
->mconfig
, parser
->deep
+1);
499 pt
= play_tree_parser_get_play_tree(ptp
, 1);
501 play_tree_parser_free(ptp
);
504 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
510 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
511 char *element
,*body
,**attribs
;
515 ref
= play_tree_new();
517 while(buffer
&& buffer
[0] != '\0') {
518 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
520 asx_warning_body_parse_error(parser
,"ENTRY");
522 } else if (r
== 0) { // No more element
525 if(strcasecmp(element
,"REF") == 0) {
526 asx_parse_ref(parser
,attribs
,ref
);
527 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
530 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
532 asx_free_attribs(attribs
);
536 play_tree_free(ref
,1);
545 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
546 char *element
,*body
,**attribs
;
547 play_tree_t
*repeat
, *list
=NULL
, *entry
;
551 repeat
= play_tree_new();
553 count
= asx_get_attrib("COUNT",_attribs
);
555 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
556 repeat
->loop
= -1; // Infinit
558 repeat
->loop
= atoi(count
);
560 if(repeat
->loop
== 0) repeat
->loop
= 1;
561 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
564 while(buffer
&& buffer
[0] != '\0') {
565 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
567 asx_warning_body_parse_error(parser
,"REPEAT");
569 } else if (r
== 0) { // No more element
572 if(strcasecmp(element
,"ENTRY") == 0) {
573 entry
= asx_parse_entry(parser
,body
,attribs
);
575 if(!list
) list
= entry
;
576 else play_tree_append_entry(list
,entry
);
577 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
579 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
580 entry
= asx_parse_entryref(parser
,body
,attribs
);
582 if(!list
) list
= entry
;
583 else play_tree_append_entry(list
,entry
);
584 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
586 } else if(strcasecmp(element
,"REPEAT") == 0) {
587 entry
= asx_parse_repeat(parser
,body
,attribs
);
589 if(!list
) list
= entry
;
590 else play_tree_append_entry(list
,entry
);
591 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
593 } else if(strcasecmp(element
,"PARAM") == 0) {
594 asx_parse_param(parser
,attribs
,repeat
);
596 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
598 asx_free_attribs(attribs
);
602 play_tree_free(repeat
,1);
605 play_tree_set_child(repeat
,list
);
614 asx_parser_build_tree(struct m_config
*mconfig
, char* buffer
,int deep
) {
615 char *element
,*asx_body
,**asx_attribs
,*body
= NULL
, **attribs
;
617 play_tree_t
*asx
,*entry
,*list
= NULL
;
618 ASX_Parser_t
* parser
= asx_parser_new(mconfig
);
623 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
625 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
626 asx_parser_free(parser
);
628 } else if(r
== 0) { // No contents
629 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
630 asx_parser_free(parser
);
634 if(strcasecmp(element
,"ASX") != 0) {
635 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
636 asx_free_attribs(asx_attribs
);
637 asx_parser_free(parser
);
642 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
643 asx_free_attribs(asx_attribs
);
644 asx_parser_free(parser
);
648 asx
= play_tree_new();
650 while(buffer
&& buffer
[0] != '\0') {
651 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
653 asx_warning_body_parse_error(parser
,"ASX");
654 asx_parser_free(parser
);
656 } else if (r
== 0) { // No more element
659 if(strcasecmp(element
,"ENTRY") == 0) {
660 entry
= asx_parse_entry(parser
,body
,attribs
);
662 if(!list
) list
= entry
;
663 else play_tree_append_entry(list
,entry
);
664 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
666 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
667 entry
= asx_parse_entryref(parser
,body
,attribs
);
669 if(!list
) list
= entry
;
670 else play_tree_append_entry(list
,entry
);
671 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
673 } else if(strcasecmp(element
,"REPEAT") == 0) {
674 entry
= asx_parse_repeat(parser
,body
,attribs
);
676 if(!list
) list
= entry
;
677 else play_tree_append_entry(list
,entry
);
678 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
681 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
683 asx_free_attribs(attribs
);
687 asx_free_attribs(asx_attribs
);
688 asx_parser_free(parser
);
692 play_tree_free(asx
,1);
697 play_tree_set_child(asx
,list
);