10 #include "playtreeparser.h"
11 #include "libmpdemux/stream.h"
12 #include "libmpdemux/demuxer.h"
13 #include "asxparser.h"
17 extern m_config_t
* mconfig
;
22 asx_list_add(void* list_ptr
,void* entry
){
23 void** list
= *(void***)list_ptr
;
27 for( ; list
[c
] != NULL
; c
++) ;
29 list
= (void*)realloc(list
,sizeof(void*)*(c
+2));
34 *(void***)list_ptr
= list
;
39 asx_list_remove(void* list_ptr
,void* entry
,ASX_FreeFunc free_func
) {
40 void** list
= *(void***)list_ptr
;
43 if(list
== NULL
) return;
45 for(c
= 0 ; list
[c
] != NULL
; c
++){
46 if(list
[c
] == entry
) e
= c
;
49 if(e
== -1) return; // Not found
51 if(free_func
!= NULL
) free_func(list
[e
]);
53 if(c
== 1) { // Only one entry, we drop all
55 *(void**)list_ptr
= NULL
;
59 if(c
> e
) // If c==e the memmove is not needed
60 memmove(list
+e
,list
+e
+1,(c
-e
)*sizeof(void*));
62 list
= (void*)realloc(list
,(c
-1)*sizeof(void*));
65 *(void***)list_ptr
= list
;
69 asx_list_free(void* list_ptr
,ASX_FreeFunc free_func
) {
70 void** ptr
= *(void***)list_ptr
;
71 if(ptr
== NULL
) return;
72 if(free_func
!= NULL
) {
73 for( ; *ptr
!= NULL
; ptr
++)
76 free(*(void**)list_ptr
);
77 *(void**)list_ptr
= NULL
;
83 asx_get_attrib(char* attrib
,char** attribs
) {
86 if(attrib
== NULL
|| attribs
== NULL
) return NULL
;
87 for(ptr
= attribs
; ptr
[0] != NULL
; ptr
+= 2){
88 if(strcasecmp(ptr
[0],attrib
) == 0)
89 return strdup(ptr
[1]);
95 asx_attrib_to_enum(char* val
,char** valid_vals
) {
99 if(valid_vals
== NULL
|| val
== NULL
) return -2;
100 for(ptr
= valid_vals
; ptr
[0] != NULL
; ptr
++) {
101 if(strcasecmp(val
,ptr
[0]) == 0) return r
;
109 asx_warning_attrib_invalid(ASX_Parser_t
* parser
, char* elem
, char* attrib
,
110 char** valid_vals
,char* val
) {
111 char *str
,*vals
,**ptr
;
114 if(valid_vals
== NULL
|| valid_vals
[0] == NULL
) return;
116 len
= strlen(valid_vals
[0]) + 1;
117 for(ptr
= valid_vals
+1 ; ptr
[0] != NULL
; ptr
++) {
118 len
+= strlen(ptr
[0]);
119 len
+= ((ptr
[1] == NULL
) ? 4 : 2);
121 str
= vals
= (char*)malloc(len
);
122 vals
+= sprintf(vals
,"%s",valid_vals
[0]);
123 for(ptr
= valid_vals
+ 1 ; ptr
[0] != NULL
; ptr
++) {
125 vals
+= sprintf(vals
," or %s",ptr
[0]);
127 vals
+= sprintf(vals
,", %s",ptr
[0]);
129 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"at line %d : attribute %s of element %s is invalid (%s). Valid values are %s",
130 parser
->line
,attrib
,elem
,val
,str
);
135 asx_get_yes_no_attrib(ASX_Parser_t
* parser
, char* element
, char* attrib
,char** attribs
,int def
) {
136 char* val
= asx_get_attrib(attrib
,attribs
);
137 char* valids
[] = { "NO", "YES", NULL
};
140 if(val
== NULL
) return def
;
141 r
= asx_attrib_to_enum(val
,valids
);
144 asx_warning_attrib_invalid(parser
,element
,attrib
,valids
,val
);
152 #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)
153 #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)
156 asx_parser_new(void) {
157 ASX_Parser_t
* parser
= calloc(1,sizeof(ASX_Parser_t
));
162 asx_parser_free(ASX_Parser_t
* parser
) {
164 if(parser
->ret_stack
) free(parser
->ret_stack
);
169 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
170 #define SPACE " \n\t\r"
173 asx_parse_attribs(ASX_Parser_t
* parser
,char* buffer
,char*** _attribs
) {
174 char *ptr1
, *ptr2
, *ptr3
;
176 char **attribs
= NULL
;
181 for( ; strchr(SPACE
,*ptr1
) != NULL
; ptr1
++) { // Skip space
182 if(*ptr1
== '\0') break;
184 ptr3
= strchr(ptr1
,'=');
185 if(ptr3
== NULL
) break;
186 for(ptr2
= ptr3
-1; strchr(SPACE
,*ptr2
) != NULL
; ptr2
--) {
188 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : this should never append, back to attribute begin while skipping end space",parser
->line
);
192 attrib
= (char*)malloc(ptr2
-ptr1
+2);
193 strncpy(attrib
,ptr1
,ptr2
-ptr1
+1);
194 attrib
[ptr2
-ptr1
+1] = '\0';
196 ptr1
= strchr(ptr3
,'"');
197 if(ptr1
== NULL
|| ptr1
[1] == '\0') ptr1
= strchr(ptr3
,'\'');
198 if(ptr1
== NULL
|| ptr1
[1] == '\0') {
199 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : can't find attribute %s value",parser
->line
,attrib
);
203 ptr2
= strchr(ptr1
+1,ptr1
[0]);
205 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : value of attribute %s isn't finished",parser
->line
,attrib
);
210 val
= (char*)malloc(ptr2
-ptr1
+1);
211 strncpy(val
,ptr1
,ptr2
-ptr1
);
212 val
[ptr2
-ptr1
] = '\0';
215 attribs
= (char**)realloc(attribs
,(2*n_attrib
+1)*sizeof(char*));
216 attribs
[n_attrib
*2-2] = attrib
;
217 attribs
[n_attrib
*2-1] = val
;
223 attribs
[n_attrib
*2] = NULL
;
231 * Return -1 on error, 0 when nothing is found, 1 on sucess
234 asx_get_element(ASX_Parser_t
* parser
,char** _buffer
,
235 char** _element
,char** _body
,char*** _attribs
) {
236 char *ptr1
,*ptr2
, *ptr3
, *ptr4
;
237 char *attribs
= NULL
;
238 char *element
= NULL
, *body
= NULL
, *ret
= NULL
, *buffer
;
240 int body_line
= 0,attrib_line
,ret_line
,in
= 0;
243 if(_buffer
== NULL
|| _element
== NULL
|| _body
== NULL
|| _attribs
== NULL
) {
244 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : asx_get_element called with invalid value",parser
->line
);
248 *_body
= *_element
= NULL
;
252 if(buffer
== NULL
) return 0;
254 if(parser
->ret_stack
&& /*parser->last_body && */buffer
!= parser
->last_body
) {
255 ASX_LineSave_t
* ls
= parser
->ret_stack
;
257 for(i
= 0 ; i
< parser
->ret_stack_size
; i
++) {
258 if(buffer
== ls
[i
].buffer
) {
259 parser
->line
= ls
[i
].line
;
264 if( i
< parser
->ret_stack_size
) {
266 if( i
< parser
->ret_stack_size
)
267 memmove(parser
->ret_stack
,parser
->ret_stack
+i
, (parser
->ret_stack_size
- i
)*sizeof(ASX_LineSave_t
));
268 parser
->ret_stack_size
-= i
;
269 if(parser
->ret_stack_size
> 0)
270 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
272 free(parser
->ret_stack
);
273 parser
->ret_stack
= NULL
;
280 for( ; ptr1
[0] != '<' ; ptr1
++) {
281 if(ptr1
[0] == '\0') {
285 if(ptr1
[0] == '\n') parser
->line
++;
287 //ptr1 = strchr(ptr1,'<');
288 if(!ptr1
|| ptr1
[1] == '\0') return 0; // Nothing found
290 if(strncmp(ptr1
,"<!--",4) == 0) { // Comments
291 for( ; strncmp(ptr1
,"-->",3) != 0 ; ptr1
++) {
292 if(ptr1
[0] == '\0') {
296 if(ptr1
[0] == '\n') parser
->line
++;
298 //ptr1 = strstr(ptr1,"-->");
300 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : unfinished comment",parser
->line
);
308 // Is this space skip very useful ??
309 for(ptr1
++; strchr(SPACE
,ptr1
[0]) != NULL
; ptr1
++) { // Skip space
310 if(ptr1
[0] == '\0') {
311 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
314 if(ptr1
[0] == '\n') parser
->line
++;
317 for(ptr2
= ptr1
; strchr(LETTER
,*ptr2
) != NULL
;ptr2
++) { // Go to end of name
319 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
322 if(ptr2
[0] == '\n') parser
->line
++;
325 element
= (char*)malloc(ptr2
-ptr1
+1);
326 strncpy(element
,ptr1
,ptr2
-ptr1
);
327 element
[ptr2
-ptr1
] = '\0';
329 for( ; strchr(SPACE
,*ptr2
) != NULL
; ptr2
++) { // Skip space
330 if(ptr2
[0] == '\0') {
331 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
335 if(ptr2
[0] == '\n') parser
->line
++;
337 attrib_line
= parser
->line
;
341 for(ptr3
= ptr2
; ptr3
[0] != '\0'; ptr3
++) { // Go to element end
342 if(ptr3
[0] == '"') quotes
^= 1;
343 if(!quotes
&& (ptr3
[0] == '>' || strncmp(ptr3
,"/>",2) == 0))
345 if(ptr3
[0] == '\n') parser
->line
++;
347 if(ptr3
[0] == '\0' || ptr3
[1] == '\0') { // End of file
348 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing element start",parser
->line
);
353 // Save attribs string
355 attribs
= (char*)malloc(ptr3
-ptr2
+1);
356 strncpy(attribs
,ptr2
,ptr3
-ptr2
);
357 attribs
[ptr3
-ptr2
] = '\0';
359 //bs_line = parser->line;
360 if(ptr3
[0] != '/') { // Not Self closed element
362 for( ; strchr(SPACE
,*ptr3
) != NULL
; ptr3
++) { // Skip space on body begin
364 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
366 if(attribs
) free(attribs
);
369 if(ptr3
[0] == '\n') parser
->line
++;
372 body_line
= parser
->line
;
373 while(1) { // Find closing element
374 for( ; ptr4
[0] != '<' ; ptr4
++) {
375 if(ptr4
[0] == '\0') {
379 if(ptr4
[0] == '\n') parser
->line
++;
381 if(ptr4
&& strncmp(ptr4
,"<!--",4) == 0) { // Comments
382 for( ; strncmp(ptr4
,"-->",3) != 0 ; ptr4
++) {
383 if(ptr4
[0] == '\0') {
387 if(ptr1
[0] == '\n') parser
->line
++;
391 if(ptr4
== NULL
|| ptr4
[1] == '\0') {
392 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : EOB reached while parsing %s element body",parser
->line
,element
);
394 if(attribs
) free(attribs
);
397 if(ptr4
[1] != '/' && strncasecmp(element
,ptr4
+1,strlen(element
)) == 0) {
401 } else if(strncasecmp(element
,ptr4
+2,strlen(element
)) == 0) { // Extract body
404 ptr4
+= 2+strlen(element
);
407 ret
= ptr4
+strlen(element
)+3;
410 for( ; ptr4
!= ptr3
&& strchr(SPACE
,*ptr4
) != NULL
; ptr4
--) ;// Skip space on body end
411 // if(ptr4[0] == '\0') parser->line--;
414 body
= (char*)malloc(ptr4
-ptr3
+1);
415 strncpy(body
,ptr3
,ptr4
-ptr3
);
416 body
[ptr4
-ptr3
] = '\0';
424 ret
= ptr3
+ 2; // 2 is for />
427 for( ; ret
[0] != '\0' && strchr(SPACE
,ret
[0]) != NULL
; ret
++) { // Skip space
428 if(ret
[0] == '\n') parser
->line
++;
431 ret_line
= parser
->line
;
434 parser
->line
= attrib_line
;
435 n_attrib
= asx_parse_attribs(parser
,attribs
,_attribs
);
438 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"At line %d : error while parsing element %s attributes",parser
->line
,element
);
449 parser
->last_body
= body
;
450 parser
->ret_stack_size
++;
451 parser
->ret_stack
= (ASX_LineSave_t
*)realloc(parser
->ret_stack
,parser
->ret_stack_size
*sizeof(ASX_LineSave_t
));
452 if(parser
->ret_stack_size
> 1)
453 memmove(parser
->ret_stack
+1,parser
->ret_stack
,(parser
->ret_stack_size
-1)*sizeof(ASX_LineSave_t
));
454 parser
->ret_stack
[0].buffer
= ret
;
455 parser
->ret_stack
[0].line
= ret_line
;
456 parser
->line
= body
? body_line
: ret_line
;
464 asx_parse_param(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
467 name
= asx_get_attrib("NAME",attribs
);
469 asx_warning_attrib_required(parser
,"PARAM" ,"NAME" );
472 val
= asx_get_attrib("VALUE",attribs
);
473 if(m_config_get_option(mconfig
,name
) == NULL
) {
474 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Found unknown param in asx: %s",name
);
476 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"=%s\n",val
);
478 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"\n");
481 play_tree_set_param(pt
,name
,val
);
487 asx_parse_ref(ASX_Parser_t
* parser
, char** attribs
, play_tree_t
* pt
) {
490 href
= asx_get_attrib("HREF",attribs
);
492 asx_warning_attrib_required(parser
,"REF" ,"HREF" );
496 play_tree_add_file(pt
,href
);
498 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding file %s to element entry\n",href
);
505 asx_parse_entryref(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
509 play_tree_parser_t
* ptp
;
510 int f
=DEMUXER_TYPE_UNKNOWN
;
515 href
= asx_get_attrib("HREF",_attribs
);
517 asx_warning_attrib_required(parser
,"ENTRYREF" ,"HREF" );
520 stream
=open_stream(href
,0,&f
);
522 mp_msg(MSGT_PLAYTREE
,MSGL_WARN
,"Can't open playlist %s\n",href
);
527 mp_msg(MSGT_PLAYTREE
,MSGL_V
,"Adding playlist %s to element entryref\n",href
);
529 ptp
= play_tree_parser_new(stream
,parser
->deep
+1);
531 pt
= play_tree_parser_get_play_tree(ptp
, 1);
533 play_tree_parser_free(ptp
);
536 //mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
542 asx_parse_entry(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
543 char *element
,*body
,**attribs
;
547 ref
= play_tree_new();
549 while(buffer
&& buffer
[0] != '\0') {
550 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
552 asx_warning_body_parse_error(parser
,"ENTRY");
554 } else if (r
== 0) { // No more element
557 if(strcasecmp(element
,"REF") == 0) {
558 asx_parse_ref(parser
,attribs
,ref
);
559 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to entry\n",element
);
562 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
564 asx_free_attribs(attribs
);
568 play_tree_free(ref
,1);
577 asx_parse_repeat(ASX_Parser_t
* parser
,char* buffer
,char** _attribs
) {
578 char *element
,*body
,**attribs
;
579 play_tree_t
*repeat
, *list
=NULL
, *entry
;
583 repeat
= play_tree_new();
585 count
= asx_get_attrib("COUNT",_attribs
);
587 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to infinit\n");
588 repeat
->loop
= -1; // Infinit
590 repeat
->loop
= atoi(count
);
592 if(repeat
->loop
== 0) repeat
->loop
= 1;
593 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Setting element repeat loop to %d\n",repeat
->loop
);
596 while(buffer
&& buffer
[0] != '\0') {
597 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
599 asx_warning_body_parse_error(parser
,"REPEAT");
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 repeat\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 repeat\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 repeat\n",element
);
625 } else if(strcasecmp(element
,"PARAM") == 0) {
626 asx_parse_param(parser
,attribs
,repeat
);
628 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
630 asx_free_attribs(attribs
);
634 play_tree_free(repeat
,1);
637 play_tree_set_child(repeat
,list
);
646 asx_parser_build_tree(char* buffer
,int deep
) {
647 char *element
,*asx_body
,**asx_attribs
,*body
, **attribs
;
649 play_tree_t
*asx
,*entry
,*list
= NULL
;
650 ASX_Parser_t
* parser
= asx_parser_new();
655 r
= asx_get_element(parser
,&buffer
,&element
,&asx_body
,&asx_attribs
);
657 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"At line %d : Syntax error ???",parser
->line
);
658 asx_parser_free(parser
);
660 } else if(r
== 0) { // No contents
661 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"empty asx element");
662 asx_parser_free(parser
);
666 if(strcasecmp(element
,"ASX") != 0) {
667 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"first element isn't ASX, it's %s\n",element
);
668 asx_free_attribs(asx_attribs
);
670 asx_parser_free(parser
);
675 mp_msg(MSGT_PLAYTREE
,MSGL_ERR
,"ASX element is empty");
676 asx_free_attribs(asx_attribs
);
677 asx_parser_free(parser
);
681 asx
= play_tree_new();
683 while(buffer
&& buffer
[0] != '\0') {
684 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
686 asx_warning_body_parse_error(parser
,"ASX");
687 asx_parser_free(parser
);
689 } else if (r
== 0) { // No more element
692 if(strcasecmp(element
,"ENTRY") == 0) {
693 entry
= asx_parse_entry(parser
,body
,attribs
);
695 if(!list
) list
= entry
;
696 else play_tree_append_entry(list
,entry
);
697 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
699 } else if(strcasecmp(element
,"ENTRYREF") == 0) {
700 entry
= asx_parse_entryref(parser
,body
,attribs
);
702 if(!list
) list
= entry
;
703 else play_tree_append_entry(list
,entry
);
704 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
706 } else if(strcasecmp(element
,"REPEAT") == 0) {
707 entry
= asx_parse_repeat(parser
,body
,attribs
);
709 if(!list
) list
= entry
;
710 else play_tree_append_entry(list
,entry
);
711 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Adding element %s to asx\n",element
);
714 mp_msg(MSGT_PLAYTREE
,MSGL_DBG2
,"Ignoring element %s\n",element
);
716 asx_free_attribs(attribs
);
720 asx_free_attribs(asx_attribs
);
721 asx_parser_free(parser
);
725 play_tree_free(asx
,1);
730 play_tree_set_child(asx
,list
);