10 #include "playtreeparser.h"
11 #include "libmpdemux/stream.h"
12 #include "asxparser.h"
16 extern m_config_t
* mconfig
;
21 asx_list_add(void* list_ptr
,void* entry
){
22 void** list
= *(void***)list_ptr
;
26 for( ; list
[c
] != NULL
; c
++) ;
28 list
= (void*)realloc(list
,sizeof(void*)*(c
+2));
33 *(void***)list_ptr
= list
;
38 asx_list_remove(void* list_ptr
,void* entry
,ASX_FreeFunc free_func
) {
39 void** list
= *(void***)list_ptr
;
42 if(list
== NULL
) return;
44 for(c
= 0 ; list
[c
] != NULL
; c
++){
45 if(list
[c
] == entry
) e
= c
;
48 if(e
== -1) return; // Not found
50 if(free_func
!= NULL
) free_func(list
[e
]);
52 if(c
== 1) { // Only one entry, we drop all
54 *(void**)list_ptr
= NULL
;
58 if(c
> e
) // If c==e the memmove is not needed
59 memmove(list
+e
,list
+e
+1,(c
-e
)*sizeof(void*));
61 list
= (void*)realloc(list
,(c
-1)*sizeof(void*));
64 *(void***)list_ptr
= list
;
68 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
69 void** ptr
= *(void***)list_ptr
;
70 if(ptr
== NULL
) return;
71 if(free_func
!= NULL
) {
72 for( ; *ptr
!= NULL
; ptr
++)
75 free(*(void**)list_ptr
);
76 *(void**)list_ptr
= NULL
;
82 asx_get_attrib(char* attrib
,char** attribs
) {
85 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
86 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
87 if(strcasecmp(ptr
[0],attrib
) == 0)
88 return strdup(ptr
[1]);
94 asx_attrib_to_enum(char* val
,char** valid_vals
) {
98 if(valid_vals
== NULL
|| val
== NULL
) return -2;
99 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
100 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
108 asx_warning_attrib_invalid(ASX_Parser_t
* parser
, char* elem
, char* attrib
,
109 char** valid_vals
,char* val
) {
110 char *str
,*vals
,**ptr
;
113 if(valid_vals
== NULL
|| valid_vals
[0] == NULL
) return;
115 len
= strlen(valid_vals
[0]) + 1;
116 for(ptr
= valid_vals
+1 ; ptr
[0] != NULL
; ptr
++) {
117 len
+= strlen(ptr
[0]);
118 len
+= ((ptr
[1] == NULL
) ? 4 : 2);
120 str
= vals
= (char*)malloc(len
);
121 vals
+= sprintf(vals
,"%s",valid_vals
[0]);
122 for(ptr
= valid_vals
+ 1 ; ptr
[0] != NULL
; ptr
++) {
124 vals
+= sprintf(vals
," or %s",ptr
[0]);
126 vals
+= sprintf(vals
,", %s",ptr
[0]);
128 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"at line %d : attribute %s of element %s is invalid (%s). Valid values are %s",
129 parser
->line
,attrib
,elem
,val
,str
);
134 asx_get_yes_no_attrib(ASX_Parser_t
* parser
, char* element
, char* attrib
,char** attribs
,int def
) {
135 char* val
= asx_get_attrib(attrib
,attribs
);
136 char* valids
[] = { "NO", "YES", NULL
};
139 if(val
== NULL
) return def
;
140 r
= asx_attrib_to_enum(val
,valids
);
143 asx_warning_attrib_invalid(parser
,element
,attrib
,valids
,val
);
151 #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)
152 #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)
155 asx_parser_new(void) {
156 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
161 asx_parser_free(ASX_Parser_t
* parser
) {
163 if(parser
->ret_stack
) free(parser
->ret_stack
);
168 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
169 #define SPACE " \n\t\r"
172 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
173 char *ptr1
, *ptr2
, *ptr3
;
175 char **attribs
= NULL
;
180 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
181 if(*ptr1
== '\0') break;
183 ptr3
= strchr(ptr1
,'=');
184 if(ptr3
== NULL
) break;
185 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
187 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
191 attrib
= (char*)malloc(ptr2
-ptr1
+2);
192 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
193 attrib
[ptr2
-ptr1
+1] = '\0';
195 ptr1
= strchr(ptr3
,'"');
196 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
197 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
198 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
202 ptr2
= strchr(ptr1
+1,ptr1
[0]);
204 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
209 val
= (char*)malloc(ptr2
-ptr1
+1);
210 strncpy(val
,ptr1
,ptr2
-ptr1
);
211 val
[ptr2
-ptr1
] = '\0';
214 attribs
= (char**)realloc(attribs
,(2*n_attrib
+1)*sizeof(char*));
215 attribs
[n_attrib
*2-2] = attrib
;
216 attribs
[n_attrib
*2-1] = val
;
222 attribs
[n_attrib
*2] = NULL
;
230 * Return -1 on error, 0 when nothing is found, 1 on sucess
233 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
234 char** _element
,char** _body
,char*** _attribs
) {
235 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
236 char *attribs
= NULL
;
237 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
239 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
242 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
243 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
247 *_body
= *_element
= NULL
;
251 if(buffer
== NULL
) return 0;
253 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
254 ASX_LineSave_t
* ls
= parser
->ret_stack
;
256 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
257 if(buffer
== ls
[i
].buffer
) {
258 parser
->line
= ls
[i
].line
;
263 if( i
< parser
->ret_stack_size
) {
265 if( i
< parser
->ret_stack_size
)
266 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
267 parser
->ret_stack_size
-= i
;
268 if(parser
->ret_stack_size
> 0)
269 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
271 free(parser
->ret_stack
);
272 parser
->ret_stack
= NULL
;
279 for( ; ptr1
[0] != '<' ; ptr1
++) {
280 if(ptr1
[0] == '\0') {
284 if(ptr1
[0] == '\n') parser
->line
++;
286 //ptr1 = strchr(ptr1,'<');
287 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
289 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
290 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
291 if(ptr1
[0] == '\0') {
295 if(ptr1
[0] == '\n') parser
->line
++;
297 //ptr1 = strstr(ptr1,"-->");
299 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
307 // Is this space skip very useful ??
308 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
309 if(ptr1
[0] == '\0') {
310 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
313 if(ptr1
[0] == '\n') parser
->line
++;
316 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
318 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
321 if(ptr2
[0] == '\n') parser
->line
++;
324 element
= (char*)malloc(ptr2
-ptr1
+1);
325 strncpy(element
,ptr1
,ptr2
-ptr1
);
326 element
[ptr2
-ptr1
] = '\0';
328 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
329 if(ptr2
[0] == '\0') {
330 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
334 if(ptr2
[0] == '\n') parser
->line
++;
336 attrib_line
= parser
->line
;
340 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
341 if(ptr3
[0] == '"') quotes
^= 1;
342 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
344 if(ptr3
[0] == '\n') parser
->line
++;
346 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
347 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
352 // Save attribs string
354 attribs
= (char*)malloc(ptr3
-ptr2
+1);
355 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
356 attribs
[ptr3
-ptr2
] = '\0';
358 //bs_line = parser->line;
359 if(ptr3
[0] != '/') { // Not Self closed element
361 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
363 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
365 if(attribs
) free(attribs
);
368 if(ptr3
[0] == '\n') parser
->line
++;
371 body_line
= parser
->line
;
372 while(1) { // Find closing element
373 for( ; ptr4
[0] != '<' ; ptr4
++) {
374 if(ptr4
[0] == '\0') {
378 if(ptr4
[0] == '\n') parser
->line
++;
380 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
381 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
382 if(ptr4
[0] == '\0') {
386 if(ptr1
[0] == '\n') parser
->line
++;
390 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
391 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
393 if(attribs
) free(attribs
);
396 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
400 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
403 ptr4
+= 2+strlen(element
);
406 ret
= ptr4
+strlen(element
)+3;
409 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
410 // if(ptr4[0] == '\0') parser->line--;
413 body
= (char*)malloc(ptr4
-ptr3
+1);
414 strncpy(body
,ptr3
,ptr4
-ptr3
);
415 body
[ptr4
-ptr3
] = '\0';
423 ret
= ptr3
+ 2; // 2 is for />
426 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
427 if(ret
[0] == '\n') parser
->line
++;
430 ret_line
= parser
->line
;
433 parser
->line
= attrib_line
;
434 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
437 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
448 parser
->last_body
= body
;
449 parser
->ret_stack_size
++;
450 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
451 if(parser
->ret_stack_size
> 1)
452 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
453 parser
->ret_stack
[0].buffer
= ret
;
454 parser
->ret_stack
[0].line
= ret_line
;
455 parser
->line
= body
? body_line
: ret_line
;
463 asx_parse_param(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
466 name
= asx_get_attrib("NAME",attribs
);
468 asx_warning_attrib_required(parser
,"PARAM" ,"NAME" );
471 val
= asx_get_attrib("VALUE",attribs
);
472 if(m_config_get_option(mconfig
,name
) == NULL
) {
473 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Found unknown param in asx: %s",name
);
475 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"=%s\n",val
);
477 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"\n");
480 play_tree_set_param(pt
,name
,val
);
486 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
489 href
= asx_get_attrib("HREF",attribs
);
491 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
495 play_tree_add_file(pt
,href
);
497 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
504 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
508 play_tree_parser_t
* ptp
;
514 href
= asx_get_attrib("HREF",_attribs
);
516 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
519 stream
=open_stream(href
,0,&f
);
521 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
526 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
528 ptp
= play_tree_parser_new(stream
,parser
->deep
+1);
530 pt
= play_tree_parser_get_play_tree(ptp
, 1);
532 play_tree_parser_free(ptp
);
535 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
541 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
542 char *element
,*body
,**attribs
;
546 ref
= play_tree_new();
548 while(buffer
&& buffer
[0] != '\0') {
549 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
551 asx_warning_body_parse_error(parser
,"ENTRY");
553 } else if (r
== 0) { // No more element
556 if(strcasecmp(element
,"REF") == 0) {
557 asx_parse_ref(parser
,attribs
,ref
);
558 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
561 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
563 asx_free_attribs(attribs
);
567 play_tree_free(ref
,1);
576 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
577 char *element
,*body
,**attribs
;
578 play_tree_t
*repeat
, *list
=NULL
, *entry
;
582 repeat
= play_tree_new();
584 count
= asx_get_attrib("COUNT",_attribs
);
586 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
587 repeat
->loop
= -1; // Infinit
589 repeat
->loop
= atoi(count
);
591 if(repeat
->loop
== 0) repeat
->loop
= 1;
592 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
595 while(buffer
&& buffer
[0] != '\0') {
596 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
598 asx_warning_body_parse_error(parser
,"REPEAT");
600 } else if (r
== 0) { // No more element
603 if(strcasecmp(element
,"ENTRY") == 0) {
604 entry
= asx_parse_entry(parser
,body
,attribs
);
606 if(!list
) list
= entry
;
607 else play_tree_append_entry(list
,entry
);
608 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
610 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
611 entry
= asx_parse_entryref(parser
,body
,attribs
);
613 if(!list
) list
= entry
;
614 else play_tree_append_entry(list
,entry
);
615 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
617 } else if(strcasecmp(element
,"REPEAT") == 0) {
618 entry
= asx_parse_repeat(parser
,body
,attribs
);
620 if(!list
) list
= entry
;
621 else play_tree_append_entry(list
,entry
);
622 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to repeat\n",element
);
624 } else if(strcasecmp(element
,"PARAM") == 0) {
625 asx_parse_param(parser
,attribs
,repeat
);
627 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
629 asx_free_attribs(attribs
);
633 play_tree_free(repeat
,1);
636 play_tree_set_child(repeat
,list
);
645 asx_parser_build_tree(char* buffer
,int deep
) {
646 char *element
,*asx_body
,**asx_attribs
,*body
, **attribs
;
648 play_tree_t
*asx
,*entry
,*list
= NULL
;
649 ASX_Parser_t
* parser
= asx_parser_new();
654 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
656 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
657 asx_parser_free(parser
);
659 } else if(r
== 0) { // No contents
660 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
661 asx_parser_free(parser
);
665 if(strcasecmp(element
,"ASX") != 0) {
666 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
667 asx_free_attribs(asx_attribs
);
669 asx_parser_free(parser
);
674 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
675 asx_free_attribs(asx_attribs
);
676 asx_parser_free(parser
);
680 asx
= play_tree_new();
682 while(buffer
&& buffer
[0] != '\0') {
683 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
685 asx_warning_body_parse_error(parser
,"ASX");
686 asx_parser_free(parser
);
688 } else if (r
== 0) { // No more element
691 if(strcasecmp(element
,"ENTRY") == 0) {
692 entry
= asx_parse_entry(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
);
698 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
699 entry
= asx_parse_entryref(parser
,body
,attribs
);
701 if(!list
) list
= entry
;
702 else play_tree_append_entry(list
,entry
);
703 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
705 } else if(strcasecmp(element
,"REPEAT") == 0) {
706 entry
= asx_parse_repeat(parser
,body
,attribs
);
708 if(!list
) list
= entry
;
709 else play_tree_append_entry(list
,entry
);
710 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
713 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
715 asx_free_attribs(attribs
);
719 asx_free_attribs(asx_attribs
);
720 asx_parser_free(parser
);
724 play_tree_free(asx
,1);
729 play_tree_set_child(asx
,list
);