comparison asxparser.c @ 4043:25590564842f

tree-based playlist parser code by Alban Bedel <albeu@free.fr>
author arpi
date Tue, 08 Jan 2002 01:24:25 +0000
parents
children da61596bcd87
comparison
equal deleted inserted replaced
4042:d651a7b5d213 4043:25590564842f
1
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <string.h>
6
7 #include "asxparser.h"
8 #include "mp_msg.h"
9
10 ////// List utils
11
12 typedef void (*ASX_FreeFunc)(void* arg);
13
14 void
15 asx_list_add(void* list_ptr,void* entry){
16 void** list = *(void***)list_ptr;
17 int c = 0;
18
19 if(list != NULL)
20 for( ; list[c] != NULL; c++) ;
21
22 list = (void*)realloc(list,sizeof(void*)*(c+2));
23
24 list[c] = entry;
25 list[c+1] = NULL;
26
27 *(void***)list_ptr = list;
28 }
29
30
31 void
32 asx_list_remove(void* list_ptr,void* entry,ASX_FreeFunc free_func) {
33 void** list = *(void***)list_ptr;
34 int c,e = -1;
35
36 if(list == NULL) return;
37
38 for(c = 0 ; list[c] != NULL; c++){
39 if(list[c] == entry) e = c;
40 }
41
42 if(e == -1) return; // Not found
43
44 if(free_func != NULL) free_func(list[e]);
45
46 if(c == 1) { // Only one entry, we drop all
47 free(list);
48 *(void**)list_ptr = NULL;
49 return;
50 }
51
52 if(c > e) // If c==e the memmove is not needed
53 memmove(list+e,list+e+1,(c-e)*sizeof(void*));
54
55 list = (void*)realloc(list,(c-1)*sizeof(void*));
56 list[c-1] = NULL;
57
58 *(void***)list_ptr = list;
59 }
60
61 void
62 asx_list_free(void* list_ptr,ASX_FreeFunc free_func) {
63 void** ptr = *(void***)list_ptr;
64 if(ptr == NULL) return;
65 if(free_func != NULL) {
66 for( ; *ptr != NULL ; ptr++)
67 free_func(*ptr);
68 }
69 free(*(void**)list_ptr);
70 *(void**)list_ptr = NULL;
71 }
72
73 /////// Attribs utils
74
75 static char*
76 asx_get_attrib(char* attrib,char** attribs) {
77 char** ptr;
78
79 if(attrib == NULL || attribs == NULL) return NULL;
80 for(ptr = attribs; ptr[0] != NULL; ptr += 2){
81 if(strcasecmp(ptr[0],attrib) == 0)
82 return strdup(ptr[1]);
83 }
84 return NULL;
85 }
86
87 static int
88 asx_attrib_to_enum(char* val,char** valid_vals) {
89 char** ptr;
90 int r = 0;
91
92 if(valid_vals == NULL || val == NULL) return -2;
93 for(ptr = valid_vals ; ptr[0] != NULL ; ptr++) {
94 if(strcasecmp(val,ptr[0]) == 0) return r;
95 r++;
96 }
97
98 return -1;
99 }
100
101 static void
102 asx_warning_attrib_invalid(ASX_Parser_t* parser, char* elem, char* attrib,
103 char** valid_vals,char* val) {
104 char *str,*vals,**ptr;
105 int len;
106
107 if(valid_vals == NULL || valid_vals[0] == NULL) return;
108
109 len = strlen(valid_vals[0]) + 1;
110 for(ptr = valid_vals+1 ; ptr[0] != NULL; ptr++) {
111 len += strlen(ptr[0]);
112 len += ((ptr[1] == NULL) ? 4 : 2);
113 }
114 str = vals = (char*)malloc(len);
115 vals += sprintf(vals,"%s",valid_vals[0]);
116 for(ptr = valid_vals + 1 ; ptr[0] != NULL ; ptr++) {
117 if(ptr[1] == NULL)
118 vals += sprintf(vals," or %s",ptr[0]);
119 else
120 vals += sprintf(vals,", %s",ptr[0]);
121 }
122 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"at line %d : attribute %s of element %s is invalid (%s). Valid values are %s",
123 parser->line,attrib,elem,val,str);
124 free(str);
125 }
126
127 static int
128 asx_get_yes_no_attrib(ASX_Parser_t* parser, char* element, char* attrib,char** attribs,int def) {
129 char* val = asx_get_attrib(attrib,attribs);
130 char* valids[] = { "NO", "YES", NULL };
131 int r;
132
133 if(val == NULL) return def;
134 r = asx_attrib_to_enum(val,valids);
135
136 if(r < 0) {
137 asx_warning_attrib_invalid(parser,element,attrib,valids,val);
138 r = def;
139 }
140
141 free(val);
142 return r;
143 }
144
145 #define asx_free_attribs(a) asx_list_free((void***)&a,free)
146
147 #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)
148 #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)
149
150 ASX_Parser_t*
151 asx_parser_new(void) {
152 ASX_Parser_t* parser = calloc(1,sizeof(ASX_Parser_t));
153 return parser;
154 }
155
156 void
157 asx_parser_free(ASX_Parser_t* parser) {
158 if(!parser) return;
159 if(parser->ret_stack) free(parser->ret_stack);
160 free(parser);
161
162 }
163
164 #define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
165 #define SPACE " \n\t\r"
166
167 static int
168 asx_parse_attribs(ASX_Parser_t* parser,char* buffer,char*** _attribs) {
169 char *ptr1, *ptr2, *ptr3;
170 int n_attrib = 0;
171 char **attribs = NULL;
172 char *attrib, *val;
173
174 ptr1 = buffer;
175 while(1) {
176 for( ; strchr(SPACE,*ptr1) != NULL; ptr1++) { // Skip space
177 if(*ptr1 == '\0') break;
178 }
179 ptr3 = strchr(ptr1,'=');
180 if(ptr3 == NULL) break;
181 for(ptr2 = ptr3-1; strchr(SPACE,*ptr2) != NULL; ptr2--) {
182 if (ptr2 == ptr1) {
183 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : this should never append, back to attribute begin while skipping end space",parser->line);
184 break;
185 }
186 }
187 attrib = (char*)malloc(ptr2-ptr1+2);
188 strncpy(attrib,ptr1,ptr2-ptr1+1);
189 attrib[ptr2-ptr1+1] = '\0';
190
191 ptr1 = strchr(ptr3,'"');
192 if(ptr1 == NULL || ptr1[1] == '\0') {
193 mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : can't find attribute %s value",parser->line,attrib);
194 free(attrib);
195 break;
196 }
197 ptr1++;
198 ptr2 = strchr(ptr1,'"');
199 if (ptr2 == NULL) {
200 mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : value of attribute %s isn't finished",parser->line,attrib);
201 free(attrib);
202 break;
203 }
204 val = (char*)malloc(ptr2-ptr1+1);
205 strncpy(val,ptr1,ptr2-ptr1);
206 val[ptr2-ptr1] = '\0';
207 n_attrib++;
208
209 attribs = (char**)realloc(attribs,2*n_attrib*sizeof(char*)+1);
210 attribs[n_attrib*2-2] = attrib;
211 attribs[n_attrib*2-1] = val;
212
213 ptr1 = ptr2+2;
214 }
215
216 if(n_attrib > 0)
217 attribs[n_attrib*2] = NULL;
218
219 *_attribs = attribs;
220
221 return n_attrib;
222 }
223
224 /*
225 * Return -1 on error, 0 when nothing is found, 1 on sucess
226 */
227 static int
228 asx_get_element(ASX_Parser_t* parser,char** _buffer,
229 char** _element,char** _body,char*** _attribs) {
230 char *ptr1,*ptr2, *ptr3, *ptr4;
231 char *attribs = NULL;
232 char *element = NULL, *body = NULL, *ret = NULL, *buffer;
233 int n_attrib = 0;
234 int body_line = 0,attrib_line,ret_line;
235
236 if(_buffer == NULL || _element == NULL || _body == NULL || _attribs == NULL) {
237 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : asx_get_element called with invalid value",parser->line);
238 return -1;
239 }
240
241 *_body = *_element = NULL;
242 *_attribs = NULL;
243 buffer = *_buffer;
244
245 if(buffer == NULL) return 0;
246
247 if(parser->ret_stack && /*parser->last_body && */buffer != parser->last_body) {
248 ASX_LineSave_t* ls = parser->ret_stack;
249 int i;
250 for(i = 0 ; i < parser->ret_stack_size ; i++) {
251 if(buffer == ls[i].buffer) {
252 parser->line = ls[i].line;
253 break;
254 }
255
256 }
257 if( i < parser->ret_stack_size) {
258 i++;
259 if( i < parser->ret_stack_size)
260 memmove(parser->ret_stack,parser->ret_stack+i, (parser->ret_stack_size - i)*sizeof(ASX_LineSave_t));
261 parser->ret_stack_size -= i;
262 parser->ret_stack = (ASX_LineSave_t*)realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t));
263 }
264 }
265
266 ptr1 = buffer;
267 while(1) {
268 for( ; ptr1[0] != '<' ; ptr1++) {
269 if(ptr1[0] == '\0') {
270 ptr1 = NULL;
271 break;
272 }
273 if(ptr1[0] == '\n') parser->line++;
274 }
275 //ptr1 = strchr(ptr1,'<');
276 if(!ptr1 || ptr1[1] == '\0') return 0; // Nothing found
277
278 if(strncmp(ptr1,"<!--",4) == 0) { // Comments
279 for( ; strncmp(ptr1,"-->",3) != 0 ; ptr1++) {
280 if(ptr1[0] == '\0') {
281 ptr1 = NULL;
282 break;
283 }
284 if(ptr1[0] == '\n') parser->line++;
285 }
286 //ptr1 = strstr(ptr1,"-->");
287 if(!ptr1) {
288 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : unfinished comment",parser->line);
289 return -1;
290 }
291 } else {
292 break;
293 }
294 }
295
296 // Is this space skip very useful ??
297 for(ptr1++; strchr(SPACE,ptr1[0]) != NULL; ptr1++) { // Skip space
298 if(ptr1[0] == '\0') {
299 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line);
300 return -1;
301 }
302 if(ptr1[0] == '\n') parser->line++;
303 }
304
305 for(ptr2 = ptr1; strchr(LETTER,*ptr2) != NULL;ptr2++) { // Go to end of name
306 if(*ptr2 == '\0'){
307 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line);
308 return -1;
309 }
310 if(ptr2[0] == '\n') parser->line++;
311 }
312
313 element = (char*)malloc(ptr2-ptr1+1);
314 strncpy(element,ptr1,ptr2-ptr1);
315 element[ptr2-ptr1] = '\0';
316
317 for( ; strchr(SPACE,*ptr2) != NULL; ptr2++) { // Skip space
318 if(ptr2[0] == '\0') {
319 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line);
320 free(element);
321 return -1;
322 }
323 if(ptr2[0] == '\n') parser->line++;
324 }
325 attrib_line = parser->line;
326
327
328
329 for(ptr3 = ptr2; ptr3[0] != '\0'; ptr3++) { // Go to element end
330 if(ptr3[0] == '>' || strncmp(ptr3,"/>",2) == 0)
331 break;
332 if(ptr3[0] == '\n') parser->line++;
333 }
334 if(ptr3[0] == '\0' || ptr3[1] == '\0') { // End of file
335 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line);
336 free(element);
337 return -1;
338 }
339
340 // Save attribs string
341 if(ptr3-ptr2 > 0) {
342 attribs = (char*)malloc(ptr3-ptr2+1);
343 strncpy(attribs,ptr2,ptr3-ptr2);
344 attribs[ptr3-ptr2] = '\0';
345 }
346 //bs_line = parser->line;
347 if(ptr3[0] != '/') { // Not Self closed element
348 ptr3++;
349 for( ; strchr(SPACE,*ptr3) != NULL; ptr3++) { // Skip space on body begin
350 if(*ptr3 == '\0') {
351 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing %s element body",parser->line,element);
352 free(element);
353 if(attribs) free(attribs);
354 return -1;
355 }
356 if(ptr3[0] == '\n') parser->line++;
357 }
358 ptr4 = ptr3;
359 body_line = parser->line;
360 while(1) { // Find closing element
361 for( ; strncmp(ptr4,"</",2) != 0; ptr4++) {
362 if(ptr4[0] == '\0') {
363 ptr4 = NULL;
364 break;
365 }
366 if(ptr4[0] == '\n') parser->line++;
367 }
368 //ptr4 = strstr(ptr4,"</");
369 if(ptr4 == NULL || ptr4[2] == '\0') {
370 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing %s element body",parser->line,element);
371 free(element);
372 if(attribs) free(attribs);
373 return -1;
374 }
375 if(strncasecmp(element,ptr4+2,strlen(element)) == 0) { // Extract body
376 ret = ptr4+strlen(element)+3;
377 if(ptr4 != ptr3) {
378 ptr4--;
379 for( ; ptr4 != ptr3 && strchr(SPACE,*ptr4) != NULL; ptr4--) ;// Skip space on body end
380 // if(ptr4[0] == '\0') parser->line--;
381 //}
382 ptr4++;
383 body = (char*)malloc(ptr4-ptr3+1);
384 strncpy(body,ptr3,ptr4-ptr3);
385 body[ptr4-ptr3] = '\0';
386 }
387 break;
388 } else {
389 ptr4 += 2;
390 }
391 }
392 } else {
393 ret = ptr3 + 2; // 2 is for />
394 }
395
396 for( ; ret[0] != '\0' && strchr(SPACE,ret[0]) != NULL; ret++) { // Skip space
397 if(ret[0] == '\n') parser->line++;
398 }
399
400 ret_line = parser->line;
401
402 if(attribs) {
403 parser->line = attrib_line;
404 n_attrib = asx_parse_attribs(parser,attribs,_attribs);
405 free(attribs);
406 if(n_attrib < 0) {
407 mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : error while parsing element %s attributes",parser->line,element);
408 free(element);
409 free(body);
410 return -1;
411 }
412 } else
413 *_attribs = NULL;
414
415 *_element = element;
416 *_body = body;
417
418 parser->last_body = body;
419 parser->ret_stack_size++;
420 parser->ret_stack = (ASX_LineSave_t*)realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t));
421 if(parser->ret_stack_size > 1)
422 memmove(parser->ret_stack+1,parser->ret_stack,(parser->ret_stack_size-1)*sizeof(ASX_LineSave_t));
423 parser->ret_stack[0].buffer = ret;
424 parser->ret_stack[0].line = ret_line;
425 parser->line = body ? body_line : ret_line;
426
427 *_buffer = ret;
428 return 1;
429
430 }
431
432 static void
433 asx_parse_ref(ASX_Parser_t* parser, char** attribs, play_tree_t* pt) {
434 char *href;
435
436 href = asx_get_attrib("HREF",attribs);
437 if(href == NULL) {
438 asx_warning_attrib_required(parser,"ENTRYREF" ,"HREF" );
439 return;
440 }
441
442 play_tree_add_file(pt,href);
443
444 mp_msg(MSGT_PLAYTREE,MSGL_V,"Adding file %s to element entry\n",href);
445
446 free(href);
447
448 }
449
450 static play_tree_t*
451 asx_parse_entryref(ASX_Parser_t* parser,char* buffer,char** _attribs) {
452 mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n");
453 return NULL;
454 }
455
456 static play_tree_t*
457 asx_parse_entry(ASX_Parser_t* parser,char* buffer,char** _attribs) {
458 char *element,*body,**attribs;
459 int r,nref=0;
460 play_tree_t *ref;
461
462 ref = play_tree_new();
463
464 while(buffer && buffer[0] != '\0') {
465 r = asx_get_element(parser,&buffer,&element,&body,&attribs);
466 if(r < 0) {
467 asx_warning_body_parse_error(parser,"ENTRY");
468 return NULL;
469 } else if (r == 0) { // No more element
470 break;
471 }
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);
475 nref++;
476 } else
477 mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element);
478 if(body) free(body);
479 asx_free_attribs(attribs);
480 }
481
482 if(nref <= 0) {
483 play_tree_free(ref,1);
484 return NULL;
485 }
486 return ref;
487
488 }
489
490
491 static play_tree_t*
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;
495 char* count;
496 int r;
497
498 repeat = play_tree_new();
499
500 count = asx_get_attrib("COUNT",_attribs);
501 if(count == NULL) {
502 mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Setting element repeat loop to infinit\n");
503 repeat->loop = -1; // Infinit
504 } else {
505 repeat->loop = atoi(count);
506 free(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);
509 }
510
511 while(buffer && buffer[0] != '\0') {
512 r = asx_get_element(parser,&buffer,&element,&body,&attribs);
513 if(r < 0) {
514 asx_warning_body_parse_error(parser,"REPEAT");
515 return NULL;
516 } else if (r == 0) { // No more element
517 break;
518 }
519 if(strcasecmp(element,"ENTRY") == 0) {
520 entry = asx_parse_entry(parser,body,attribs);
521 if(entry) {
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);
525 }
526 } else if(strcasecmp(element,"ENTRYREF") == 0) {
527 entry = asx_parse_entryref(parser,body,attribs);
528 if(entry) {
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);
532 }
533 } else if(strcasecmp(element,"REPEAT") == 0) {
534 entry = asx_parse_repeat(parser,body,attribs);
535 if(entry) {
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);
539 }
540 } else
541 mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element);
542 if(body) free(body);
543 asx_free_attribs(attribs);
544 }
545
546 if(!list) {
547 play_tree_free(repeat,1);
548 return NULL;
549 }
550 play_tree_set_child(repeat,list);
551
552 return repeat;
553
554 }
555
556
557
558 play_tree_t*
559 asx_parser_build_tree(char* buffer) {
560 char *element,*asx_body,**asx_attribs,*body, **attribs;
561 int r;
562 play_tree_t *asx,*entry,*list = NULL;
563 ASX_Parser_t* parser = asx_parser_new();
564
565 parser->line = 1;
566
567 r = asx_get_element(parser,&buffer,&element,&asx_body,&asx_attribs);
568 if(r < 0) {
569 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : Syntax error ???",parser->line);
570 asx_parser_free(parser);
571 return NULL;
572 } else if(r == 0) { // No contents
573 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"empty asx element");
574 asx_parser_free(parser);
575 return NULL;
576 }
577
578 if(strcasecmp(element,"ASX") != 0) {
579 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"first element isn't ASX, it's %s\n",element);
580 asx_free_attribs(asx_attribs);
581 if(body) free(body);
582 asx_parser_free(parser);
583 return NULL;
584 }
585
586 if(!asx_body) {
587 mp_msg(MSGT_PLAYTREE,MSGL_ERR,"ASX element is empty");
588 asx_free_attribs(asx_attribs);
589 asx_parser_free(parser);
590 return NULL;
591 }
592
593 asx = play_tree_new();
594 buffer = asx_body;
595 while(buffer && buffer[0] != '\0') {
596 r = asx_get_element(parser,&buffer,&element,&body,&attribs);
597 if(r < 0) {
598 asx_warning_body_parse_error(parser,"ASX");
599 asx_parser_free(parser);
600 return NULL;
601 } else if (r == 0) { // No more element
602 break;
603 }
604 if(strcasecmp(element,"ENTRY") == 0) {
605 entry = asx_parse_entry(parser,body,attribs);
606 if(entry) {
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);
610 }
611 } else if(strcasecmp(element,"ENTRYREF") == 0) {
612 entry = asx_parse_entryref(parser,body,attribs);
613 if(entry) {
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);
617 }
618 } else if(strcasecmp(element,"REPEAT") == 0) {
619 entry = asx_parse_repeat(parser,body,attribs);
620 if(entry) {
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);
624 }
625 } else
626 mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element);
627 if(body) free(body);
628 asx_free_attribs(attribs);
629 }
630
631 free(asx_body);
632 asx_free_attribs(asx_attribs);
633 asx_parser_free(parser);
634
635
636 if(!list) {
637 play_tree_free(asx,1);
638
639 return NULL;
640 }
641
642 play_tree_set_child(asx,list);
643
644 return asx;
645 }