From 10d82182e0840b71675d0d7ebc742081446568f7 Mon Sep 17 00:00:00 2001 From: arpi Date: Tue, 8 Jan 2002 01:24:25 +0000 Subject: tree-based playlist parser code by Alban Bedel git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@4044 b3059339-0415-0410-9bf9-f77b7e298cf2 --- Makefile | 2 +- asxparser.c | 645 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ asxparser.h | 17 ++ playtree.c | 631 +++++++++++++++++++++++++++++++++++++++++++++++++++++ playtree.h | 144 +++++++++++++ playtreeparser.c | 298 +++++++++++++++++++++++++ 6 files changed, 1736 insertions(+), 1 deletion(-) create mode 100644 asxparser.c create mode 100644 asxparser.h create mode 100644 playtree.c create mode 100644 playtree.h create mode 100644 playtreeparser.c diff --git a/Makefile b/Makefile index 29ee450f71..df5af81596 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ INSTALL = install SRCS_COMMON = cyuv.c adpcm.c xacodec.c cpudetect.c mp_msg.c ac3-iec958.c dec_audio.c dec_video.c msvidc.c cinepak.c fli.c qtrle.c codec-cfg.c cfgparser.c my_profile.c RTjpegN.c minilzo.c nuppelvideo.c SRCS_MENCODER = mencoder.c $(SRCS_COMMON) libao2/afmt.c divx4_vbr.c libvo/aclib.c libvo/img_format.c -SRCS_MPLAYER = mplayer.c $(SRCS_COMMON) find_sub.c subreader.c lirc_mp.c mixer.c spudec.c +SRCS_MPLAYER = mplayer.c $(SRCS_COMMON) find_sub.c subreader.c lirc_mp.c mixer.c spudec.c playtree.c playtreeparser.c asxparser.c OBJS_MENCODER = $(SRCS_MENCODER:.c=.o) OBJS_MPLAYER = $(SRCS_MPLAYER:.c=.o) diff --git a/asxparser.c b/asxparser.c new file mode 100644 index 0000000000..f02a66f46b --- /dev/null +++ b/asxparser.c @@ -0,0 +1,645 @@ + +#include +#include +#include +#include + +#include "asxparser.h" +#include "mp_msg.h" + +////// List utils + +typedef void (*ASX_FreeFunc)(void* arg); + +void +asx_list_add(void* list_ptr,void* entry){ + void** list = *(void***)list_ptr; + int c = 0; + + if(list != NULL) + for( ; list[c] != NULL; c++) ; + + list = (void*)realloc(list,sizeof(void*)*(c+2)); + + list[c] = entry; + list[c+1] = NULL; + + *(void***)list_ptr = list; +} + + +void +asx_list_remove(void* list_ptr,void* entry,ASX_FreeFunc free_func) { + void** list = *(void***)list_ptr; + int c,e = -1; + + if(list == NULL) return; + + for(c = 0 ; list[c] != NULL; c++){ + if(list[c] == entry) e = c; + } + + if(e == -1) return; // Not found + + if(free_func != NULL) free_func(list[e]); + + if(c == 1) { // Only one entry, we drop all + free(list); + *(void**)list_ptr = NULL; + return; + } + + if(c > e) // If c==e the memmove is not needed + memmove(list+e,list+e+1,(c-e)*sizeof(void*)); + + list = (void*)realloc(list,(c-1)*sizeof(void*)); + list[c-1] = NULL; + + *(void***)list_ptr = list; +} + +void +asx_list_free(void* list_ptr,ASX_FreeFunc free_func) { + void** ptr = *(void***)list_ptr; + if(ptr == NULL) return; + if(free_func != NULL) { + for( ; *ptr != NULL ; ptr++) + free_func(*ptr); + } + free(*(void**)list_ptr); + *(void**)list_ptr = NULL; +} + +/////// Attribs utils + +static char* +asx_get_attrib(char* attrib,char** attribs) { + char** ptr; + + if(attrib == NULL || attribs == NULL) return NULL; + for(ptr = attribs; ptr[0] != NULL; ptr += 2){ + if(strcasecmp(ptr[0],attrib) == 0) + return strdup(ptr[1]); + } + return NULL; +} + +static int +asx_attrib_to_enum(char* val,char** valid_vals) { + char** ptr; + int r = 0; + + if(valid_vals == NULL || val == NULL) return -2; + for(ptr = valid_vals ; ptr[0] != NULL ; ptr++) { + if(strcasecmp(val,ptr[0]) == 0) return r; + r++; + } + + return -1; +} + +static void +asx_warning_attrib_invalid(ASX_Parser_t* parser, char* elem, char* attrib, + char** valid_vals,char* val) { + char *str,*vals,**ptr; + int len; + + if(valid_vals == NULL || valid_vals[0] == NULL) return; + + len = strlen(valid_vals[0]) + 1; + for(ptr = valid_vals+1 ; ptr[0] != NULL; ptr++) { + len += strlen(ptr[0]); + len += ((ptr[1] == NULL) ? 4 : 2); + } + str = vals = (char*)malloc(len); + vals += sprintf(vals,"%s",valid_vals[0]); + for(ptr = valid_vals + 1 ; ptr[0] != NULL ; ptr++) { + if(ptr[1] == NULL) + vals += sprintf(vals," or %s",ptr[0]); + else + vals += sprintf(vals,", %s",ptr[0]); + } + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"at line %d : attribute %s of element %s is invalid (%s). Valid values are %s", + parser->line,attrib,elem,val,str); + free(str); +} + +static int +asx_get_yes_no_attrib(ASX_Parser_t* parser, char* element, char* attrib,char** attribs,int def) { + char* val = asx_get_attrib(attrib,attribs); + char* valids[] = { "NO", "YES", NULL }; + int r; + + if(val == NULL) return def; + r = asx_attrib_to_enum(val,valids); + + if(r < 0) { + asx_warning_attrib_invalid(parser,element,attrib,valids,val); + r = def; + } + + free(val); + return r; +} + +#define asx_free_attribs(a) asx_list_free((void***)&a,free) + +#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) +#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) + +ASX_Parser_t* +asx_parser_new(void) { + ASX_Parser_t* parser = calloc(1,sizeof(ASX_Parser_t)); + return parser; +} + +void +asx_parser_free(ASX_Parser_t* parser) { + if(!parser) return; + if(parser->ret_stack) free(parser->ret_stack); + free(parser); + +} + +#define LETTER "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define SPACE " \n\t\r" + +static int +asx_parse_attribs(ASX_Parser_t* parser,char* buffer,char*** _attribs) { + char *ptr1, *ptr2, *ptr3; + int n_attrib = 0; + char **attribs = NULL; + char *attrib, *val; + + ptr1 = buffer; + while(1) { + for( ; strchr(SPACE,*ptr1) != NULL; ptr1++) { // Skip space + if(*ptr1 == '\0') break; + } + ptr3 = strchr(ptr1,'='); + if(ptr3 == NULL) break; + for(ptr2 = ptr3-1; strchr(SPACE,*ptr2) != NULL; ptr2--) { + if (ptr2 == ptr1) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : this should never append, back to attribute begin while skipping end space",parser->line); + break; + } + } + attrib = (char*)malloc(ptr2-ptr1+2); + strncpy(attrib,ptr1,ptr2-ptr1+1); + attrib[ptr2-ptr1+1] = '\0'; + + ptr1 = strchr(ptr3,'"'); + if(ptr1 == NULL || ptr1[1] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : can't find attribute %s value",parser->line,attrib); + free(attrib); + break; + } + ptr1++; + ptr2 = strchr(ptr1,'"'); + if (ptr2 == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : value of attribute %s isn't finished",parser->line,attrib); + free(attrib); + break; + } + val = (char*)malloc(ptr2-ptr1+1); + strncpy(val,ptr1,ptr2-ptr1); + val[ptr2-ptr1] = '\0'; + n_attrib++; + + attribs = (char**)realloc(attribs,2*n_attrib*sizeof(char*)+1); + attribs[n_attrib*2-2] = attrib; + attribs[n_attrib*2-1] = val; + + ptr1 = ptr2+2; + } + + if(n_attrib > 0) + attribs[n_attrib*2] = NULL; + + *_attribs = attribs; + + return n_attrib; +} + +/* + * Return -1 on error, 0 when nothing is found, 1 on sucess + */ +static int +asx_get_element(ASX_Parser_t* parser,char** _buffer, + char** _element,char** _body,char*** _attribs) { + char *ptr1,*ptr2, *ptr3, *ptr4; + char *attribs = NULL; + char *element = NULL, *body = NULL, *ret = NULL, *buffer; + int n_attrib = 0; + int body_line = 0,attrib_line,ret_line; + + if(_buffer == NULL || _element == NULL || _body == NULL || _attribs == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : asx_get_element called with invalid value",parser->line); + return -1; + } + + *_body = *_element = NULL; + *_attribs = NULL; + buffer = *_buffer; + + if(buffer == NULL) return 0; + + if(parser->ret_stack && /*parser->last_body && */buffer != parser->last_body) { + ASX_LineSave_t* ls = parser->ret_stack; + int i; + for(i = 0 ; i < parser->ret_stack_size ; i++) { + if(buffer == ls[i].buffer) { + parser->line = ls[i].line; + break; + } + + } + if( i < parser->ret_stack_size) { + i++; + if( i < parser->ret_stack_size) + memmove(parser->ret_stack,parser->ret_stack+i, (parser->ret_stack_size - i)*sizeof(ASX_LineSave_t)); + parser->ret_stack_size -= i; + parser->ret_stack = (ASX_LineSave_t*)realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t)); + } + } + + ptr1 = buffer; + while(1) { + for( ; ptr1[0] != '<' ; ptr1++) { + if(ptr1[0] == '\0') { + ptr1 = NULL; + break; + } + if(ptr1[0] == '\n') parser->line++; + } + //ptr1 = strchr(ptr1,'<'); + if(!ptr1 || ptr1[1] == '\0') return 0; // Nothing found + + if(strncmp(ptr1,"",3) != 0 ; ptr1++) { + if(ptr1[0] == '\0') { + ptr1 = NULL; + break; + } + if(ptr1[0] == '\n') parser->line++; + } + //ptr1 = strstr(ptr1,"-->"); + if(!ptr1) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : unfinished comment",parser->line); + return -1; + } + } else { + break; + } + } + + // Is this space skip very useful ?? + for(ptr1++; strchr(SPACE,ptr1[0]) != NULL; ptr1++) { // Skip space + if(ptr1[0] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + return -1; + } + if(ptr1[0] == '\n') parser->line++; + } + + for(ptr2 = ptr1; strchr(LETTER,*ptr2) != NULL;ptr2++) { // Go to end of name + if(*ptr2 == '\0'){ + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + return -1; + } + if(ptr2[0] == '\n') parser->line++; + } + + element = (char*)malloc(ptr2-ptr1+1); + strncpy(element,ptr1,ptr2-ptr1); + element[ptr2-ptr1] = '\0'; + + for( ; strchr(SPACE,*ptr2) != NULL; ptr2++) { // Skip space + if(ptr2[0] == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + free(element); + return -1; + } + if(ptr2[0] == '\n') parser->line++; + } + attrib_line = parser->line; + + + + for(ptr3 = ptr2; ptr3[0] != '\0'; ptr3++) { // Go to element end + if(ptr3[0] == '>' || strncmp(ptr3,"/>",2) == 0) + break; + if(ptr3[0] == '\n') parser->line++; + } + if(ptr3[0] == '\0' || ptr3[1] == '\0') { // End of file + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing element start",parser->line); + free(element); + return -1; + } + + // Save attribs string + if(ptr3-ptr2 > 0) { + attribs = (char*)malloc(ptr3-ptr2+1); + strncpy(attribs,ptr2,ptr3-ptr2); + attribs[ptr3-ptr2] = '\0'; + } + //bs_line = parser->line; + if(ptr3[0] != '/') { // Not Self closed element + ptr3++; + for( ; strchr(SPACE,*ptr3) != NULL; ptr3++) { // Skip space on body begin + if(*ptr3 == '\0') { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : EOB reached while parsing %s element body",parser->line,element); + free(element); + if(attribs) free(attribs); + return -1; + } + if(ptr3[0] == '\n') parser->line++; + } + ptr4 = ptr3; + body_line = parser->line; + while(1) { // Find closing element + for( ; strncmp(ptr4,"line++; + } + //ptr4 = strstr(ptr4,"line,element); + free(element); + if(attribs) free(attribs); + return -1; + } + if(strncasecmp(element,ptr4+2,strlen(element)) == 0) { // Extract body + ret = ptr4+strlen(element)+3; + if(ptr4 != ptr3) { + ptr4--; + for( ; ptr4 != ptr3 && strchr(SPACE,*ptr4) != NULL; ptr4--) ;// Skip space on body end + // if(ptr4[0] == '\0') parser->line--; + //} + ptr4++; + body = (char*)malloc(ptr4-ptr3+1); + strncpy(body,ptr3,ptr4-ptr3); + body[ptr4-ptr3] = '\0'; + } + break; + } else { + ptr4 += 2; + } + } + } else { + ret = ptr3 + 2; // 2 is for /> + } + + for( ; ret[0] != '\0' && strchr(SPACE,ret[0]) != NULL; ret++) { // Skip space + if(ret[0] == '\n') parser->line++; + } + + ret_line = parser->line; + + if(attribs) { + parser->line = attrib_line; + n_attrib = asx_parse_attribs(parser,attribs,_attribs); + free(attribs); + if(n_attrib < 0) { + mp_msg(MSGT_PLAYTREE,MSGL_WARN,"At line %d : error while parsing element %s attributes",parser->line,element); + free(element); + free(body); + return -1; + } + } else + *_attribs = NULL; + + *_element = element; + *_body = body; + + parser->last_body = body; + parser->ret_stack_size++; + parser->ret_stack = (ASX_LineSave_t*)realloc(parser->ret_stack,parser->ret_stack_size*sizeof(ASX_LineSave_t)); + if(parser->ret_stack_size > 1) + memmove(parser->ret_stack+1,parser->ret_stack,(parser->ret_stack_size-1)*sizeof(ASX_LineSave_t)); + parser->ret_stack[0].buffer = ret; + parser->ret_stack[0].line = ret_line; + parser->line = body ? body_line : ret_line; + + *_buffer = ret; + return 1; + +} + +static void +asx_parse_ref(ASX_Parser_t* parser, char** attribs, play_tree_t* pt) { + char *href; + + href = asx_get_attrib("HREF",attribs); + if(href == NULL) { + asx_warning_attrib_required(parser,"ENTRYREF" ,"HREF" ); + return; + } + + play_tree_add_file(pt,href); + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Adding file %s to element entry\n",href); + + free(href); + +} + +static play_tree_t* +asx_parse_entryref(ASX_Parser_t* parser,char* buffer,char** _attribs) { + mp_msg(MSGT_PLAYTREE,MSGL_INFO,"Need to implement entryref\n"); + return NULL; +} + +static play_tree_t* +asx_parse_entry(ASX_Parser_t* parser,char* buffer,char** _attribs) { + char *element,*body,**attribs; + int r,nref=0; + play_tree_t *ref; + + ref = play_tree_new(); + + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"ENTRY"); + return NULL; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"REF") == 0) { + asx_parse_ref(parser,attribs,ref); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to entry\n",element); + nref++; + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + if(body) free(body); + asx_free_attribs(attribs); + } + + if(nref <= 0) { + play_tree_free(ref,1); + return NULL; + } + return ref; + +} + + +static play_tree_t* +asx_parse_repeat(ASX_Parser_t* parser,char* buffer,char** _attribs) { + char *element,*body,**attribs; + play_tree_t *repeat, *list=NULL, *entry; + char* count; + int r; + + repeat = play_tree_new(); + + count = asx_get_attrib("COUNT",_attribs); + if(count == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Setting element repeat loop to infinit\n"); + repeat->loop = -1; // Infinit + } else { + repeat->loop = atoi(count); + free(count); + if(repeat->loop == 0) repeat->loop = 1; + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Setting element repeat loop to %d\n",repeat->loop); + } + + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"REPEAT"); + return NULL; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"ENTRY") == 0) { + entry = asx_parse_entry(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to repeat\n",element); + } + } else if(strcasecmp(element,"ENTRYREF") == 0) { + entry = asx_parse_entryref(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to repeat\n",element); + } + } else if(strcasecmp(element,"REPEAT") == 0) { + entry = asx_parse_repeat(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to repeat\n",element); + } + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + if(body) free(body); + asx_free_attribs(attribs); + } + + if(!list) { + play_tree_free(repeat,1); + return NULL; + } + play_tree_set_child(repeat,list); + + return repeat; + +} + + + +play_tree_t* +asx_parser_build_tree(char* buffer) { + char *element,*asx_body,**asx_attribs,*body, **attribs; + int r; + play_tree_t *asx,*entry,*list = NULL; + ASX_Parser_t* parser = asx_parser_new(); + + parser->line = 1; + + r = asx_get_element(parser,&buffer,&element,&asx_body,&asx_attribs); + if(r < 0) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"At line %d : Syntax error ???",parser->line); + asx_parser_free(parser); + return NULL; + } else if(r == 0) { // No contents + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"empty asx element"); + asx_parser_free(parser); + return NULL; + } + + if(strcasecmp(element,"ASX") != 0) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"first element isn't ASX, it's %s\n",element); + asx_free_attribs(asx_attribs); + if(body) free(body); + asx_parser_free(parser); + return NULL; + } + + if(!asx_body) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"ASX element is empty"); + asx_free_attribs(asx_attribs); + asx_parser_free(parser); + return NULL; + } + + asx = play_tree_new(); + buffer = asx_body; + while(buffer && buffer[0] != '\0') { + r = asx_get_element(parser,&buffer,&element,&body,&attribs); + if(r < 0) { + asx_warning_body_parse_error(parser,"ASX"); + asx_parser_free(parser); + return NULL; + } else if (r == 0) { // No more element + break; + } + if(strcasecmp(element,"ENTRY") == 0) { + entry = asx_parse_entry(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to asx\n",element); + } + } else if(strcasecmp(element,"ENTRYREF") == 0) { + entry = asx_parse_entryref(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to asx\n",element); + } + } else if(strcasecmp(element,"REPEAT") == 0) { + entry = asx_parse_repeat(parser,body,attribs); + if(entry) { + if(!list) list = entry; + else play_tree_append_entry(list,entry); + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Adding element %s to asx\n",element); + } + } else + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"Ignoring element %s\n",element); + if(body) free(body); + asx_free_attribs(attribs); + } + + free(asx_body); + asx_free_attribs(asx_attribs); + asx_parser_free(parser); + + + if(!list) { + play_tree_free(asx,1); + + return NULL; + } + + play_tree_set_child(asx,list); + + return asx; +} diff --git a/asxparser.h b/asxparser.h new file mode 100644 index 0000000000..d3571a8cf7 --- /dev/null +++ b/asxparser.h @@ -0,0 +1,17 @@ + +#include "playtree.h" + +typedef struct _ASX_Parser_t ASX_Parser_t; + +typedef struct { + char* buffer; + int line; +} ASX_LineSave_t; + +struct _ASX_Parser_t { + int line; // Curent line + ASX_LineSave_t *ret_stack; + int ret_stack_size; + char* last_body; +}; + diff --git a/playtree.c b/playtree.c new file mode 100644 index 0000000000..4feda54ae2 --- /dev/null +++ b/playtree.c @@ -0,0 +1,631 @@ + +#include +#include +#include +#include +#include "playtree.h" +#include "mp_msg.h" + +static int +play_tree_is_valid(play_tree_t* pt); + +play_tree_t* +play_tree_new(void) { + play_tree_t* r = (play_tree_t*)calloc(1,sizeof(play_tree_t)); + if(r == NULL) + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",sizeof(play_tree_t)); + return r; +} + +void +play_tree_free(play_tree_t* pt, int childs) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + if(childs) { + for(iter = pt->child; iter != NULL; iter = iter->next) + play_tree_free(iter,1); + pt->child = NULL; + } + + play_tree_remove(pt,0,0); + + for(iter = pt->child ; iter != NULL ; iter = iter->next) + iter->parent = NULL; + + //if(pt->params) free(pt->params); + if(pt->files) { + int i; + for(i = 0 ; pt->files[i] != NULL ; i++) + free(pt->files[i]); + free(pt->files); + } + + free(pt); +} + +void +play_tree_free_list(play_tree_t* pt, int childs) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + for(iter = pt ; iter->prev != NULL ; iter = iter->prev) + /* NOTHING */; + + for( ; iter != NULL ; iter = iter->next) + play_tree_free(iter,childs); + + +} + +void +play_tree_append_entry(play_tree_t* pt, play_tree_t* entry) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); + assert(entry != NULL); +#endif + + if(pt == entry) + return; + + for(iter = pt ; iter->next != NULL ; iter = iter->next) + /* NOTHING */; + + entry->parent = iter->parent; + entry->prev = iter; + entry->next = NULL; + iter->next = entry; +} + +void +play_tree_prepend_entry(play_tree_t* pt, play_tree_t* entry) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); + assert(entry != NULL); +#endif + + for(iter = pt ; iter->prev != NULL; iter = iter->prev) + /* NOTHING */; + + entry->prev = NULL; + entry->next = iter; + entry->parent = iter->parent; + + iter->prev = entry; + if(entry->parent) { +#ifdef DEBUG + assert(entry->parent->child == iter); +#endif + entry->parent->child = entry; + } +} + +void +play_tree_insert_entry(play_tree_t* pt, play_tree_t* entry) { + +#ifdef DEBUG + assert(pt != NULL); + assert(entry != NULL); +#endif + + entry->parent = pt->parent; + entry->prev = pt; + if(pt->next) { +#ifdef DEBUG + assert(pt->next->prev == pt); +#endif + entry->next = pt->next; + entry->next->prev = entry; + } else + entry->next = NULL; + pt->next = entry; + +} + +void +play_tree_remove(play_tree_t* pt, int free_it,int with_childs) { + +#ifdef DEBUG + assert(pt != NULL); +#endif + + // Middle of list + if(pt->prev && pt->next) { +#ifdef DEBUG + assert(pt->prev->next == pt); + assert(pt->next->prev == pt); +#endif + pt->prev->next = pt->next; + pt->next->prev = pt->prev; + } // End of list + else if(pt->prev) { +#ifdef DEBUG + assert(pt->prev->next == pt); +#endif + pt->prev->next = NULL; + } // Begining of list + else if(pt->next) { +#ifdef DEBUG + assert(pt->next->prev == pt); +#endif + pt->next->prev = NULL; + if(pt->parent) { +#ifdef DEBUG + assert(pt->parent->child == pt); +#endif + pt->parent->child = pt->next; + } + } // The only one + else if(pt->parent) { +#ifdef DEBUG + assert(pt->parent->child == pt); +#endif + pt->parent->child = NULL; + } + + pt->prev = pt->next = pt->parent = NULL; + if(free_it) + play_tree_free(pt,with_childs); + +} + +void +play_tree_set_child(play_tree_t* pt, play_tree_t* child) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); + assert(pt->files == NULL); +#endif + + for(iter = pt->child ; iter != NULL ; iter = iter->next) + iter->parent = NULL; + + // Go back to first one + for(iter = child ; iter->prev != NULL ; iter = iter->prev) + /* NOTHING */; + + pt->child = iter; + + for( ; iter != NULL ; iter= iter->next) + iter->parent = pt; + +} + +void +play_tree_set_parent(play_tree_t* pt, play_tree_t* parent) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + if(pt->parent) + pt->parent->child = NULL; + + for(iter = pt ; iter != NULL ; iter = iter->next) + iter->parent = parent; + + if(pt->prev) { + for(iter = pt->prev ; iter->prev != NULL ; iter = iter->prev) + iter->parent = parent; + iter->parent = parent; + parent->child = iter; + } else + parent->child = pt; + +} + + +void +play_tree_add_file(play_tree_t* pt,char* file) { + int n = 0; + +#ifdef DEBUG + assert(pt != NULL); + assert(pt->child == NULL); + assert(file != NULL); +#endif + + if(pt->files) { + for(n = 0 ; pt->files[n] != NULL ; n++) + /* NOTHING */; + } + pt->files = (char**)realloc(pt->files,(n+2)*sizeof(char*)); + if(pt->files ==NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",(n+2)*sizeof(char*)); + return; + } + + pt->files[n] = strdup(file); + pt->files[n+1] = NULL; + +} + +int +play_tree_remove_file(play_tree_t* pt,char* file) { + int n,f = -1; + +#ifdef DEBUG + assert(pt != NULL); + assert(file != NULL); + assert(pt->files != NULL); +#endif + + for(n=0 ; pt->files[n] != NULL ; n++) { + if(strcmp(file,pt->files[n]) == 0) + f = n; + } + + if(f < 0) // Not found + return 0; + +#ifdef DEBUG + assert(n > f); +#endif + + free(pt->files[f]); + + if(n > 1) { + memmove(&pt->files[f],&pt->files[f+1],(n-f)*sizeof(char*)); + pt->files = (char**)realloc(pt->files,n*sizeof(char*)); + if(pt->files == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",(n+2)*sizeof(char*)); + return -1; + } + } else { + free(pt->files); + pt->files = NULL; + } + + return 1; +} + +#if 0 +void +play_tree_set_param(play_tree_t* pt, char* name, char* val) { + int n = 0,ni = -1; + +#ifdef DEBUG + assert(pt != NULL); + assert(name != NULL); +#endif + + if(pt->params) { + for( ; pt->params[n].name != NULL ; n++) { + if(strcasecmp(pt->params[n].name,name) == 0) + ni = n; + } + } + + if(ni > 0) { + if(pt->params[n].value != NULL) free(pt->params[n].value); + pt->params[n].value = val; + return; + } + + pt->params = (play_tree_param_t*)realloc(pt->params,(n+2)*sizeof(play_tree_param_t)); + pt->params[n].name = strdup(name); + pt->params[n].value = val != NULL ? strdup(val) : NULL; + memset(&pt->params[n+1],0,sizeof(play_tree_param_t)); + + return; +} + +int +play_tree_unset_param(play_tree_t* pt, char* name) { + int n,ni = 0; + +#ifdef DEBUG + assert(pt != NULL); + assert(name != NULL); + assert(pt->params != NULL); +#endif + + for(n = 0 ; pt->params[n].name != NULL ; n++) { + if(strcasecmp(pt->params[n].name,name) == 0) + ni = n; + } + + if(ni < 0) + return 0; + + if(n > 1) { + memmove(&pt->params[ni],&pt->params[ni+1],(n-ni)*sizeof(play_tree_param_t)); + pt->params = (play_tree_param_t*)realloc(pt->params,n*sizeof(play_tree_param_t)); + assert(pt->params != NULL); + } else { + free(pt->params); + pt->params = NULL; + } + + return 1; +} + +#endif + +play_tree_iter_t* +play_tree_iter_new(play_tree_t* pt) { + play_tree_iter_t* iter; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + if( ! play_tree_is_valid(pt)) + return NULL; + + iter = (play_tree_iter_t*)calloc(1,sizeof(play_tree_iter_t)); + if(! iter) return NULL; + iter->root = pt; + iter->tree = NULL; + + if(pt->parent) + iter->loop = pt->parent->loop; + + return iter; +} + +void +play_tree_iter_free(play_tree_iter_t* iter) { + +#ifdef DEBUG + assert(iter != NULL); +#endif + + if(iter->status_stack) { +#ifdef DEBUG + assert(iter->stack_size > 0); +#endif + free(iter->status_stack); + } + + free(iter); +} + +int +play_tree_iter_step(play_tree_iter_t* iter, int d,int with_nodes) { + play_tree_t* pt; + +#ifdef DEBUG + assert(iter != NULL); + assert(iter->root != NULL); +#endif + + if(iter->tree == NULL) { + iter->tree = iter->root; + return play_tree_iter_step(iter,0,with_nodes); + } + + iter->file = 0; + if( d > 0 ) + pt = iter->tree->next; + else if(d < 0) + pt = iter->tree->prev; + else + pt = iter->tree; + + if(pt == NULL) { // No next + // Must we loop? + if(iter->tree->parent && iter->tree->parent->loop != 0 && ((d > 0 && iter->loop != 0) || ( d < 0 && (iter->loop < 0 || iter->loop < iter->tree->parent->loop) ) ) ) { + if(d > 0) { // Go back to the first one + for(pt = iter->tree ; pt->prev != NULL; pt = pt->prev) + /* NOTHNG */; + if(iter->loop > 0) iter->loop--; + } else if( d < 0 ) { // Or the last one + for(pt = iter->tree ; pt->next != NULL; pt = pt->next) + /* NOTHNG */; + if(iter->loop >= 0 && iter->loop < iter->tree->parent->loop) iter->loop++; + } + iter->tree = pt; + return play_tree_iter_step(iter,0,with_nodes); + } + // Go up one level + return play_tree_iter_up_step(iter,d,with_nodes); + + } + + // Is there any valid childs ? + if(pt->child && play_tree_is_valid(pt->child)) { + iter->tree = pt; + if(with_nodes) { // Stop on the node + return PLAY_TREE_ITER_NODE; + } else // Or follow it + return play_tree_iter_down_step(iter,d,with_nodes); + } + + // Is it a valid enty ? + if(! play_tree_is_valid(pt)) { + if(d == 0) { // Can this happen ? + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"What to do now ???? Infinite loop if we continue\n"); + return PLAY_TREE_ITER_ERROR; + } // Not a valid entry : go to next one + return play_tree_iter_step(iter,d,with_nodes); + } + +#ifdef DEBUG + assert(iter->tree->files != NULL); +#endif + + iter->tree = pt; + for(d = 0 ; iter->tree->files[d] != NULL ; d++) + /* NOTHING */; + iter->num_files = d; + + return PLAY_TREE_ITER_ENTRY; + +} + +static int +play_tree_is_valid(play_tree_t* pt) { + play_tree_t* iter; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + if(pt->files != NULL) + return 1; + else if (pt->child != NULL) { + for(iter = pt->child ; iter != NULL ; iter = iter->next) { + if(play_tree_is_valid(iter)) + return 1; + } + } + return 0; +} + +int +play_tree_iter_up_step(play_tree_iter_t* iter, int d,int with_nodes) { + +#ifdef DEBUG + assert(iter != NULL); + assert(iter->tree != NULL); +#endif + + iter->file = 0; + if(iter->tree->parent == iter->root->parent) + return PLAY_TREE_ITER_END; + +#ifdef DEBUG + assert(iter->tree->parent != NULL); + assert(iter->stack_size > 0); + assert(iter->status_stack != NULL); +#endif + + // Pop status + iter->stack_size--; + iter->loop = iter->status_stack[iter->stack_size]; + iter->status_stack = (int*)realloc(iter->status_stack,iter->stack_size*sizeof(int)); + if(iter->stack_size > 0 && iter->status_stack == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",iter->stack_size*sizeof(char*)); + return PLAY_TREE_ITER_ERROR; + } + iter->tree = iter->tree->parent; + return play_tree_iter_step(iter,d,with_nodes); +} + +int +play_tree_iter_down_step(play_tree_iter_t* iter, int d,int with_nodes) { + +#ifdef DEBUG + assert(iter->tree->files == NULL); + assert(iter->tree->child != NULL); + assert(iter->tree->child->parent == iter->tree); +#endif + + iter->file = 0; + // Push status + iter->stack_size++; + iter->status_stack = (int*)realloc(iter->status_stack,iter->stack_size*sizeof(int)); + if(iter->status_stack == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",iter->stack_size*sizeof(int)); + return PLAY_TREE_ITER_ERROR; + } + iter->status_stack[iter->stack_size-1] = iter->loop; + // Set new status + iter->loop = iter->tree->loop-1; + if(d >= 0) + iter->tree = iter->tree->child; + else { + play_tree_t* pt; + for(pt = iter->tree->child ; pt->next != NULL ; pt = pt->next) + /*NOTING*/; + iter->tree = pt; + } + return play_tree_iter_step(iter,0,with_nodes); +} + +char* +play_tree_iter_get_file(play_tree_iter_t* iter, int d) { + +#ifdef DEBUG + assert(iter != NULL); + assert(iter->tree->child == NULL); +#endif + + if(iter->tree->files == NULL) + return NULL; + + if(d > 0) { + if(iter->tree->files[iter->file] == NULL) + return NULL; + iter->file++; + } else if(d < 0) { + if(iter->file == 0) + return NULL; + iter->file--; + } + return iter->tree->files[iter->file-1]; +} + +play_tree_t* +play_tree_cleanup(play_tree_t* pt) { + play_tree_t* iter, *tmp, *first; + +#ifdef DEBUG + assert(pt != NULL); +#endif + + if( ! play_tree_is_valid(pt)) { + play_tree_remove(pt,1,1); + return NULL; + } + + first = pt->child; + + for(iter = pt->child ; iter != NULL ; ) { + tmp = iter; + iter = iter->next; + if(! play_tree_is_valid(tmp)) { + play_tree_remove(tmp,1,1); + if(tmp == first) first = iter; + } + } + + for(iter = first ; iter != NULL ; ) { + tmp = iter; + iter = iter->next; + play_tree_cleanup(tmp); + } + + return pt; + +} + +play_tree_iter_t* +play_tree_iter_new_copy(play_tree_iter_t* old) { + play_tree_iter_t* iter; + +#ifdef DEBUG + assert(old != NULL); +#endif + + iter = (play_tree_iter_t*)malloc(sizeof(play_tree_iter_t)); + if(iter == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",sizeof(play_tree_iter_t)); + return NULL; + } +; + memcpy(iter,old,sizeof(play_tree_iter_t)); + if(old->status_stack) { + iter->status_stack = (int*)malloc(old->stack_size * sizeof(int)); + if(iter->status_stack == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",old->stack_size * sizeof(int)); + free(iter); + return NULL; + } + memcpy(iter->status_stack,old->status_stack,iter->stack_size*sizeof(int)); + } + return iter; +} diff --git a/playtree.h b/playtree.h new file mode 100644 index 0000000000..ef293ec2b7 --- /dev/null +++ b/playtree.h @@ -0,0 +1,144 @@ + +#include "libmpdemux/stream.h" + +#define PLAY_TREE_ITER_ERROR 0 +#define PLAY_TREE_ITER_ENTRY 1 +#define PLAY_TREE_ITER_NODE 2 +#define PLAY_TREE_ITER_END 3 + +typedef struct play_tree play_tree_t; +typedef struct play_tree_iter play_tree_iter_t; + +#if 0 +typedef struct play_tree_info play_tree_info_t; +typedef struct play_tree_param play_tree_param_t; + +// TODO : a attrib,val pair system and not something hardcoded +struct play_tree_info { + char* title; + char* author; + char* copyright; + char* abstract; + // Some more ?? +} + +struct play_tree_param { + char* name; + char* value; +} +#endif + +struct play_tree { + play_tree_t* parent; + play_tree_t* child; + play_tree_t* next; + play_tree_t* prev; + + //play_tree_info_t info; + //int n_param; + //play_tree_param_t* params; + int loop; + char** files; +}; + +struct play_tree_iter { + play_tree_t* root; // Iter root tree + play_tree_t* tree; // Current tree + // struct m_config* config; + int loop; // Looping status + int file; + int num_files; + + int* status_stack; // loop/valid stack to save/revert status when we go up/down + int stack_size; // status stack size +}; + +play_tree_t* +play_tree_new(void); + +// If childs is true free also the childs +void +play_tree_free(play_tree_t* pt, int childs); + + +void +play_tree_free_list(play_tree_t* pt, int childs); + + +// Childs +void +play_tree_set_child(play_tree_t* pt, play_tree_t* child); +// Or parent +void +play_tree_set_parent(play_tree_t* pt, play_tree_t* parent); + + +// Add at end +void +play_tree_append_entry(play_tree_t* pt, play_tree_t* entry); + +// And on begining +void +play_tree_prepend_entry(play_tree_t* pt, play_tree_t* entry); + +// Insert after +void +play_tree_insert_entry(play_tree_t* pt, play_tree_t* entry); + +// Detach from the tree +void +play_tree_remove(play_tree_t* pt, int free_it,int with_childs); + + +void +play_tree_add_file(play_tree_t* pt,char* file); + +int +play_tree_remove_file(play_tree_t* pt,char* file); + + +#if 0 +// Val can be NULL +void +play_tree_set_param(play_tree_t* pt, char* name, char* val); + +int +play_tree_unset_param(play_tree_t* pt, char* name); + +#endif + + +/// Iterator + +play_tree_iter_t* +play_tree_iter_new(play_tree_t* pt); + +play_tree_iter_t* +play_tree_iter_new_copy(play_tree_iter_t* old); + +void +play_tree_iter_free(play_tree_iter_t* iter); + +// d is the direction : d > 0 == next , d < 0 == prev +// with_node : TRUE == stop on nodes with childs, FALSE == go directly to the next child + +int +play_tree_iter_step(play_tree_iter_t* iter, int d,int with_nodes); + +int // Break a loop, etc +play_tree_iter_up_step(play_tree_iter_t* iter, int d,int with_nodes); + +int // Enter a node child list +play_tree_iter_down_step(play_tree_iter_t* iter, int d,int with_nodes); + +char* +play_tree_iter_get_file(play_tree_iter_t* iter, int d); + +play_tree_t* +parse_playtree(stream_t *stream); + +play_tree_t* +play_tree_cleanup(play_tree_t* pt); + +play_tree_t* +parse_playlist_file(char* file); diff --git a/playtreeparser.c b/playtreeparser.c new file mode 100644 index 0000000000..19dc14c209 --- /dev/null +++ b/playtreeparser.c @@ -0,0 +1,298 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "playtree.h" +#include "libmpdemux/stream.h" +#include "mp_msg.h" + +extern play_tree_t* +asx_parser_build_tree(char* buffer); + + +static char* buffer = NULL; +static int buffer_size = 0, buffer_end = 0; + + + +play_tree_t* +parse_asx(stream_t* stream) { + int r; + int comments = 0,read = 1,eof = 0; + + mp_msg(MSGT_PLAYTREE,MSGL_V,"Trying asx...\n"); + + while(1) { + if(read && eof) // Eof reached before anything useful + return NULL; + if(read) { + if(buffer_size - buffer_end < 50) buffer_size += 255; + buffer = (char*)realloc(buffer,buffer_size*sizeof(char)); + if(buffer == NULL) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't allocate %d bytes of memory\n",buffer_size*sizeof(char)); + buffer_size = buffer_end = 0; + return NULL; + } + + r = stream_read(stream,buffer+buffer_end,buffer_size-buffer_end-1); + if(r < 0) { + mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Can't read from stream r=%d\n",r); + return NULL; + } else if(r == 0) + eof = 1; + buffer_end += r; + buffer[buffer_end] = '\0'; + } + + if(comments) { // Jump comments + int e; + char* end = strstr(buffer,"-->"); + if(!end) { + if(buffer[buffer_end-1] != '-') + buffer_end = 0; // Drop buffer content if last char isn't '-' + continue; + } + comments = 0; + e = end - buffer + 3; + if(e >= buffer_end) { // > seems impossible + buffer_end = 0; // Drop buffer content + read = 1; + continue; + } + buffer_end -= e; + memmove(buffer,end+3,buffer_end); // Drop comments + continue; + } + + for(r= 0 ; r < buffer_end ; r++) { + if(strchr(" \n\r\t",buffer[r]) != NULL) // Jump space + continue; + if(buffer[r] != '<') { + mp_msg(MSGT_PLAYTREE,MSGL_DBG2,"First char isn't '<' but '%c'\n",buffer[r]); + mp_msg(MSGT_PLAYTREE,MSGL_DBG3,"Buffer = [%s]\n",buffer); + return NULL; + } + break; // Stop on first '<' + } + if(r > buffer_end-4) { // We need more + if(r > 0) { // Drop unuseful beggining + buffer_end -= r; + memmove(buffer,&buffer[r],buffer_end); + } + read = 1; + continue; + } + + if(strncmp(&buffer[r],"