From 84cad9ba868a20f981ddb428f82c4fdd30bc5e38 Mon Sep 17 00:00:00 2001 From: Andre Pang Date: Mon, 19 Apr 2004 02:08:52 +0000 Subject: [PATCH] * Added Continuous Media Markup Language (CMML) codec --- configure.ac | 11 + modules/LIST | 4 +- modules/codec/cmml/Modules.am | 11 + modules/codec/cmml/browser_open.c | 67 +++ modules/codec/cmml/browser_open.h | 33 ++ modules/codec/cmml/cmml.c | 284 ++++++++++ modules/codec/cmml/history.c | 228 ++++++++ modules/codec/cmml/history.h | 69 +++ modules/codec/cmml/intf.c | 835 ++++++++++++++++++++++++++++++ modules/codec/cmml/xarray.c | 249 +++++++++ modules/codec/cmml/xarray.h | 73 +++ modules/codec/cmml/xlist.c | 245 +++++++++ modules/codec/cmml/xlist.h | 168 ++++++ modules/codec/cmml/xstrcat.h | 44 ++ modules/codec/cmml/xtag.c | 692 +++++++++++++++++++++++++ modules/codec/cmml/xtag.h | 49 ++ modules/codec/cmml/xurl.c | 490 ++++++++++++++++++ modules/codec/cmml/xurl.h | 68 +++ 18 files changed, 3619 insertions(+), 1 deletion(-) create mode 100644 modules/codec/cmml/Modules.am create mode 100644 modules/codec/cmml/browser_open.c create mode 100644 modules/codec/cmml/browser_open.h create mode 100644 modules/codec/cmml/cmml.c create mode 100644 modules/codec/cmml/history.c create mode 100644 modules/codec/cmml/history.h create mode 100644 modules/codec/cmml/intf.c create mode 100644 modules/codec/cmml/xarray.c create mode 100644 modules/codec/cmml/xarray.h create mode 100644 modules/codec/cmml/xlist.c create mode 100644 modules/codec/cmml/xlist.h create mode 100644 modules/codec/cmml/xstrcat.h create mode 100644 modules/codec/cmml/xtag.c create mode 100644 modules/codec/cmml/xtag.h create mode 100644 modules/codec/cmml/xurl.c create mode 100644 modules/codec/cmml/xurl.h diff --git a/configure.ac b/configure.ac index 991c40aec2..42fbc82457 100644 --- a/configure.ac +++ b/configure.ac @@ -2346,6 +2346,16 @@ AX_ADD_LDFLAGS([subsdec],[${LIBICONV}]) AX_ADD_CPPFLAGS([subsdec],[${INCICONV}]) AX_ADD_PLUGINS([subsdec]) +dnl +dnl CMML plugin +dnl +AC_ARG_ENABLE(cmml, + [ --enable-cmml CMML support (default enabled)]) +if test "${enable_cmml}" != "no" +then + AX_ADD_PLUGINS([cmml]) +fi + dnl dnl Video Filters @@ -3776,6 +3786,7 @@ AC_CONFIG_FILES([ modules/audio_mixer/Makefile modules/audio_output/Makefile modules/codec/Makefile + modules/codec/cmml/Makefile modules/codec/ffmpeg/Makefile modules/codec/ffmpeg/postprocessing/Makefile modules/codec/ogt/Makefile diff --git a/modules/LIST b/modules/LIST index 9f68c9eeb2..c13ea53292 100644 --- a/modules/LIST +++ b/modules/LIST @@ -1,5 +1,5 @@ List of vlc plugins (221) -$Id: LIST,v 1.19 2004/01/05 13:07:02 zorglub Exp $ +$Id$ * a52: A/52 basic parser/packetizer @@ -70,6 +70,8 @@ $Id: LIST,v 1.19 2004/01/05 13:07:02 zorglub Exp $ * cinepak: Cinepack video decoder * clone: Clone video filter + + * cmml: Continuous Media Markup Language annotations/hyperlinks decoder * corba: CORBA control module diff --git a/modules/codec/cmml/Modules.am b/modules/codec/cmml/Modules.am new file mode 100644 index 0000000000..27747cced4 --- /dev/null +++ b/modules/codec/cmml/Modules.am @@ -0,0 +1,11 @@ +SOURCES_cmml = \ + browser_open.c browser_open.h \ + cmml.c \ + history.c history.h \ + intf.c \ + xarray.c xarray.h \ + xlist.c xlist.h \ + xstrcat.h \ + xtag.c xtag.h \ + xurl.c xurl.h + diff --git a/modules/codec/cmml/browser_open.c b/modules/codec/cmml/browser_open.c new file mode 100644 index 0000000000..9ac067039b --- /dev/null +++ b/modules/codec/cmml/browser_open.c @@ -0,0 +1,67 @@ +/***************************************************************************** + * browser_open.c: platform-independent opening of a web browser + ***************************************************************************** + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include + +#include "xstrcat.h" + +int browser_Open( char *psz_url ) +{ +#ifdef SYS_DARWIN + char *psz_open_commandline; + + psz_open_commandline = strdup( "open " ); + xstrcat( psz_open_commandline, psz_url ); + + return system( psz_open_commandline ); +#elif defined( WIN32 ) + char *psz_open_commandline; + + psz_open_commandline = strdup( "explorer " ); + xstrcat( psz_open_commandline, psz_url ); + + return system( psz_open_commandline ); +#else + /* Assume we're on a UNIX of some sort */ + char *psz_open_commandline; + + /* Debian uses www-browser */ + psz_open_commandline = strdup( "www-browser" ); + xstrcat( psz_open_commandline, psz_url ); + + if( system( psz_open_commandline ) != 0 ) + { + free( psz_open_commandline ); + + /* Try mozilla */ + psz_open_commandline = strdup( "mozilla" ); + xstrcat( psz_open_commandline, psz_url ); + return system( psz_open_commandline ); + } +#endif +} + diff --git a/modules/codec/cmml/browser_open.h b/modules/codec/cmml/browser_open.h new file mode 100644 index 0000000000..cee25a5b49 --- /dev/null +++ b/modules/codec/cmml/browser_open.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * browser_open.h: platform-independent opening of a web browser + ***************************************************************************** + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef __BROWSER_OPEN_H__ +#define __BROWSER_OPEN_H__ + +int browser_Open( char *psz_url ); + +#endif /* __BROWSER_OPEN_H__ */ + diff --git a/modules/codec/cmml/cmml.c b/modules/codec/cmml/cmml.c new file mode 100644 index 0000000000..9650d0bcdc --- /dev/null +++ b/modules/codec/cmml/cmml.c @@ -0,0 +1,284 @@ +/***************************************************************************** + * cmml.c : CMML annotations/metadata decoder + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Author: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include + +#include + +#include "charset.h" + +#include "xtag.h" + +#undef CMML_DEBUG + +/***************************************************************************** + * decoder_sys_t : decoder descriptor + *****************************************************************************/ +struct decoder_sys_t +{ + intf_thread_t * p_intf; +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int OpenDecoder ( vlc_object_t * ); +static void CloseDecoder ( vlc_object_t * ); + +static void DecodeBlock ( decoder_t *, block_t ** ); + +static void ParseText ( decoder_t *, block_t * ); + +/***************************************************************************** + * Exported prototypes + *****************************************************************************/ +int E_(OpenIntf) ( vlc_object_t * ); +void E_(CloseIntf) ( vlc_object_t * ); + +/***************************************************************************** + * Module descriptor. + *****************************************************************************/ +vlc_module_begin(); + set_description( _("CMML annotations decoder") ); + set_capability( "decoder", 50 ); + set_callbacks( OpenDecoder, CloseDecoder ); + add_shortcut( "cmml" ); + + add_submodule(); + set_capability( "interface", 0 ); + set_callbacks( E_(OpenIntf), E_(CloseIntf) ); +vlc_module_end(); + +/***************************************************************************** + * OpenDecoder: probe the decoder and return score + ***************************************************************************** + * Tries to launch a decoder and return score so that the interface is able + * to chose. + *****************************************************************************/ +static int OpenDecoder( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t*)p_this; + input_thread_t * p_input; + decoder_sys_t *p_sys; + vlc_value_t val; + + if( p_dec->fmt_in.i_codec != VLC_FOURCC('c','m','m','l') ) + { + return VLC_EGENERIC; + } + + p_dec->pf_decode_sub = DecodeBlock; + +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "I am at %p", p_dec ); +#endif + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_dec->p_sys = p_sys = + (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) + { + msg_Err( p_dec, "out of memory" ); + return VLC_EGENERIC; + } + + /* Let other interested modules know that we're a CMML decoder + * We have to set this variable on the input thread, because there's + * typically more than one decoder running so we can't find the CMML + * decoder succesfully with vlc_object_find. (Any hints on how to achieve + * this would be rather appreciated ;) */ + p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_ANYWHERE ); +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "p_input is at %p", p_input ); +#endif + val.p_address = p_dec; + var_Create( p_input, "has-cmml-decoder", + VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT ); + if( var_Set( p_input, "has-cmml-decoder", val ) != VLC_SUCCESS ) + { + msg_Dbg( p_dec, "var_Set of has-cmml-decoder failed" ); + } + vlc_object_release( p_input ); + + /* initialise the CMML responder interface */ + p_sys->p_intf = intf_Create( p_dec, "cmml" ); + p_sys->p_intf->b_block = VLC_FALSE; + intf_RunThread( p_sys->p_intf ); + + return VLC_SUCCESS; +} + +/**************************************************************************** + * DecodeBlock: the whole thing + **************************************************************************** + * This function must be fed with complete subtitles units. + ****************************************************************************/ +static void DecodeBlock( decoder_t *p_dec, block_t **pp_block ) +{ + if( !pp_block || *pp_block == NULL ) + { + return; + } + + ParseText( p_dec, *pp_block ); + + block_Release( *pp_block ); + *pp_block = NULL; +} + +/***************************************************************************** + * CloseDecoder: clean up the decoder + *****************************************************************************/ +static void CloseDecoder( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t *)p_this; + decoder_sys_t *p_sys = p_dec->p_sys; + intf_thread_t *p_intf; + + /* Destroy the interface object/thread */ + p_intf = vlc_object_find( p_dec, VLC_OBJECT_INTF, FIND_CHILD ); + if( p_intf != NULL ) + { +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "CMML decoder is freeing interface thread" ); +#endif + intf_StopThread( p_intf ); + vlc_object_detach( p_intf ); + vlc_object_release( p_intf ); + intf_Destroy( p_intf ); + } + + p_sys->p_intf = NULL; + + free( p_sys ); +} + +/***************************************************************************** + * ParseText: parse an text subtitle packet and send it to the video output + *****************************************************************************/ +static void ParseText( decoder_t *p_dec, block_t *p_block ) +{ + char *psz_subtitle, *psz_cmml, *psz_url; + XTag *p_clip_parser, *p_anchor; + vlc_value_t val; + + /* We cannot display a subpicture with no date */ + if( p_block->i_pts == 0 ) + { + msg_Warn( p_dec, "subtitle without a date" ); + return; + } + + /* Check validity of packet data */ + if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' ) + { + msg_Warn( p_dec, "empty subtitle" ); + return; + } + + /* get anchor text from CMML */ + + /* Copy the whole CMML tag into our own buffer: + allocate i_buffer bytes + 1 for the terminating \0 */ + if ( (psz_cmml = malloc( p_block->i_buffer + 1 )) == NULL ) + return; + psz_cmml = memcpy( psz_cmml, p_block->p_buffer, p_block->i_buffer ); + psz_cmml[p_block->i_buffer] = '\0'; /* terminate the string */ +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "psz_cmml is \"%s\"", psz_cmml ); +#endif + + /* Parse the part of the CMML */ + p_clip_parser = xtag_new_parse( psz_cmml, p_block->i_buffer ); + if( !p_clip_parser ) + { + msg_Warn( p_dec, "couldn't initialise parser" ); + free( psz_cmml ); + return; + } + + /* Parse the anchor tag and get its contents */ + p_anchor = xtag_first_child( p_clip_parser, "a" ); + if( p_anchor != NULL ) + { + psz_subtitle = xtag_get_pcdata( p_anchor ); + } + else + { + psz_subtitle = strdup( " " ); + } + +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "psz_subtitle is \"%s\"", psz_subtitle ); +#endif + + /* get URL from the current clip, if one exists */ + psz_url = xtag_get_attribute( p_anchor, "href" ); +#ifdef CMML_DEBUG + msg_Dbg( p_dec, "psz_url is \"%s\"", psz_url ); +#endif + if( psz_url ) + { + char *psz_tmp = strdup( psz_url ); + + val.p_address = psz_tmp; + if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS ) + { + (void) var_Create( p_dec, "psz-current-anchor-url", + VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT ); + msg_Dbg( p_dec, "creating psz-current-anchor-url" ); + if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS ) + msg_Dbg( p_dec, "var_Set of psz-current-anchor-url failed" ); + } + } + + if( psz_subtitle ) + { + char *psz_tmp = strdup( psz_subtitle ); + + val.p_address = psz_tmp; + if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS ) + { + (void) var_Create( p_dec, "psz-current-anchor-description", + VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT ); + msg_Dbg( p_dec, "creating psz-current-anchor-description" ); + if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS ) + msg_Dbg( p_dec, "var_Set of psz-current-anchor-description failed" ); + } + + } + + if( psz_subtitle ) free( psz_subtitle ); + if( psz_cmml ) free( psz_cmml ); + if( p_anchor ) free( p_anchor ); + if( p_clip_parser ) free( p_clip_parser ); + if( psz_url ) free( psz_url ); +} + diff --git a/modules/codec/cmml/history.c b/modules/codec/cmml/history.c new file mode 100644 index 0000000000..ae57503129 --- /dev/null +++ b/modules/codec/cmml/history.c @@ -0,0 +1,228 @@ +/***************************************************************************** + * history.c: vlc_history_t (web-browser-like back/forward history) handling + ***************************************************************************** + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include + +#include "history.h" + +#include "xarray.h" + +#ifdef HAVE_STDLIB_H +# include /* realloc() */ +#endif + +#undef HISTORY_DEBUG + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void history_Dump( history_t *p_history ); + +/***************************************************************************** + * Local structure lock + *****************************************************************************/ + +/***************************************************************************** + * Actual history code + *****************************************************************************/ + +history_t *history_New() +{ + history_t *p_new_history; + + p_new_history = calloc( 1, sizeof( struct history_t ) ); + if( p_new_history == NULL ) return NULL; + + p_new_history->p_xarray = xarray_New( 0 ); + if( p_new_history->p_xarray == NULL ) + { + free( p_new_history ); + return NULL; + } + +#ifndef HISTORY_DEBUG + /* make dummy reference to history_Dump to avoid compiler warnings */ + while (0) + { + void *p_tmp; + + p_tmp = history_Dump; + } +#endif + + return p_new_history; +} + +vlc_bool_t history_GoBackSavingCurrentItem ( history_t *p_history, + history_item_t *p_item ) +{ + history_PruneAndInsert( p_history, p_item ); + + /* PruneAndInsert will increment the index, so we need to go + * back one position to reset the index to the place we were at + * before saving the current state, and then go back one more to + * actually go back */ + p_history->i_index -= 2; + +#ifdef HISTORY_DEBUG + history_Dump( p_history ); +#endif + return VLC_TRUE; +} + +static void history_Dump( history_t *p_history ) +{ + unsigned int i_count; + int i; + + if( xarray_Count( p_history->p_xarray, &i_count ) != XARRAY_SUCCESS ) + return; + + for (i = 0; i < (int) i_count; i++) + { + history_item_t *p_item; + void *pv_item; + + xarray_ObjectAtIndex( p_history->p_xarray, i, &pv_item ); + + p_item = (history_item_t *) pv_item; + + if( p_item == NULL ) + fprintf( stderr, "HISTORY: [%d] NULL\n", i ); + else + fprintf( stderr, "HISTORY: [%d] %p (%p->%s)\n", i, p_item, + p_item->psz_uri, p_item->psz_uri ); + } +} + +vlc_bool_t history_GoForwardSavingCurrentItem ( history_t *p_history, + history_item_t *p_item ) +{ +#ifdef HISTORY_DEBUG + history_Dump( p_history ); +#endif + + if( xarray_ReplaceObject( p_history->p_xarray, p_history->i_index, p_item ) + == XARRAY_SUCCESS ) + { + p_history->i_index++; + return VLC_TRUE; + } + else + { + return VLC_FALSE; + } +} + +vlc_bool_t history_CanGoBack( history_t *p_history ) +{ + if( p_history->i_index > 0 ) + return VLC_TRUE; + else + return VLC_FALSE; +} + +vlc_bool_t history_CanGoForward( history_t *p_history ) +{ + unsigned int i_count; + + if( xarray_Count( p_history->p_xarray, &i_count ) != XARRAY_SUCCESS ) + return VLC_FALSE; + + if( p_history->i_index < i_count ) + return VLC_TRUE; + else + return VLC_FALSE; +} + +history_item_t *history_Item( history_t *p_history ) +{ + history_item_t *p_item; + void *pv_item; + + if( xarray_ObjectAtIndex( p_history->p_xarray, p_history->i_index, + &pv_item ) + == XARRAY_SUCCESS ) + { + p_item = (history_item_t *) pv_item; + return p_item; + } + else + { + return NULL; + } +} + +void history_Prune( history_t *p_history ) +{ + xarray_RemoveObjectsAfter( p_history->p_xarray, p_history->i_index ); + xarray_RemoveObject( p_history->p_xarray, p_history->i_index ); +} + +void history_PruneAndInsert( history_t *p_history, history_item_t *p_item ) +{ + unsigned int i_count; + + xarray_Count( p_history->p_xarray, &i_count ); + + if( i_count == 0 ) + { + xarray_InsertObject( p_history->p_xarray, p_item, 0 ); + p_history->i_index = 1; + } + else + { + history_Prune( p_history ); + xarray_InsertObject( p_history->p_xarray, p_item, p_history->i_index ); + p_history->i_index++; + } +} + +unsigned int history_Count( history_t *p_history ) +{ + int i_count; + xarray_Count( p_history->p_xarray, &i_count ); + return i_count; +} + +unsigned int history_Index( history_t *p_history ) +{ + return p_history->i_index; +} + +history_item_t * historyItem_New( char *psz_name, char *psz_uri ) +{ + history_item_t *p_history_item = NULL; + + p_history_item = (history_item_t *) malloc( sizeof(history_item_t) ); + if( !p_history_item ) return NULL; + + p_history_item->psz_uri = strdup( psz_uri ); + p_history_item->psz_name = strdup( psz_name ); + + return p_history_item; +} + diff --git a/modules/codec/cmml/history.h b/modules/codec/cmml/history.h new file mode 100644 index 0000000000..63f0556712 --- /dev/null +++ b/modules/codec/cmml/history.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * history.h: vlc_history_t (web-browser-like back/forward history) handling + ***************************************************************************** + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef __VLC_HISTORY_H__ +#define __VLC_HISTORY_H__ + +#define XARRAY_EMBED_IN_HOST_C_FILE +#include "xarray.h" + +struct history_item_t +{ + char * psz_name; + char * psz_uri; +}; + +struct history_t +{ + unsigned int i_index; /* current index into history */ + XArray * p_xarray; +}; + +typedef struct history_item_t history_item_t; +typedef struct history_t history_t; + + +/***************************************************************************** + * Exported prototypes + *****************************************************************************/ +history_t * history_New (); +vlc_bool_t history_GoBackSavingCurrentItem ( history_t *, + history_item_t * ); +vlc_bool_t history_GoForwardSavingCurrentItem ( history_t *, + history_item_t * ); +vlc_bool_t history_CanGoBack ( history_t * ); +vlc_bool_t history_CanGoForward ( history_t * ); +history_item_t * history_Item ( history_t * ); +void history_Prune ( history_t * ); +void history_PruneAndInsert ( history_t *, + history_item_t * ); +unsigned int history_Count ( history_t * ); +unsigned int history_Index ( history_t * ); + +history_item_t * historyItem_New ( char *, char * ); + +#endif /* __VLC_HISTORY_H__ */ + diff --git a/modules/codec/cmml/intf.c b/modules/codec/cmml/intf.c new file mode 100644 index 0000000000..1907938a82 --- /dev/null +++ b/modules/codec/cmml/intf.c @@ -0,0 +1,835 @@ +/***************************************************************************** + * intf.c: interface for CMML annotations/hyperlinks + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include +#include + +#include +#include +#include +#include + +#include + +#include "stream_control.h" +#include "input_ext-intf.h" +#include "input_ext-dec.h" + +#include "vlc_keys.h" + +#include "browser_open.h" +#include "history.h" +#include "xstrcat.h" +#include "xurl.h" + +#undef CMML_INTF_USE_TIMED_URIS + +#undef CMML_INTF_DEBUG +#undef CMML_INTF_SUBPICTURE_DEBUG +#undef CMML_INTF_HISTORY_DEBUG + +/***************************************************************************** + * intf_sys_t: description and status of interface + *****************************************************************************/ +struct intf_sys_t +{ + decoder_t * p_cmml_decoder; + input_thread_t * p_input; + + vlc_bool_t b_key_pressed; +}; + +struct navigation_history_t +{ + int i_history_size; + int i_last_item; +}; + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ +static int InitThread ( intf_thread_t * ); +static int MouseEvent ( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int KeyEvent ( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static void FollowAnchor ( intf_thread_t * ); +static void GoBack ( intf_thread_t * ); +static void GoForward ( intf_thread_t * ); + +static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * ); +static char *GetTimedURIFragmentForTime ( int ); +static int GetCurrentTimeInSeconds ( input_thread_t * ); +static int DisplayAnchor ( intf_thread_t *, vout_thread_t *, + char *, char * ); +static history_t * GetHistory ( playlist_t * ); +static void ReplacePlaylistItem ( playlist_t *, char * ); + +/* Exported functions */ +static void RunIntf ( intf_thread_t *p_intf ); + +/***************************************************************************** + * OpenIntf: initialize CMML interface + *****************************************************************************/ +int E_(OpenIntf) ( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_this; + + p_intf->p_sys = malloc( sizeof( intf_sys_t ) ); + if( p_intf->p_sys == NULL ) + { + return( 1 ); + }; + + p_intf->pf_run = RunIntf; + + var_AddCallback( p_intf->p_vlc, "key-pressed", KeyEvent, p_intf ); + /* we also need to add the callback for "mouse-clicked", but do that later + * when we've found a p_vout */ + + return( 0 ); +} + +/***************************************************************************** + * CloseIntf: destroy dummy interface + *****************************************************************************/ +void E_(CloseIntf) ( vlc_object_t *p_this ) +{ + intf_thread_t * p_intf = (intf_thread_t *)p_this; + vout_thread_t * p_vout; + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "freeing CMML interface" ); +#endif + + /* Erase the anchor text description from the video output if it exists */ + p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_vout != NULL && p_vout->p_subpicture != NULL ) + { + subpicture_t *p_subpic; + int i_subpic; + + for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ ) + { + p_subpic = &p_vout->p_subpicture[i_subpic]; + + if( p_subpic != NULL && + ( p_subpic->i_status == RESERVED_SUBPICTURE + || p_subpic->i_status == READY_SUBPICTURE ) ) + { + vout_DestroySubPicture( p_vout, p_subpic ); + } + } + } + if( p_vout ) vlc_object_release( p_vout ); + + var_DelCallback( p_intf->p_vlc, "key-pressed", KeyEvent, p_intf ); + + vlc_object_release( p_intf->p_sys->p_cmml_decoder ); + + free( p_intf->p_sys ); +} + + +/***************************************************************************** + * RunIntf: main loop + *****************************************************************************/ +static void RunIntf( intf_thread_t *p_intf ) +{ + vout_thread_t * p_vout = NULL; + + if( InitThread( p_intf ) < 0 ) + { + msg_Err( p_intf, "can't initialize CMML interface" ); + return; + } +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "CMML intf initialized" ); +#endif + + /* if video output is dying, disassociate ourselves from it */ + if( p_vout && p_vout->b_die ) + { + var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf ); + vlc_object_release( p_vout ); + p_vout = NULL; + } + + /* Main loop */ + while( !p_intf->b_die ) + { + vlc_value_t val; + decoder_t *p_cmml_decoder; + + /* find a video output if we currently don't have one */ + if( p_vout == NULL ) + { + p_vout = vlc_object_find( p_intf->p_sys->p_input, + VLC_OBJECT_VOUT, FIND_CHILD ); + if( p_vout ) + { +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "found vout thread" ); +#endif + var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf ); + } + } + + vlc_mutex_lock( &p_intf->change_lock ); + + /* + * keyboard event + */ + if( p_intf->p_sys->b_key_pressed ) + { + vlc_value_t val; + int i, i_action = -1; + struct hotkey *p_hotkeys = p_intf->p_vlc->p_hotkeys; + + /* Find action triggered by hotkey (if any) */ + var_Get( p_intf->p_vlc, "key-pressed", &val ); + + /* Acknowledge that we've handled the b_key_pressed event */ + p_intf->p_sys->b_key_pressed = VLC_FALSE; + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Got a keypress: %d", val.i_int ); +#endif + + for( i = 0; p_hotkeys[i].psz_action != NULL; i++ ) + { + if( p_hotkeys[i].i_key == val.i_int ) + i_action = p_hotkeys[i].i_action; + } + + /* What did the user do? */ + if( i_action != -1 ) + { + switch( i_action ) + { + case ACTIONID_NAV_ACTIVATE: + FollowAnchor( p_intf ); + break; + case ACTIONID_HISTORY_BACK: + GoBack( p_intf ); + break; + case ACTIONID_HISTORY_FORWARD: + GoForward( p_intf ); + break; + default: + break; + } + } + } + + vlc_mutex_unlock( &p_intf->change_lock ); + + /* + * Get a pending anchor description/URL from the CMML decoder + * and display it on screen + */ + p_cmml_decoder = p_intf->p_sys->p_cmml_decoder; + if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val ) + == VLC_SUCCESS ) + { + if( val.p_address ) + { + char *psz_description = NULL; + char *psz_url = NULL; + + psz_description = val.p_address; + + if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) + == VLC_SUCCESS ) + { + psz_url = val.p_address; + } + + if( p_vout != NULL ) + { + if( DisplayAnchor( p_intf, p_vout, psz_description, + psz_url ) != VLC_SUCCESS ) + { + /* text render unsuccessful: do nothing */ + } + else + { + /* text render successful: clear description */ + val.p_address = NULL; + if( var_Set( p_cmml_decoder, + "psz-current-anchor-description", val ) != + VLC_SUCCESS ) + { + msg_Dbg( p_intf, "reset of " + "psz-current-anchor-description failed" ); + } + free( psz_description ); + psz_url = NULL; + } + } + } + } + + /* Wait a bit */ + msleep( INTF_IDLE_SLEEP ); + } + + /* if we're here, the video output is dying: release the vout object */ + + if( p_vout ) + { + var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf ); + vlc_object_release( p_vout ); + } + + vlc_object_release( p_intf->p_sys->p_input ); +} + +/***************************************************************************** + * InitThread: + *****************************************************************************/ +static int InitThread( intf_thread_t * p_intf ) +{ + /* We might need some locking here */ + if( !p_intf->b_die ) + { + input_thread_t * p_input; + decoder_t *p_cmml_decoder; + + p_cmml_decoder = vlc_object_find( p_intf, VLC_OBJECT_DECODER, FIND_PARENT ); + p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT ); + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "cmml decoder at %p, input thread at %p", + p_cmml_decoder, p_input ); +#endif + + /* Maybe the input just died */ + if( p_input == NULL ) + { + return VLC_EGENERIC; + } + + vlc_mutex_lock( &p_intf->change_lock ); + + p_intf->p_sys->p_input = p_input; + p_intf->p_sys->p_cmml_decoder = p_cmml_decoder; + + p_intf->p_sys->b_key_pressed = VLC_FALSE; + + vlc_mutex_unlock( &p_intf->change_lock ); + + return VLC_SUCCESS; + } + else + { + return VLC_EGENERIC; + } +} + +/***************************************************************************** + * MouseEvent: callback for mouse events + *****************************************************************************/ +static int MouseEvent( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + /* TODO: handle mouse clicks on the anchor text */ + + return VLC_SUCCESS; +} + +/***************************************************************************** + * KeyEvent: callback for keyboard events + *****************************************************************************/ +static int KeyEvent( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_data; + vlc_mutex_lock( &p_intf->change_lock ); + + p_intf->p_sys->b_key_pressed = VLC_TRUE; + + vlc_mutex_unlock( &p_intf->change_lock ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * FollowAnchor: follow the current anchor being displayed to the user + *****************************************************************************/ +static void FollowAnchor ( intf_thread_t *p_intf ) +{ + intf_sys_t *p_sys; + decoder_t *p_cmml_decoder; + char *psz_url = NULL; + vlc_value_t val; + + msg_Dbg( p_intf, "User followed anchor" ); + + p_sys = p_intf->p_sys; + p_cmml_decoder = p_sys->p_cmml_decoder; + + if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) == + VLC_SUCCESS ) + { + if( val.p_address ) psz_url = val.p_address; + } + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Current URL is \"%s\"", psz_url ); +#endif + + if( psz_url ) + { + playlist_t *p_playlist; + playlist_item_t *p_current_item; + char *psz_uri_to_load; + + p_playlist = (playlist_t *) vlc_object_find( p_intf, + VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); + if ( !p_playlist ) + { + msg_Warn( p_intf, "can't find playlist" ); + return; + } + + /* Get new URL */ + p_current_item = p_playlist->pp_items[p_playlist->i_index]; +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Current playlist item URL is \"%s\"", + p_current_item->psz_uri ); +#endif + + psz_uri_to_load = XURL_Concat( p_current_item->input.psz_uri, + psz_url ); + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "URL to load is \"%s\"", psz_uri_to_load ); +#endif + + mtime_t i_seconds; + vlc_value_t time; + if( var_Get( p_intf->p_sys->p_input, "time", &time ) ) + { + msg_Dbg( p_intf, "couldn't get time from current clip" ); + time.i_time = 0; + } + i_seconds = time.i_time / 1000000; +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Current time is \"%lld\"", i_seconds ); +#endif + + /* TODO: we need a (much) more robust way of detecting whether + * the file's a media file ... */ + if( strstr( psz_uri_to_load, ".anx" ) != NULL ) + { + history_t *p_history = NULL; + history_item_t *p_history_item = NULL; + char *psz_timed_url; + + p_history = GetHistory( p_playlist ); + + /* create history item */ + psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item ); + p_history_item = historyItem_New( psz_timed_url, psz_timed_url ); + free( psz_timed_url ); + + if( !p_history_item ) + { + msg_Warn( p_intf, "could not initialise history item" ); + } + else + { +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "history pre-index %d", p_history->i_index ); +#endif + history_PruneAndInsert( p_history, p_history_item ); +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "new history item at %p, uri is \"%s\"", + p_history_item, p_history_item->psz_uri ); + msg_Dbg( p_intf, "history index now %d", p_history->i_index ); +#endif + } + + /* free current-anchor-url */ + free( psz_url ); + val.p_address = NULL; + if( var_Set( p_cmml_decoder, "psz-current-anchor-url", val ) != + VLC_SUCCESS ) + { + msg_Dbg( p_intf, "couldn't reset psz-current-anchor-url" ); + } + + ReplacePlaylistItem( p_playlist, psz_uri_to_load ); + } + else + { + (void) browser_Open( psz_url ); + playlist_Command( p_playlist, PLAYLIST_PAUSE, 0 ); + } + + free( psz_uri_to_load ); + + vlc_object_release( p_playlist ); + } +} + +static +char *GetTimedURLFromPlaylistItem( intf_thread_t *p_intf, + playlist_item_t *p_current_item ) +{ +#ifdef CMML_INTF_USE_TIMED_URIS + char *psz_url = NULL; + char *psz_return_value = NULL; + char *psz_seconds = NULL; + int i_seconds; + + psz_url = XURL_GetWithoutFragment( p_current_item->input->psz_uri ); + + /* Get current time as a string */ + if( XURL_IsFileURL( psz_url ) == VLC_TRUE ) + psz_url = xstrcat( psz_url, "#" ); + else + psz_url = xstrcat( psz_url, "?" ); + + /* jump back to 2 seconds before where we are now */ + i_seconds = GetCurrentTimeInSeconds( p_intf->p_sys->p_input ) - 2; + psz_seconds = GetTimedURIFragmentForTime( i_seconds < 0 ? 0 : i_seconds ); + if( psz_seconds ) + { + psz_url = xstrcat( psz_url, psz_seconds ); + free( psz_seconds ); + psz_return_value = psz_url; + } + + return psz_return_value; +#else + void *p; + + /* Suppress warning messages about unused functions */ + p = GetTimedURIFragmentForTime; /* unused */ + p = GetCurrentTimeInSeconds; /* unused */ + + return strdup( p_current_item->input.psz_uri ); +#endif +} + + +/* + * Get the current time, rounded down to the nearest second + * + * http://www.ietf.org/internet-drafts/draft-pfeiffer-temporal-fragments-02.txt + */ +static +int GetCurrentTimeInSeconds( input_thread_t *p_input ) +{ + vlc_value_t time; + mtime_t i_seconds; + + var_Get( p_input, "time", &time ); + i_seconds = time.i_time / 1000000; + + return i_seconds; +} + +static +char *GetTimedURIFragmentForTime( int seconds ) +{ + char *psz_time; + + asprintf( &psz_time, "%d", seconds ); + + return psz_time; +} + +static +void GoBack( intf_thread_t *p_intf ) +{ + vlc_value_t history; + history_t *p_history = NULL; + history_item_t *p_history_item = NULL; + history_item_t *p_new_history_item = NULL; + playlist_t *p_playlist = NULL; + char *psz_timed_url = NULL; + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Going back in navigation history" ); +#endif + + /* Find the playlist */ + p_playlist = (playlist_t *) vlc_object_find( p_intf, + VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); + if ( !p_playlist ) + { + msg_Warn( p_intf, "can't find playlist" ); + return; + } + + /* Retrieve navigation history from playlist */ + if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS || + !history.p_address ) + { + /* History doesn't exist yet: ignore user's request */ + msg_Warn( p_intf, "can't go back: no history exists yet" ); + vlc_object_release( p_playlist ); + return; + } + + p_history = history.p_address; +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "back: nav history retrieved from %p", p_history ); + msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index, + p_history->p_xarray ); +#endif + + /* Check whether we can go back in the history */ + if( history_CanGoBack( p_history ) == VLC_FALSE ) + { + msg_Warn( p_intf, "can't go back: already at beginning of history" ); + vlc_object_release( p_playlist ); + return; + } + + playlist_item_t *p_current_item; + p_current_item = p_playlist->pp_items[p_playlist->i_index]; + + /* Save the currently-playing media in a new history item */ + psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item ); + p_new_history_item = historyItem_New( psz_timed_url, psz_timed_url ); + free( psz_timed_url ); + + if( !p_new_history_item ) + { +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "back: could not initialise new history item" ); +#endif + vlc_object_release( p_playlist ); + return; + } + + /* Go back in the history, saving the currently-playing item */ + (void) history_GoBackSavingCurrentItem( p_history, p_new_history_item ); + p_history_item = history_Item( p_history ); + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index ); + msg_Dbg( p_intf, "got previous history item: %p", p_history_item ); + msg_Dbg( p_intf, "prev history item URL: \"%s\"", p_history_item->psz_uri ); +#endif + + ReplacePlaylistItem( p_playlist, p_history_item->psz_uri ); + vlc_object_release( p_playlist ); +} + +static +void GoForward( intf_thread_t *p_intf ) +{ + vlc_value_t history; + history_t *p_history = NULL; + history_item_t *p_history_item = NULL; + history_item_t *p_new_history_item = NULL; + playlist_t *p_playlist = NULL; + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "Going forward in navigation history" ); +#endif + + /* Find the playlist */ + p_playlist = (playlist_t *) vlc_object_find( p_intf, + VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); + if ( !p_playlist ) + { + msg_Warn( p_intf, "can't find playlist" ); + return; + } + + /* Retrieve navigation history from playlist */ + if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS || + !history.p_address ) + { + /* History doesn't exist yet: ignore user's request */ + msg_Warn( p_intf, "can't go back: no history exists yet" ); + vlc_object_release( p_playlist ); + return; + } + + p_history = history.p_address; +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "forward: nav history retrieved from %p", p_history ); + msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index, + p_history->p_xarray ); +#endif + + /* Check whether we can go forward in the history */ + if( history_CanGoForward( p_history ) == VLC_FALSE ) + { + msg_Warn( p_intf, "can't go forward: already at end of history" ); + vlc_object_release( p_playlist ); + return; + } + + /* Save the currently-playing media in a new history item */ + p_new_history_item = malloc( sizeof(history_item_t) ); + if( !p_new_history_item ) + { +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "forward: could not initialise new history item" ); +#endif + vlc_object_release( p_playlist ); + return; + } + playlist_item_t *p_current_item; + p_current_item = p_playlist->pp_items[p_playlist->i_index]; + p_new_history_item->psz_uri = GetTimedURLFromPlaylistItem( p_intf, + p_current_item ); + p_new_history_item->psz_name = p_new_history_item->psz_uri; + + /* Go forward in the history, saving the currently-playing item */ + (void) history_GoForwardSavingCurrentItem( p_history, p_new_history_item ); + p_history_item = history_Item( p_history ); + +#ifdef CMML_INTF_DEBUG + msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index ); + msg_Dbg( p_intf, "got next history item: %p", p_history_item ); + msg_Dbg( p_intf, "next history item URL: \"%s\"", p_history_item->psz_uri ); +#endif + + ReplacePlaylistItem( p_playlist, p_history_item->psz_uri ); + vlc_object_release( p_playlist ); +} + +static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri ) +{ + playlist_Stop( p_playlist ); + (void) playlist_Add( p_playlist, psz_uri, psz_uri, + PLAYLIST_REPLACE, p_playlist->i_index ); + playlist_Goto( p_playlist, p_playlist->i_index ); +} + +/**************************************************************************** + * DisplayAnchor: displays an anchor on the given video output + ****************************************************************************/ +static int DisplayAnchor( intf_thread_t *p_intf, + vout_thread_t *p_vout, + char *psz_anchor_description, + char *psz_anchor_url ) +{ + int i_margin_h, i_margin_v; + mtime_t i_now; + + i_margin_h = 0; + i_margin_v = 10; + + i_now = mdate(); + + if( p_vout ) + { + text_style_t *p_style = NULL; + + text_style_t blue_with_underline = default_text_style; + blue_with_underline.b_underline = VLC_TRUE; + blue_with_underline.i_color = 0x22ff22; + + if( psz_anchor_url ) + { + /* Should display subtitle underlined and in blue, + * but it looks like VLC doesn't implement any + * text styles yet. D'oh! */ + p_style = &blue_with_underline; + + } + + /* TODO: p_subpicture doesn't have the proper i_x and i_y + * coordinates. Need to look at the subpicture display system to + * work out why. */ + if ( vout_ShowTextAbsolute( p_vout, + psz_anchor_description, p_style, OSD_ALIGN_BOTTOM, + i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS ) + { + /* Displayed successfully */ +#ifdef CMML_INTF_SUBPICTURE_DEBUG + msg_Dbg( p_intf, "subpicture created at (%d, %d) (%d, %d)", + p_subpicture->i_x, p_subpicture->i_y, + p_subpicture->i_width, p_subpicture->i_height ); +#endif + } + else + { + return VLC_EGENERIC; + } + + } + else + { + msg_Dbg( p_intf, "DisplayAnchor couldn't find a video output" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +static history_t * GetHistory( playlist_t *p_playlist ) +{ + vlc_value_t val; + history_t *p_history = NULL; + + if( var_Get( p_playlist, "navigation-history", &val ) != VLC_SUCCESS ) + { + /* history doesn't exist yet: need to create it */ + history_t *new_history = history_New(); + val.p_address = new_history; + var_Create( p_playlist, "navigation-history", + VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT ); + if( var_Set( p_playlist, "navigation-history", val ) != VLC_SUCCESS ) + { + msg_Warn( p_playlist, "could not initialise history" ); + } + else + { + p_history = new_history; +#ifdef CMML_INTF_HISTORY_DEBUG + msg_Dbg( p_playlist, "nav history created at %p", new_history ); + msg_Dbg( p_playlist, "nav history index:%d, p_xarray:%p", + p_history->i_index, p_history->p_xarray ); +#endif + } + } + else + { + p_history = val.p_address; +#ifdef CMML_INTF_HISTORY_DEBUG + msg_Dbg( p_playlist, "nav history retrieved from %p", p_history ); +#endif + } + + return p_history; +} + diff --git a/modules/codec/cmml/xarray.c b/modules/codec/cmml/xarray.c new file mode 100644 index 0000000000..e0383a79ef --- /dev/null +++ b/modules/codec/cmml/xarray.c @@ -0,0 +1,249 @@ +/************************************************************************* + * xarray.c: Mutable (dynamically growable) array + ************************************************************************* + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + ************************************************************************/ + +#include +#include /* memmove(1) */ + +#include "xarray.h" + +#define XARRAY_ASSERT_NOT_NULL(xarray) \ + { \ + if (xarray == NULL) return XARRAY_ENULLPOINTER; \ + } + +#define XARRAY_BOUNDS_CHECK(xarray, index) \ + { \ + if (index < 0) \ + return XARRAY_ENEGATIVEINDEX; \ + else if (xarray->last_valid_element != -1 && \ + (int) index > xarray->last_valid_element) \ + return XARRAY_EINDEXTOOLARGE; \ + } + +#define XARRAY_GROW_ARRAY(xarray) \ + { \ + xarray->array = (void *) realloc (xarray->array, xarray->size * 2); \ + if (xarray->array == NULL) return XARRAY_ENOMEM; \ + } + +XSTATIC XArray * xarray_New (unsigned int initial_size_hint) +{ + XArray *new_xarray = NULL; + void *inner_array; + unsigned int initial_size; + + new_xarray = (XArray *) malloc (sizeof(XArray)); + if (new_xarray == NULL) return NULL; + + if (initial_size_hint <= 0) + initial_size = XARRAY_DEFAULT_SIZE; + else + initial_size = initial_size_hint; + + inner_array = calloc (initial_size, sizeof(void *)); + + new_xarray->last_valid_element = -1; + new_xarray->size = initial_size; + new_xarray->last_error = 0; + + if (inner_array == NULL) + { + free (new_xarray); + return NULL; + } + + new_xarray->array = inner_array; + + /* Make a dummy reference to other functions, so that we don't get + * warnings about unused functions from the compiler. Ahem :) */ + while (0) + { + void *dummy_reference; + + dummy_reference = xarray_AddObject; + dummy_reference = xarray_InsertObject; + dummy_reference = xarray_RemoveLastObject; + dummy_reference = xarray_RemoveObject; + dummy_reference = xarray_RemoveObjects; + dummy_reference = xarray_RemoveObjectsAfter; + dummy_reference = xarray_ReplaceObject; + + dummy_reference = xarray_ObjectAtIndex; + dummy_reference = xarray_Count; + } + + return new_xarray; +} + +XSTATIC int xarray_ObjectAtIndex (XArray *xarray, unsigned int index, + void **out_object) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + XARRAY_BOUNDS_CHECK (xarray, index); + + *out_object = xarray->array[index]; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_AddObject (XArray *xarray, void *object) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + + ++xarray->last_valid_element; + if (xarray->last_valid_element >= (int) xarray->size) + { + XARRAY_GROW_ARRAY (xarray); + } + + xarray->array[xarray->last_valid_element] = object; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_InsertObject (XArray *xarray, void *object, + unsigned int at_index) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + ++xarray->last_valid_element; + XARRAY_BOUNDS_CHECK (xarray, at_index); + if (xarray->last_valid_element >= (int) xarray->size) + { + XARRAY_GROW_ARRAY (xarray); + } + + /* Shift everything from a[i] onward one pointer forward */ + + if ((int) at_index < xarray->last_valid_element) + { + (void) memmove (&xarray->array[at_index + 1], + &xarray->array[at_index], + (xarray->last_valid_element - at_index) * + sizeof(void *)); + } + + xarray->array[at_index] = object; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_RemoveLastObject (XArray *xarray) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + + if (xarray->last_valid_element == -1) + return XARRAY_EEMPTYARRAY; + + xarray->array[xarray->last_valid_element] = NULL; + --xarray->last_valid_element; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_RemoveObject (XArray *xarray, unsigned int at_index) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + XARRAY_BOUNDS_CHECK (xarray, at_index); + + /* Shift everything from a[i] onward one pointer backward */ + + if ((int) at_index < xarray->last_valid_element) + { + (void) memmove (&xarray->array[at_index], + &xarray->array[at_index + 1], + (xarray->last_valid_element - at_index) * + sizeof(void *)); + } + + xarray->array[xarray->last_valid_element] = NULL; + --xarray->last_valid_element; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_RemoveObjects (XArray *xarray, unsigned int at_index, + int count) +{ + int i; + + XARRAY_ASSERT_NOT_NULL (xarray); + XARRAY_BOUNDS_CHECK (xarray, at_index); + + if (count == 0) return XARRAY_SUCCESS; + + if ((int) at_index + (count - 1) > xarray->last_valid_element) + return XARRAY_ECOUNTOUTOFBOUNDS; + + for (i = 0; i < count; i++) + { + int e = xarray_RemoveObject (xarray, at_index); + if (e != XARRAY_SUCCESS) return e; + } + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_RemoveObjectsAfter (XArray *xarray, unsigned int index) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + XARRAY_BOUNDS_CHECK (xarray, index); + + index++; + + while ((int) index <= xarray->last_valid_element) + { + int e = xarray_RemoveObject (xarray, index); + if (e != XARRAY_SUCCESS) return e; + } + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_ReplaceObject (XArray *xarray, unsigned int index, + void *new_object) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + XARRAY_BOUNDS_CHECK (xarray, index); + + xarray->array[index] = new_object; + + return XARRAY_SUCCESS; +} + +XSTATIC int xarray_Count (XArray *xarray, unsigned int *out_count) +{ + XARRAY_ASSERT_NOT_NULL (xarray); + + *out_count = xarray->last_valid_element + 1; + + return XARRAY_SUCCESS; +} + + +#undef XARRAY_ASSERT_NOT_NULL +#undef XARRAY_BOUNDS_CHECK +#undef XARRAY_GROW_ARRAY + diff --git a/modules/codec/cmml/xarray.h b/modules/codec/cmml/xarray.h new file mode 100644 index 0000000000..141b40f437 --- /dev/null +++ b/modules/codec/cmml/xarray.h @@ -0,0 +1,73 @@ +/************************************************************************* + * xarray.h: Mutable (dynamically growable) array (header file) + ************************************************************************* + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + ************************************************************************/ + +#ifndef __XARRAY_H__ +#define __XARRAY_H__ + +/* define this to 'static' for static linkage */ +#define XSTATIC + +#define XARRAY_DEFAULT_SIZE 69 +#define xarray_malloc malloc + +/* Error codes */ +enum xarray_errors +{ + XARRAY_SUCCESS, XARRAY_ENULLPOINTER, XARRAY_ENEGATIVEINDEX, + XARRAY_EINDEXTOOLARGE, XARRAY_ENOMEM, XARRAY_EEMPTYARRAY, + XARRAY_ECOUNTOUTOFBOUNDS +}; + + +typedef struct +{ + void **array; + int last_valid_element; + unsigned int size; + unsigned int last_error; +} +XArray; + +/* Mutable methods */ +XSTATIC int xarray_AddObject (XArray *xarray, void *object); +XSTATIC int xarray_InsertObject (XArray *xarray, void *object, + unsigned int at_index); +XSTATIC int xarray_RemoveLastObject (XArray *xarray); +XSTATIC int xarray_RemoveObject (XArray *xarray, unsigned int at_index); +XSTATIC int xarray_RemoveObjects (XArray *xarray, unsigned int at_index, + int count); +XSTATIC int xarray_RemoveObjectsAfter (XArray *xarray, unsigned int index); +XSTATIC int xarray_ReplaceObject (XArray *xarray, unsigned int index, + void *new_object); + +/* Immutable methods */ +XSTATIC XArray * xarray_New (); +XSTATIC int xarray_ObjectAtIndex (XArray *xarray, unsigned int index, + void **out_object); +XSTATIC int xarray_Count (XArray *xarray, unsigned int *out_count); + +#endif /* __XARRAY_H__ */ + diff --git a/modules/codec/cmml/xlist.c b/modules/codec/cmml/xlist.c new file mode 100644 index 0000000000..da02b2e57b --- /dev/null +++ b/modules/codec/cmml/xlist.c @@ -0,0 +1,245 @@ +/***************************************************************************** + * xlist.c : a simple doubly linked list in C + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2000-2004 VideoLAN + * + * $Id$ + * + * Authors: Conrad Parker + * Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include + +#include "xlist.h" + +static XList * +xlist_node_new (void * data) +{ + XList * l; + + l = (XList *) malloc (sizeof (XList)); + l->prev = l->next = NULL; + l->data = data; + + return l; +} + +XList * +xlist_new (void) +{ + return NULL; +} + +XList * +xlist_clone (XList * list) +{ + XList * l, * new_list; + + if (list == NULL) return NULL; + new_list = xlist_new (); + + for (l = list; l; l = l->next) { + new_list = xlist_append (new_list, l->data); + } + + return new_list; +} + +XList * +xlist_clone_with (XList * list, XCloneFunc clone) +{ + XList * l, * new_list; + void * new_data; + + if (list == NULL) return NULL; + if (clone == NULL) return xlist_clone (list); + + new_list = xlist_new (); + + for (l = list; l; l = l->next) { + new_data = clone (l->data); + new_list = xlist_append (new_list, new_data); + } + + return new_list; +} + + +XList * +xlist_tail (XList * list) +{ + XList * l; + for (l = list; l; l = l->next) + if (l->next == NULL) return l; + return NULL; +} + +XList * +xlist_prepend (XList * list, void * data) +{ + XList * l = xlist_node_new (data); + + if (list == NULL) return l; + + l->next = list; + list->prev = l; + + return l; +} + +XList * +xlist_append (XList * list, void * data) +{ + XList * l = xlist_node_new (data); + XList * last; + + if (list == NULL) return l; + + last = xlist_tail (list); + if (last) last->next = l; + l->prev = last; + return list; +} + +XList * +xlist_add_before (XList * list, void * data, XList * node) +{ + XList * l, * p; + + if (list == NULL) return xlist_node_new (data); + if (node == NULL) return xlist_append (list, data); + if (node == list) return xlist_prepend (list, data); + + l = xlist_node_new (data); + p = node->prev; + + l->prev = p; + l->next = node; + if (p) p->next = l; + node->prev = l; + + return list; +} + +XList * +xlist_add_after (XList * list, void * data, XList * node) +{ + XList * l, * n; + + if (node == NULL) return xlist_prepend (list, data); + + l = xlist_node_new (data); + n = node->next; + + l->prev = node; + l->next = n; + if (n) n->prev = l; + node->next = l; + + return list; +} + +XList * +xlist_find (XList * list, void * data) +{ + XList * l; + + for (l = list; l; l = l->next) + if (l->data == data) return l; + + return NULL; +} + +XList * +xlist_remove (XList * list, XList * node) +{ + if (node == NULL) return list; + + if (node->prev) node->prev->next = node->next; + if (node->next) node->next->prev = node->prev; + + if (node == list) return list->next; + else return list; +} + +int +xlist_length (XList * list) +{ + XList * l; + int c = 0; + + for (l = list; l; l = l->next) + c++; + + return c; +} + +int +xlist_is_empty (XList * list) +{ + return (list == NULL); +} + +int +xlist_is_singleton (XList * list) +{ + if (list == NULL) return 0; + if (list->next == NULL) return 1; + else return 0; +} + +/* + * xlist_free_with (list, free_func) + * + * Step through list 'list', freeing each node using free_func(), and + * also free the list structure itself. + */ +XList * +xlist_free_with (XList * list, XFreeFunc free_func) +{ + XList * l, * ln; + + for (l = list; l; l = ln) { + ln = l->next; + free_func (l->data); + free (l); + } + + return NULL; +} + +/* + * xlist_free (list) + * + * Free the list structure 'list', but not its nodes. + */ +XList * +xlist_free (XList * list) +{ + XList * l, * ln; + + for (l = list; l; l = ln) { + ln = l->next; + free (l); + } + + return NULL; +} + diff --git a/modules/codec/cmml/xlist.h b/modules/codec/cmml/xlist.h new file mode 100644 index 0000000000..441345dfb4 --- /dev/null +++ b/modules/codec/cmml/xlist.h @@ -0,0 +1,168 @@ +/***************************************************************************** + * xlist.h : a simple doubly linked list in C (header file) + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2000-2004 VideoLAN + * + * $Id$ + * + * Authors: Conrad Parker + * Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + + +#ifndef __XLIST__ +#define __XLIST__ + +/** + * A doubly linked list + */ +typedef struct _XList XList; + +struct _XList { + XList * prev; + XList * next; + void * data; +}; + +/** + * Signature of a cloning function. + */ +typedef void * (*XCloneFunc) (void * data); + +/** + * Signature of a freeing function. + */ +typedef void * (*XFreeFunc) (void * data); + +/** Create a new list + * \return a new list + */ +XList * xlist_new (void); + +/** + * Clone a list using the default clone function + * \param list the list to clone + * \returns a newly cloned list + */ +XList * xlist_clone (XList * list); + +/** + * Clone a list using a custom clone function + * \param list the list to clone + * \param clone the function to use to clone a list item + * \returns a newly cloned list + */ +XList * xlist_clone_with (XList * list, XCloneFunc clone); + +/** + * Return the tail element of a list + * \param list the list + * \returns the tail element + */ +XList * xlist_tail (XList * list); + +/** + * Prepend a new node to a list containing given data + * \param list the list + * \param data the data element of the newly created node + * \returns the new list head + */ +XList * xlist_prepend (XList * list, void * data); + +/** + * Append a new node to a list containing given data + * \param list the list + * \param data the data element of the newly created node + * \returns the head of the list + */ +XList * xlist_append (XList * list, void * data); + +/** + * Add a new node containing given data before a given node + * \param list the list + * \param data the data element of the newly created node + * \param node the node before which to add the newly created node + * \returns the head of the list (which may have changed) + */ +XList * xlist_add_before (XList * list, void * data, XList * node); + +/** + * Add a new node containing given data after a given node + * \param list the list + * \param data the data element of the newly created node + * \param node the node after which to add the newly created node + * \returns the head of the list + */ +XList * xlist_add_after (XList * list, void * data, XList * node); + +/** + * Find the first node containing given data in a list + * \param list the list + * \param data the data element to find + * \returns the first node containing given data, or NULL if it is not found + */ +XList * xlist_find (XList * list, void * data); + +/** + * Remove a node from a list + * \param list the list + * \param node the node to remove + * \returns the head of the list (which may have changed) + */ +XList * xlist_remove (XList * list, XList * node); + +/** + * Query the number of items in a list + * \param list the list + * \returns the number of nodes in the list + */ +int xlist_length (XList * list); + +/** + * Query if a list is empty, ie. contains no items + * \param list the list + * \returns 1 if the list is empty, 0 otherwise + */ +int xlist_is_empty (XList * list); + +/** + * Query if the list is singleton, ie. contains exactly one item + * \param list the list + * \returns 1 if the list is singleton, 0 otherwise + */ +int xlist_is_singleton (XList * list); + +/** + * Free a list, using a given function to free each data element + * \param list the list + * \param free_func a function to free each data element + * \returns NULL on success + */ +XList * xlist_free_with (XList * list, XFreeFunc free_func); + +/** + * Free a list, using anx_free() to free each data element + * \param list the list + * \returns NULL on success + */ +XList * xlist_free (XList * list); + +#endif /* __XLIST__ */ + + diff --git a/modules/codec/cmml/xstrcat.h b/modules/codec/cmml/xstrcat.h new file mode 100644 index 0000000000..1c9ef6f7a1 --- /dev/null +++ b/modules/codec/cmml/xstrcat.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * xstrcat.h: strcat with realloc + ***************************************************************************** + * Copyright (C) 2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef __XSTRCAT_H__ +#define __XSTRCAT_H__ + +/* like strcat, but realloc's enough memory for the new string too */ + +static inline +char *xstrcat( char *psz_string, char *psz_to_append ) +{ + size_t i_new_string_length = strlen( psz_string ) + + strlen( psz_to_append ) + 1; + + psz_string = (char *) realloc( psz_string, i_new_string_length ); + + return strcat( psz_string, psz_to_append ); +} + +#endif /* __XSTRCAT_H__ */ + diff --git a/modules/codec/cmml/xtag.c b/modules/codec/cmml/xtag.c new file mode 100644 index 0000000000..a9c3a4249e --- /dev/null +++ b/modules/codec/cmml/xtag.c @@ -0,0 +1,692 @@ +/***************************************************************************** + * xlist.c : a trivial parser for XML-like tags + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2000-2004 VideoLAN + * + * $Id$ + * + * Authors: Conrad Parker + * Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#undef XTAG_DEBUG + +#undef FALSE +#undef TRUE + +#define FALSE (0) +#define TRUE (!FALSE) + +#undef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) + +#undef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) + +typedef struct _XTag XTag; +typedef struct _XAttribute XAttribute; +typedef struct _XTagParser XTagParser; + +/* + * struct _XTag is kind of a union ... it normally represents a whole + * tag (and its children), but it could alternatively represent some + * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and + * ignore the name, attributes and inner_tags. + */ +struct _XTag { + char * name; + char * pcdata; + XTag * parent; + XList * attributes; + XList * children; + XList * current_child; +}; + +struct _XAttribute { + char * name; + char * value; +}; + +struct _XTagParser { + int valid; /* boolean */ + XTag * current_tag; + char * start; + char * end; +}; + +/* Character classes */ +#define X_NONE 0 +#define X_WHITESPACE 1<<0 +#define X_OPENTAG 1<<1 +#define X_CLOSETAG 1<<2 +#define X_DQUOTE 1<<3 +#define X_SQUOTE 1<<4 +#define X_EQUAL 1<<5 +#define X_SLASH 1<<6 + +static int +xtag_cin (char c, int char_class) +{ + if (char_class & X_WHITESPACE) + if (isspace(c)) return TRUE; + + if (char_class & X_OPENTAG) + if (c == '<') return TRUE; + + if (char_class & X_CLOSETAG) + if (c == '>') return TRUE; + + if (char_class & X_DQUOTE) + if (c == '"') return TRUE; + + if (char_class & X_SQUOTE) + if (c == '\'') return TRUE; + + if (char_class & X_EQUAL) + if (c == '=') return TRUE; + + if (char_class & X_SLASH) + if (c == '/') return TRUE; + + return FALSE; +} + +static int +xtag_index (XTagParser * parser, int char_class) +{ + char * s; + int i; + + s = parser->start; + + for (i = 0; s[i] && s != parser->end; i++) { + if (xtag_cin(s[i], char_class)) return i; + } + + return -1; +} + +static void +xtag_skip_over (XTagParser * parser, int char_class) +{ + char * s; + int i; + + if (!parser->valid) return; + + s = (char *)parser->start; + + for (i = 0; s[i] && s != parser->end; i++) { + if (!xtag_cin(s[i], char_class)) { + parser->start = &s[i]; + return; + } + } + + return; +} + +static void +xtag_skip_whitespace (XTagParser * parser) +{ + xtag_skip_over (parser, X_WHITESPACE); +} + +#if 0 +static void +xtag_skip_to (XTagParser * parser, int char_class) +{ + char * s; + int i; + + if (!parser->valid) return; + + s = (char *)parser->start; + + for (i = 0; s[i] && s != parser->end; i++) { + if (xtag_cin(s[i], char_class)) { + parser->start = &s[i]; + return; + } + } + + return; +} +#endif + +static char * +xtag_slurp_to (XTagParser * parser, int good_end, int bad_end) +{ + char * s, * ret; + int xi; + + if (!parser->valid) return NULL; + + s = parser->start; + + xi = xtag_index (parser, good_end | bad_end); + + if (xi > 0 && xtag_cin (s[xi], good_end)) { + ret = malloc ((xi+1) * sizeof(char)); + strncpy (ret, s, xi); + ret[xi] = '\0'; + parser->start = &s[xi]; + return ret; + } + + return NULL; +} + +static int +xtag_assert_and_pass (XTagParser * parser, int char_class) +{ + char * s; + + if (!parser->valid) return FALSE; + + s = parser->start; + + if (!xtag_cin (s[0], char_class)) { + parser->valid = FALSE; + return FALSE; + } + + parser->start = &s[1]; + + return TRUE; +} + +static char * +xtag_slurp_quoted (XTagParser * parser) +{ + char * s, * ret; + int quote = X_DQUOTE; /* quote char to match on */ + int xi; + + if (!parser->valid) return NULL; + + xtag_skip_whitespace (parser); + + s = parser->start; + + if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE; + + if (!xtag_assert_and_pass (parser, quote)) return NULL; + + s = parser->start; + + for (xi = 0; s[xi]; xi++) { + if (xtag_cin (s[xi], quote)) { + if (!(xi > 1 && s[xi-1] == '\\')) break; + } + } + + ret = malloc ((xi+1) * sizeof(char)); + strncpy (ret, s, xi); + ret[xi] = '\0'; + parser->start = &s[xi]; + + if (!xtag_assert_and_pass (parser, quote)) return NULL; + + return ret; +} + +static XAttribute * +xtag_parse_attribute (XTagParser * parser) +{ + XAttribute * attr; + char * name, * value; + char * s; + + if (!parser->valid) return NULL; + + xtag_skip_whitespace (parser); + + name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG); + + if (name == NULL) return NULL; + + xtag_skip_whitespace (parser); + s = parser->start; + + if (!xtag_assert_and_pass (parser, X_EQUAL)) { +#ifdef XTAG_DEBUG + printf ("xtag: attr failed EQUAL on <%s>\n", name); +#endif + goto err_free_name; + } + + xtag_skip_whitespace (parser); + + value = xtag_slurp_quoted (parser); + + if (value == NULL) { +#ifdef XTAG_DEBUG + printf ("Got NULL quoted attribute value\n"); +#endif + goto err_free_name; + } + + attr = malloc (sizeof (*attr)); + attr->name = name; + attr->value = value; + + return attr; + + err_free_name: + free (name); + + parser->valid = FALSE; + + return NULL; +} + +static XTag * +xtag_parse_tag (XTagParser * parser) +{ + XTag * tag, * inner; + XAttribute * attr; + char * name; + char * pcdata; + char * s; + + if (!parser->valid) return NULL; + + if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) { + tag = malloc (sizeof (*tag)); + tag->name = NULL; + tag->pcdata = pcdata; + tag->parent = parser->current_tag; + tag->attributes = NULL; + tag->children = NULL; + tag->current_child = NULL; + + return tag; + } + + s = parser->start; + + /* if this starts a close tag, return NULL and let the parent take it */ + if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH)) + return NULL; + + if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL; + + name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE); + + if (name == NULL) return NULL; + +#ifdef XTAG_DEBUG + printf ("<%s ...\n", name); +#endif + + tag = malloc (sizeof (*tag)); + tag->name = name; + tag->pcdata = NULL; + tag->parent = parser->current_tag; + tag->attributes = NULL; + tag->children = NULL; + tag->current_child = NULL; + + s = parser->start; + + if (xtag_cin (s[0], X_WHITESPACE)) { + while ((attr = xtag_parse_attribute (parser)) != NULL) { + tag->attributes = xlist_append (tag->attributes, attr); + } + } + + xtag_skip_whitespace (parser); + + s = parser->start; + + if (xtag_cin (s[0], X_CLOSETAG)) { + parser->current_tag = tag; + + xtag_assert_and_pass (parser, X_CLOSETAG); + + while ((inner = xtag_parse_tag (parser)) != NULL) { + tag->children = xlist_append (tag->children, inner); + } + + xtag_skip_whitespace (parser); + + xtag_assert_and_pass (parser, X_OPENTAG); + xtag_assert_and_pass (parser, X_SLASH); + name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE); + if (name) { + if (strcmp (name, tag->name)) { +#ifdef XTAG_DEBUG + printf ("got %s expected %s\n", name, tag->name); +#endif + parser->valid = FALSE; + } + free (name); + } + + xtag_skip_whitespace (parser); + xtag_assert_and_pass (parser, X_CLOSETAG); + + } else { + xtag_assert_and_pass (parser, X_SLASH); + xtag_assert_and_pass (parser, X_CLOSETAG); + } + + + return tag; +} + +XTag * +xtag_free (XTag * xtag) +{ + XList * l; + XAttribute * attr; + XTag * child; + + if (xtag == NULL) return NULL; + + if (xtag->name) free (xtag->name); + if (xtag->pcdata) free (xtag->pcdata); + + for (l = xtag->attributes; l; l = l->next) { + if ((attr = (XAttribute *)l->data) != NULL) { + if (attr->name) free (attr->name); + if (attr->value) free (attr->value); + free (attr); + } + } + xlist_free (xtag->attributes); + + for (l = xtag->children; l; l = l->next) { + child = (XTag *)l->data; + xtag_free (child); + } + xlist_free (xtag->children); + + free (xtag); + + return NULL; +} + +XTag * +xtag_new_parse (const char * s, int n) +{ + XTagParser parser; + XTag * tag, * ttag, * wrapper; + + parser.valid = TRUE; + parser.current_tag = NULL; + parser.start = (char *)s; + + if (n == -1) + parser.end = NULL; + else if (n == 0) + return NULL; + else + parser.end = (char *)&s[n]; + + tag = xtag_parse_tag (&parser); + + if (!parser.valid) { + xtag_free (tag); + return NULL; + } + + if ((ttag = xtag_parse_tag (&parser)) != NULL) { + + if (!parser.valid) { + xtag_free (ttag); + return tag; + } + + wrapper = malloc (sizeof (XTag)); + wrapper->name = NULL; + wrapper->pcdata = NULL; + wrapper->parent = NULL; + wrapper->attributes = NULL; + wrapper->children = NULL; + wrapper->current_child = NULL; + + wrapper->children = xlist_append (wrapper->children, tag); + wrapper->children = xlist_append (wrapper->children, ttag); + + while ((ttag = xtag_parse_tag (&parser)) != NULL) { + + if (!parser.valid) { + xtag_free (ttag); + return wrapper; + } + + wrapper->children = xlist_append (wrapper->children, ttag); + } + return wrapper; + } + + return tag; +} + +char * +xtag_get_name (XTag * xtag) +{ + return xtag ? xtag->name : NULL; +} + +char * +xtag_get_pcdata (XTag * xtag) +{ + XList * l; + XTag * child; + + if (xtag == NULL) return NULL; + + for (l = xtag->children; l; l = l->next) { + child = (XTag *)l->data; + if (child->pcdata != NULL) { + return child->pcdata; + } + } + + return NULL; +} + +char * +xtag_get_attribute (XTag * xtag, char * attribute) +{ + XList * l; + XAttribute * attr; + + if (xtag == NULL) return NULL; + + for (l = xtag->attributes; l; l = l->next) { + if ((attr = (XAttribute *)l->data) != NULL) { + if (!strcmp (attr->name, attribute)) + return attr->value; + } + } + + return NULL; +} + +XTag * +xtag_first_child (XTag * xtag, char * name) +{ + XList * l; + XTag * child; + + if (xtag == NULL) return NULL; + + if ((l = xtag->children) == NULL) return NULL; + + if (name == NULL) { + xtag->current_child = l; + return (XTag *)l->data; + } + + for (; l; l = l->next) { + child = (XTag *)l->data; + + if (!strcmp(child->name, name)) { + xtag->current_child = l; + return child; + } + } + + xtag->current_child = NULL; + + return NULL; +} + +XTag * +xtag_next_child (XTag * xtag, char * name) +{ + XList * l; + XTag * child; + + if (xtag == NULL) return NULL; + + if ((l = xtag->current_child) == NULL) + return xtag_first_child (xtag, name); + + if ((l = l->next) == NULL) + return NULL; + + if (name == NULL) { + xtag->current_child = l; + return (XTag *)l->data; + } + + for (; l; l = l->next) { + child = (XTag *)l->data; + + if (!strcmp(child->name, name)) { + xtag->current_child = l; + return child; + } + } + + xtag->current_child = NULL; + + return NULL; +} + +/* + * This snprints function takes a variable list of char *, the last of + * which must be NULL, and prints each in turn to buf. + * Returns C99-style total length that would have been written, even if + * this is larger than n. + */ +static int +xtag_snprints (char * buf, int n, ...) +{ + va_list ap; + char * s; + int len, to_copy, total = 0; + + va_start (ap, n); + + for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) { + len = strlen (s); + + if ((to_copy = MIN (n, len)) > 0) { + memcpy (buf, s, to_copy); + buf += to_copy; + n -= to_copy; + } + + total += len; + } + + va_end (ap); + + return total; +} + +int +xtag_snprint (char * buf, int n, XTag * xtag) +{ + int nn, written = 0; + XList * l; + XAttribute * attr; + XTag * child; + +#define FORWARD(N) \ + buf += MIN (n, N); \ + n = MAX (n-N, 0); \ + written += N; + + if (xtag == NULL) { + if (n > 0) buf[0] = '\0'; + return 0; + } + + if (xtag->pcdata) { + nn = xtag_snprints (buf, n, xtag->pcdata, NULL); + FORWARD(nn); + + return written; + } + + if (xtag->name) { + nn = xtag_snprints (buf, n, "<", xtag->name, NULL); + FORWARD(nn); + + for (l = xtag->attributes; l; l = l->next) { + attr = (XAttribute *)l->data; + + nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"", + NULL); + FORWARD(nn); + } + + if (xtag->children == NULL) { + nn = xtag_snprints (buf, n, "/>", NULL); + FORWARD(nn); + + return written; + } + + nn = xtag_snprints (buf, n, ">", NULL); + FORWARD(nn); + } + + for (l = xtag->children; l; l = l->next) { + child = (XTag *)l->data; + + nn = xtag_snprint (buf, n, child); + FORWARD(nn); + } + + if (xtag->name) { + nn = xtag_snprints (buf, n, "name, ">", NULL); + FORWARD(nn); + } + + return written; +} + diff --git a/modules/codec/cmml/xtag.h b/modules/codec/cmml/xtag.h new file mode 100644 index 0000000000..7ac6b719ed --- /dev/null +++ b/modules/codec/cmml/xtag.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * xlist.h : a trivial parser for XML-like tags (header file) + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2000-2004 VideoLAN + * + * $Id$ + * + * Authors: Conrad Parker + * Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef __XTAG_H__ +#define __XTAG_H__ + +typedef void XTag; + +XTag * xtag_new_parse (const char * s, int n); + +char * xtag_get_name (XTag * xtag); + +char * xtag_get_pcdata (XTag * xtag); + +char * xtag_get_attribute (XTag * xtag, char * attribute); + +XTag * xtag_first_child (XTag * xtag, char * name); + +XTag * xtag_next_child (XTag * xtag, char * name); + +XTag * xtag_free (XTag * xtag); + +int xtag_snprint (char * buf, int n, XTag * xtag); + +#endif /* __XTAG_H__ */ diff --git a/modules/codec/cmml/xurl.c b/modules/codec/cmml/xurl.c new file mode 100644 index 0000000000..f43c99e8d4 --- /dev/null +++ b/modules/codec/cmml/xurl.c @@ -0,0 +1,490 @@ +/***************************************************************************** + * xurl.c: URL manipulation functions + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include + +#include "xurl.h" + +static char *streallocat( char *psz_string, char *psz_to_append ); + +#ifndef HAVE_STRDUP +static char *xurl_strdup( const char *psz_string ); +#else +#define xurl_strdup strdup +#endif + +static char *XURL_FindHostname ( char *psz_url ); +static char *XURL_FindPath ( char *psz_url ); +static char *XURL_FindFragment ( char *psz_url ); + + +char *XURL_Join( char *psz_url1, char *psz_url2 ) +{ + if( XURL_IsAbsolute( psz_url1 ) ) + return XURL_Concat( psz_url1, psz_url2 ); + else + return XURL_Concat( psz_url2, psz_url1 ); + + return NULL; +} + +/* TODO: replace XURL_Concat's rel/absolute calculation with the one + * specified by RFC2396, and also test it on their test suite :) */ + + +char *XURL_Concat( char *psz_url, char *psz_append ) +{ + char *psz_return_value = NULL; + + if( XURL_IsAbsolute( psz_append ) == XURL_TRUE ) + return strdup( psz_append ); + + if( XURL_IsAbsolute( psz_url ) ) + { + if( XURL_HasAbsolutePath( psz_append ) ) + { + char *psz_concat_url; + + psz_concat_url = XURL_GetSchemeAndHostname( psz_url ); + + psz_concat_url = streallocat( psz_concat_url, psz_append ); +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_Concat: concat is \"%s\"\n", + psz_concat_url ); +#endif + psz_return_value = psz_concat_url; + } + else + { + /* psz_append is a relative URL */ + char *psz_new_url; + + /* strip off last path component */ + psz_new_url = XURL_GetHead( psz_url ); + psz_new_url = streallocat( psz_new_url, psz_append ); + + psz_return_value = psz_new_url; + } + } + else + { + /* not an absolute URL */ + if( XURL_HasAbsolutePath( psz_append ) == XURL_FALSE ) + { + char *psz_new_url = XURL_GetHead( psz_url ); + + psz_new_url = streallocat( psz_new_url, psz_append ); + psz_return_value = psz_new_url; + } + else + { + /* URL to append has an absolute path -- just use that instead */ + psz_return_value = xurl_strdup( psz_append ); + } + } + + return psz_return_value; +} + + +XURL_Bool XURL_IsAbsolute( char *psz_url ) +{ + if( XURL_FindHostname( psz_url ) == NULL ) + { +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_IsAbsolute(%s) returning false\n", psz_url ); +#endif + return XURL_FALSE; + } + else + { +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_IsAbsolute(%s) returning true\n", psz_url ); +#endif + return XURL_TRUE; + } +} + + +XURL_Bool XURL_HasFragment( char *psz_url ) +{ + if( XURL_FindFragment( psz_url ) == NULL ) + return XURL_FALSE; + else + return XURL_TRUE; +} + + +char *XURL_FindHostname( char *psz_url ) +{ + char *psz_return_value = NULL; + + char *psz_scheme_separator = strstr( psz_url, "://" ); + if( psz_scheme_separator != NULL) + { + char *psz_hostname = psz_scheme_separator + strlen( "://" ); + if( *psz_hostname != '\0') psz_return_value = psz_hostname; + +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_FindHostname(%s): returning \"%s\"\n", + psz_url, psz_return_value ); +#endif + } + + return psz_return_value; +} + + +XURL_Bool XURL_HasAbsolutePath( char *psz_url ) +{ +#ifdef XURL_WIN32_PATHING + if( psz_url[0] == '/' || psz_url[0] == '\\' ) +#else + if( psz_url[0] == '/' ) +#endif + return XURL_TRUE; + else + return XURL_FALSE; +} + + +char *XURL_GetHostname( char *psz_url ) +{ + char *psz_return_value = NULL; + char *psz_hostname = XURL_FindHostname( psz_url ); + + if( psz_hostname != NULL ) + { + char *psz_new_hostname; + size_t i_hostname_length; + + char *psz_one_past_end_of_hostname = strchr( psz_hostname, '/' ); + if( psz_one_past_end_of_hostname != NULL) + { + /* Found a '/' after the hostname, so copy characters between + * the hostname and the '/' to a new string */ + i_hostname_length = psz_one_past_end_of_hostname - + psz_hostname; + } + else + { + /* Didn't find a '/', so copy from the start of the hostname + * until the end of the string */ + i_hostname_length = strlen( psz_url ) - ( psz_hostname - psz_url ); + } + + /* Copy hostname to a new string */ + psz_new_hostname = xurl_malloc( i_hostname_length ); + if (psz_new_hostname == NULL) return NULL; + strncpy( psz_new_hostname, psz_hostname, i_hostname_length ); + +#ifdef XURL_DEBUG + fprintf (stderr, "XURL_GetHostname: psz_new_hostname is \"%s\"\n", + psz_new_hostname ); +#endif + psz_return_value = psz_new_hostname; + } + else + { + /* Didn't find a hostname */ + return NULL; + } + + return psz_return_value; +} + + +char *XURL_GetSchemeAndHostname( char *psz_url ) +{ + char *psz_scheme, *psz_hostname, *psz_scheme_and_hostname; + + psz_scheme = XURL_GetScheme( psz_url ); + if( psz_scheme == NULL ) return NULL; + + psz_hostname = XURL_GetHostname( psz_url ); + if( psz_hostname == NULL ) return NULL; + + /* malloc +1 for the terminating '\0' */ + psz_scheme_and_hostname = xurl_malloc( + strlen( psz_scheme ) + strlen( "://" ) + + strlen( psz_hostname ) + 1); + if( psz_scheme_and_hostname == NULL ) return NULL; + (void) strcpy( psz_scheme_and_hostname, psz_scheme ); + (void) strcat( psz_scheme_and_hostname, "://" ); + (void) strcat( psz_scheme_and_hostname, psz_hostname ); + + if (psz_scheme_and_hostname == NULL ) return NULL; + return psz_scheme_and_hostname; +} + +static +char *XURL_FindFragment( char *psz_url ) +{ + char *pc_hash = NULL; + char *pc_return_value = NULL; + + pc_hash = strchr( psz_url, '#' ); + if( pc_hash != NULL ) + { + pc_return_value = pc_hash; + } + + return pc_return_value; +} + + +char *XURL_FindQuery( char *psz_url ) +{ + char *pc_question_mark = NULL; + char *pc_return_value = NULL; + + pc_question_mark = strchr( psz_url, '?' ); + if( pc_question_mark != NULL ) + { + pc_return_value = pc_question_mark; + } + + return pc_return_value; +} + + +char *XURL_GetScheme( char *psz_url ) +{ + char *psz_colon; + size_t i_scheme_length; + char *new_scheme; + + if( XURL_IsAbsolute( psz_url ) == XURL_FALSE ) return strdup( "file" ); + + /* this strchr will always succeed since we have an absolute URL, and thus + * a scheme */ + psz_colon = strchr( psz_url, ':' ); + + i_scheme_length = psz_colon - psz_url; + + new_scheme = xurl_malloc( i_scheme_length ); + if( new_scheme == NULL ) return NULL; + + strncpy( new_scheme, psz_url, i_scheme_length ); + + return new_scheme; +} + + +XURL_Bool XURL_IsFileURL( char *psz_url ) +{ + XURL_Bool b_return_value; + char *psz_scheme = XURL_GetScheme( psz_url ); + + if( strcasecmp( psz_scheme, "file" ) == 0 ) + b_return_value = XURL_TRUE; + else + b_return_value = XURL_FALSE; + + xurl_free( psz_scheme ); + + return b_return_value; +} + +#ifndef HAVE_STRDUP +static +char *xurl_strdup( const char *psz_string ) +{ + size_t i_length; + char *psz_new_string; + + if( !psz_string ) return NULL; + + i_length = strlen( psz_string ) + 1; + psz_new_string = (char *) xurl_malloc( i_length ); + if( psz_new_string == NULL ) return NULL; + + memcpy( psz_new_string, psz_string, i_length ); + + return psz_new_string; +} +#endif + +static +char *XURL_FindPath( char *psz_url ) +{ + char *psz_return_value = NULL; + + if( XURL_IsAbsolute( psz_url ) == XURL_TRUE ) + { + char *psz_start_of_hostname = XURL_FindHostname( psz_url ); + if( psz_start_of_hostname != NULL ) + { + char *psz_start_of_path = strchr( psz_start_of_hostname, '/' ); + psz_return_value = psz_start_of_path; + } + } + else + { + if( XURL_HasAbsolutePath( psz_url ) == XURL_TRUE ) + { + psz_return_value = psz_url; + } + else + { + return xurl_strdup ("."); + } + } + + return psz_return_value; +} + + +char *XURL_GetPath( char *psz_url ) +{ + char *psz_return_value = NULL; + char *psz_path = NULL; + char *pc_question_mark = NULL; + char *pc_fragment = NULL; + + psz_path = xurl_strdup( XURL_FindPath( psz_url ) ); +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_GetPath: XURL_FindPath returning \"%s\"\n", + psz_path ); +#endif + psz_return_value = psz_path; + + pc_question_mark = XURL_FindQuery( psz_path ); + if( pc_question_mark != NULL ) + { + int i_path_length = pc_question_mark - psz_path; + *( psz_path + i_path_length ) = '\0'; + } + + pc_fragment = XURL_FindFragment( psz_path ); + if( pc_fragment != NULL ) + { +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_GetPath: XURL_FindFragment returned \"%s\"\n", + pc_fragment ); +#endif + int i_path_length = pc_fragment - psz_path; + *( psz_path + i_path_length ) = '\0'; + } + +#ifdef XURL_DEBUG + fprintf( stderr, "XURL_GetPath returning \"%s\"\n", psz_return_value ); +#endif + + return psz_return_value; +} + + +char *XURL_GetHead( const char *psz_path ) +{ + char *psz_path_head; + char *pc_last_slash; + + /* kill everything up to the last / (including the /) */ +#ifdef XURL_WIN32_PATHING + /* Windows: Try looking for a \ first; if we don't find one, look for / */ + pc_last_slash = strrchr( psz_path, '\\' ); + if( pc_last_slash == NULL ) + pc_last_slash = strrchr( psz_path, '/' ); +#else + pc_last_slash = strrchr( psz_path, '/' ); +#endif + if( pc_last_slash == NULL ) + { + psz_path_head = xurl_strdup( psz_path ); + } + else + { + size_t i_characters_until_last_slash; + + i_characters_until_last_slash = pc_last_slash - psz_path; + psz_path_head = malloc( + ( i_characters_until_last_slash + 1 ) * sizeof(char) ); + (void) strncpy( psz_path_head, psz_path, + i_characters_until_last_slash + 1 ); + + /* terminate the resulting string with '\0' */ + *(psz_path_head + + i_characters_until_last_slash) = '\0'; + } + + /* append a trailing / */ +#ifdef XURL_WIN32_PATHING + streallocat( psz_path_head, "\\" ); +#else + streallocat( psz_path_head, "/" ); +#endif + + return psz_path_head; +} + + +char *XURL_GetWithoutFragment( char *psz_url ) +{ + char *psz_return_value = NULL; + char *psz_fragment; + + psz_fragment = XURL_FindFragment( psz_url ); + if( psz_fragment == NULL ) + { + psz_return_value = xurl_strdup( psz_url ); + } + else + { + size_t i_pre_fragment_length; + char *psz_without_fragment; + + i_pre_fragment_length = psz_fragment - psz_url; + + psz_without_fragment = xurl_malloc( i_pre_fragment_length + 1 ); + if( psz_without_fragment == NULL ) + { + psz_return_value = NULL; + } + else + { + memcpy( psz_without_fragment, psz_url, i_pre_fragment_length ); + *( psz_without_fragment + i_pre_fragment_length ) = '\0'; + psz_return_value = psz_without_fragment; + } + } + + return psz_return_value; +} + +static +char *streallocat( char *psz_string, char *psz_to_append ) +{ + size_t i_new_string_length = strlen( psz_string ) + + strlen( psz_to_append ) + 1; + + psz_string = (char *) realloc( psz_string, i_new_string_length ); + + return strcat( psz_string, psz_to_append ); +} + diff --git a/modules/codec/cmml/xurl.h b/modules/codec/cmml/xurl.h new file mode 100644 index 0000000000..da8fcd15f8 --- /dev/null +++ b/modules/codec/cmml/xurl.h @@ -0,0 +1,68 @@ +/***************************************************************************** + * xurl.h: URL manipulation functions (header file) + ***************************************************************************** + * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) Australia + * Copyright (C) 2004 VideoLAN + * + * $Id$ + * + * Authors: Andre Pang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef __XURL_H__ +#define __XURL_H__ + +#include + +/* Specialise boolean definitions to VLC's boolean types */ +typedef vlc_bool_t XURL_Bool; +#define XURL_FALSE VLC_FALSE +#define XURL_TRUE VLC_TRUE + +/* Specialise general C functions to VLC's standards */ +#define xurl_malloc malloc +#define xurl_free free + +/* Use DOS/Windows path separators? */ +#ifdef WIN32 +# define XURL_WIN32_PATHING +#else +# undef XURL_WIN32_PATHING +#endif + +/* Debugging */ +#undef XURL_DEBUG + +char * XURL_Join ( char *psz_url1, char *psz_url2 ); +char * XURL_Concat ( char *psz_url, char *psz_append ); + +XURL_Bool XURL_IsAbsolute ( char *psz_url ); +XURL_Bool XURL_HasAbsolutePath ( char *psz_url ); +XURL_Bool XURL_IsFileURL ( char *psz_url ); +XURL_Bool XURL_HasFragment ( char *psz_url ); + +char * XURL_GetHostname ( char *psz_url ); +char * XURL_GetSchemeAndHostname ( char *psz_url ); +char * XURL_GetScheme ( char *psz_url ); +char * XURL_GetPath ( char *psz_url ); +char * XURL_GetWithoutFragment ( char *psz_url ); + +char * XURL_GetHead ( const char *psz_path ); + +#endif /* __XURL_H__ */ + -- 2.39.5