Commit 84cad9ba authored by Andre Pang's avatar Andre Pang

* Added Continuous Media Markup Language (CMML) codec

parent 87a35e06
......@@ -2346,6 +2346,16 @@ AX_ADD_LDFLAGS([subsdec],[${LIBICONV}])
AX_ADD_CPPFLAGS([subsdec],[${INCICONV}])
AX_ADD_PLUGINS([subsdec])
dnl
dnl CMML plugin
dnl
AC_ARG_ENABLE(cmml,
[ --enable-cmml CMML support (default enabled)])
if test "${enable_cmml}" != "no"
then
AX_ADD_PLUGINS([cmml])
fi
dnl
dnl Video Filters
......@@ -3776,6 +3786,7 @@ AC_CONFIG_FILES([
modules/audio_mixer/Makefile
modules/audio_output/Makefile
modules/codec/Makefile
modules/codec/cmml/Makefile
modules/codec/ffmpeg/Makefile
modules/codec/ffmpeg/postprocessing/Makefile
modules/codec/ogt/Makefile
......
List of vlc plugins (221)
$Id: LIST,v 1.19 2004/01/05 13:07:02 zorglub Exp $
$Id$
* a52: A/52 basic parser/packetizer
......@@ -70,6 +70,8 @@ $Id: LIST,v 1.19 2004/01/05 13:07:02 zorglub Exp $
* cinepak: Cinepack video decoder
* clone: Clone video filter
* cmml: Continuous Media Markup Language annotations/hyperlinks decoder
* corba: CORBA control module
......
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
/*****************************************************************************
* 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
}
/*****************************************************************************
* 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__ */
/*****************************************************************************
* 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 );
}
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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__ */
/*****************************************************************************
* 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;
}
/*************************************************************************
* 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
/*************************************************************************
* 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__ */
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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__ */
/*****************************************************************************
* 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__ */
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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__ */
/*****************************************************************************
* 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 );
}
/*****************************************************************************
* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment