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
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
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
* cinepak: Cinepack video decoder
* clone: Clone video filter
+
+ * cmml: Continuous Media Markup Language annotations/hyperlinks decoder
* corba: CORBA control module
--- /dev/null
+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
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#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
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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__ */
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <vlc/vlc.h>
+#include <vlc/decoder.h>
+#include <vlc/intf.h>
+
+#include <osd.h>
+
+#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 <clip> 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 <clip> 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 );
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <vlc/vlc.h>
+
+#include "history.h"
+
+#include "xarray.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h> /* 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;
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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__ */
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <stdlib.h> /* malloc(), free() */
+#include <string.h>
+#include <unistd.h>
+
+#include <vlc/vlc.h>
+#include <vlc/decoder.h>
+#include <vlc/intf.h>
+#include <vlc/vout.h>
+
+#include <osd.h>
+
+#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;
+}
+
--- /dev/null
+/*************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <stdlib.h>
+#include <string.h> /* 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
+
--- /dev/null
+/*************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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__ */
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Conrad.Parker@csiro.au>
+ * Andre Pang <Andre.Pang@csiro.au>
+ *
+ * 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 <stdlib.h>
+
+#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;
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Conrad.Parker@csiro.au>
+ * Andre Pang <Andre.Pang@csiro.au>
+ *
+ * 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__ */
+
+
--- /dev/null
+/*****************************************************************************
+ * xstrcat.h: strcat with realloc
+ *****************************************************************************
+ * Copyright (C) 2004 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) Australia
+ * Copyright (C) 2004 VideoLAN
+ *
+ * $Id$
+ *
+ * Authors: Andre Pang <Andre.Pang@csiro.au>
+ *
+ * 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__ */
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Conrad.Parker@csiro.au>
+ * Andre Pang <Andre.Pang@csiro.au>
+ *
+ * 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 <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <xlist.h>
+
+#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, "</", xtag->name, ">", NULL);
+ FORWARD(nn);
+ }
+
+ return written;
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Conrad.Parker@csiro.au>
+ * Andre Pang <Andre.Pang@csiro.au>
+ *
+ * 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__ */
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 );
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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 <Andre.Pang@csiro.au>
+ *
+ * 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 <vlc/vlc.h>
+
+/* 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__ */
+