Commit 203fc8a6 authored by Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf

Remove CMML module.

No maintainer showed up. It is broken in many ways and there is virtually no streams using that...
parent 7c8b9f86
......@@ -103,6 +103,7 @@ Removed modules:
* opie, qte and qte_main
* opengllayer
* cddax. Use cdda instead
* cmml.
Changes between 1.0.2 and 1.0.3:
......
......@@ -3500,16 +3500,6 @@ AS_IF( [test "${enable_asademux}" = "yes"], [
])
])
dnl
dnl CMML plugin
dnl
AC_ARG_ENABLE(cmml,
[ --enable-cmml CMML support (default enabled)])
if test "${enable_cmml}" != "no"
then
VLC_ADD_PLUGIN([cmml])
fi
dnl
dnl kate decoder plugin
dnl
......@@ -5132,7 +5122,6 @@ AC_CONFIG_FILES([
modules/audio_output/Makefile
modules/codec/Makefile
modules/codec/avcodec/Makefile
modules/codec/cmml/Makefile
modules/codec/dmo/Makefile
modules/codec/shine/Makefile
modules/codec/subtitles/Makefile
......
......@@ -74,7 +74,6 @@ $Id$
* chorus_flanger: variable delay audio filter
* chroma_neon: ARM NEONv1 chroma conversion module
* clone: Clone video filter
* cmml: Continuous Media Markup Language annotations/hyperlinks decoder
* colorthres: Theshold color based on similarity to reference color Video filter
* converter_fixed: Fixed-point audio format conversions
* converter_float: Floating-point audio format conversions
......
SUBDIRS = cmml dmo avcodec shine subtitles spudec wmafixed
SUBDIRS = dmo avcodec shine subtitles spudec wmafixed
SOURCES_a52 = a52.c a52.h
SOURCES_dts = dts.c
SOURCES_flac = flac.c
......
What's CMML?
------------
This is an implementation of the Continuous Media Markup Language
(CMML) for VideoLAN. In short, CMML is a (XML) markup language for
time-continuous data, which of course includes multimedia such as
video and audio. It allows one to annotate a media file with both
structured and unstructured textual data, but one of its distinguishing
features--and what this code implements--is its support for embedding
hyperlinks in media files.
So, while viewing some media (e.g. a radio interview with a band),
you could provide a hyperlink to any URL, including a standard web
page or other media (e.g. the band's home page). The hyperlinks
are active only for specific intervals of time while the media is
playing, so for example during a radio interview, the hyperlinks
can change depending on what questions the interviewer is asking
and topic is being discussed.
For more general information on CMML and its role in the bigger
picture of extending the World Wide Web to properly support multimedia,
see <http://www.annodex.net/overview.html>. For specifications of
CMML, see <http://www.annodex.net/specifications.html>.
Usage
-----
Once you have hyperlinking capability, you take on some of the
capabilities of a web browser, in particular following hyperlinks,
and also maintaining a browsing history where you can go backwards
and forwards between pieces of media you've linked to. So, if you
are viewing a file with CMML markup:
* Hyperlinks are displayed as a subtitle track
* Hyperlinks are followed with the VLC "activate" hotkey (by default,
this is just the Enter key)
* Going back and forward are done with the "history-back" and
"history-forward" keys, by default Cmd-[ and Cmd-] on Mac OS X,
and Ctrl-[ and Ctrl-] on all other platforms.
Until the media browsing history features are made available outside
of the CMML plugin, you can only use the history features while
viewing a file that contains CMML markup: e.g. you cannot navigate
backwards or forward in the history while viewing a standard MPEG
video. This is a limitation which may be removed if the media
browsing code is merged into the VLC core.
Overview of the code
--------------------
First: a lot of this code could be implemented, or should be
implemented, in VLC's core (libvlc) rather than be part of a codec
plugin, either because it's something which really should belong
in the core (e.g. media browsing history, system-indepedent interface
to web browser) or a generally useful thing outside of the codec
plugin (e.g. XML parser, URL handling library). That's well and
good, but changes to libvlc are far-reaching and affect all of VLC,
rather than only affecting a single plugin. It's sensible to
gradually merge this stuff into libvlc on an as-needs basis, rather
than trying to refactor out huge amounts of code at once.
Here's a quick overview of what the files do:
* browser_open.[ch]: A very simple attempt to provide a way to
open the system's web browser.
* history.[ch]: Media browsing history (as described above in
"Usage").
* xstrcat.h: Simple wrapper around strcat(1) which performs a
realloc(1) if needed.
* xarray.[ch]: extensible (growable) array, similar to a Vector in
Java/C++. Could be replaced with a vlc_list_t from libvlc's
src/misc/objects.c if the vlc_list_t API were made public.
* xlist.[ch]: Yet Another Linked List implementation (sorry guys
:), only here because XTag (see below) uses it internally.
* xtag.[ch]: A very simple (but working!), lightweight XML
parser.
* xurl.[ch]: A small URL handling library.
* cmml.c: The actual 'codec' parser, which parses the CMML data
(demuxed from the incoming media stream, of course), and provides
two VLC vars ("psz-current-anchor-description" and
"psz-current-anchor-url") which enable intf plugins to display
the CMML data to the user and let them interact with it.
* intf.c: Enables media browsing functionality by displaying
hyperlinks as a subtitle track, and responding to user
keypresses for following hyperlinks and navigating the media
browsing history.
So, all the files except for cmml.c and intf.c could be made available
and re-used outside of this plugin, but currently are not (for the
reasons given above).
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include "browser_open.h"
#if 0
int browser_Open( const char *psz_url )
{
#ifdef __APPLE__
char *psz_open_commandline;
int i_ret;
if( asprintf( &psz_open_commandline, "/usr/bin/open %s", psz_url ) == -1 )
return -1;
i_ret = system( psz_open_commandline );
free( psz_open_commandline );
return i_ret;
#elif defined( UNDER_CE )
return -1;
#elif defined( WIN32 )
char *psz_open_commandline;
int i_ret;
if( asprintf( &psz_open_commandline, "explorer %s", psz_url ) == -1 )
return -1;
i_ret = system( psz_open_commandline );
free( psz_open_commandline );
return i_ret;
#else
/* Assume we're on a UNIX of some sort */
char *psz_open_commandline;
int i_ret;
/* Debian uses www-browser */
if( asprintf( &psz_open_commandline, "www-browser %s", psz_url ) == -1 )
return -1;
i_ret = system( psz_open_commandline );
free( psz_open_commandline );
if( i_ret == 0 )
return 0;
/* Try mozilla */
if( asprintf( &psz_open_commandline, "mozilla %s", psz_url ) == -1 )
return -1;
i_ret = system( psz_open_commandline );
free( psz_open_commandline );
return i_ret;
#endif
}
#else
int browser_Open( const char *psz_url )
{
(void)psz_url;
return -1;
}
#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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef __BROWSER_OPEN_H__
#define __BROWSER_OPEN_H__
int browser_Open( const 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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_codec.h>
#include <vlc_osd.h>
#include <vlc_charset.h>
#include <vlc_interface.h>
#include "xtag.h"
#undef CMML_DEBUG
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int OpenDecoder ( vlc_object_t * );
static void CloseDecoder ( vlc_object_t * );
static subpicture_t *DecodeBlock ( decoder_t *, block_t ** );
static void ParseText ( decoder_t *, block_t * );
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
decoder_sys_t *OpenIntf( vlc_object_t * );
void CloseIntf( decoder_sys_t * );
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
vlc_module_begin ()
set_description( N_("CMML annotations decoder") )
set_capability( "decoder", 50 )
set_callbacks( OpenDecoder, CloseDecoder )
add_shortcut( "cmml" )
add_submodule ()
set_capability( "interface", 0 )
set_callbacks( OpenIntf, CloseIntf )
add_shortcut( "cmml" )
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;
if( p_dec->fmt_in.i_codec != VLC_CODEC_CMML )
return VLC_EGENERIC;
p_dec->pf_decode_sub = DecodeBlock;
/* 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 );
if( p_input )
{
vlc_value_t val;
#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_dec->p_sys = OpenIntf( VLC_OBJECT(p_dec) );
p_dec->fmt_out.i_cat = SPU_ES;
p_dec->fmt_out.i_codec = 0;
return VLC_SUCCESS;
}
/****************************************************************************
* DecodeBlock: the whole thing
****************************************************************************
* This function must be fed with complete subtitles units.
****************************************************************************/
static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
{
subpicture_t *p_spu;
if( !pp_block || *pp_block == NULL )
{
return NULL;
}
ParseText( p_dec, *pp_block );
block_Release( *pp_block );
*pp_block = NULL;
/* allocate an empty subpicture to return. the actual subpicture
* displaying is done in the DisplayAnchor function in intf.c (called from
* DisplayPendingAnchor, which in turn is called from the main RunIntf
* loop). */
p_spu = decoder_NewSubpicture( p_dec );
if( !p_spu )
{
msg_Dbg( p_dec, "couldn't allocate new subpicture" );
return NULL;
}
return p_spu;
}
/*****************************************************************************
* CloseDecoder: clean up the decoder
*****************************************************************************/
static void CloseDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t *)p_this;
CloseIntf( p_dec->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;
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 )
{
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 )
{
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" );
}
}
free( psz_subtitle );
free( psz_cmml );
free( p_anchor );
free( p_clip_parser );
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_input.h>
#include "history.h"
#include "xarray.h"
#ifdef HAVE_STDLIB_H
# include <stdlib.h> /* malloc() */
#endif
#undef HISTORY_DEBUG
/*****************************************************************************
* Local prototypes
*****************************************************************************/
#ifdef HISTORY_DEBUG
static void history_Dump( history_t *p_history );
#endif
/*****************************************************************************
* Local structure lock
*****************************************************************************/
/*****************************************************************************
* Actual history code
*****************************************************************************/
history_t *history_New( void )
{
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;
}
return p_new_history;
}
bool 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 true;
}
#ifdef HISTORY_DEBUG
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 );
}
}
}
#endif
bool 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 true;
}
else
{
return false;
}
}
bool history_CanGoBack( history_t *p_history )
{
if( p_history->i_index > 0 )
return true;
else
return false;
}
bool history_CanGoForward( history_t *p_history )
{
unsigned int i_count;
if( xarray_Count( p_history->p_xarray, &i_count ) != XARRAY_SUCCESS )
return false;
if( p_history->i_index < i_count )
return true;
else
return 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 )
{
unsigned 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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef __VLC_HISTORY_H__
#define __VLC_HISTORY_H__
#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 ( void );
bool history_GoBackSavingCurrentItem ( history_t *,
history_item_t * );
bool history_GoForwardSavingCurrentItem ( history_t *,
history_item_t * );
bool history_CanGoBack ( history_t * );
bool 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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <vlc_codec.h>
#include <vlc_playlist.h>
#include <vlc_osd.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_HISTORY_DEBUG
/*****************************************************************************
* intf_sys_t: description and status of interface
*****************************************************************************/
typedef struct decoder_sys_t
{
VLC_COMMON_MEMBERS
vlc_mutex_t lock;
decoder_t * p_cmml_decoder;
input_thread_t * p_input;
int i_key_action;
} intf_thread_t;
struct navigation_history_t
{
int i_history_size;
int i_last_item;
};
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
decoder_sys_t *OpenIntf ( vlc_object_t * );
void CloseIntf ( decoder_sys_t * );
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 int FollowAnchorCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int GoBackCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int GoForwardCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * );
#ifdef CMML_INTF_USE_TIMED_URIS
static int GetCurrentTimeInSeconds ( input_thread_t * );
static char *GetTimedURIFragmentForTime ( int );
#endif
static int DisplayAnchor ( intf_thread_t *, vout_thread_t *,
char *, char * );
static int DisplayPendingAnchor ( intf_thread_t *, vout_thread_t * );
static history_t * GetHistory ( playlist_t * );
static void ReplacePlaylistItem ( playlist_t *, char * );
static void *RunIntf ( vlc_object_t * );
/*****************************************************************************
* OpenIntf: initialize CMML interface
*****************************************************************************/
decoder_sys_t *OpenIntf ( vlc_object_t *p_this )
{
decoder_sys_t *p_intf = vlc_object_create( p_this, sizeof( *p_intf ) );
if( p_intf == NULL )
return NULL;
vlc_mutex_init( &p_intf->lock );
var_AddCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
/* we also need to add the callback for "mouse-clicked", but do that later
* when we've found a p_vout */
var_Create( p_intf->p_libvlc, "browse-go-back", VLC_VAR_VOID );
var_AddCallback( p_intf->p_libvlc, "browse-go-back",
GoBackCallback, p_intf );
var_Create( p_intf->p_libvlc, "browse-go-forward", VLC_VAR_VOID );
var_AddCallback( p_intf->p_libvlc, "browse-go-forward",
GoForwardCallback, p_intf );
var_Create( p_intf->p_libvlc, "browse-follow-anchor", VLC_VAR_VOID );
var_AddCallback( p_intf->p_libvlc, "browse-follow-anchor",
FollowAnchorCallback, p_intf );
int ret = vlc_thread_create( p_intf, "cmml", RunIntf, VLC_THREAD_PRIORITY_LOW );
if (ret)
{
CloseIntf( p_intf);
return NULL;
}
return p_intf;
}
/*****************************************************************************
* CloseIntf: destroy dummy interface
*****************************************************************************/
void CloseIntf ( decoder_sys_t *p_intf )
{
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 )
{
/* enable CMML as a subtitle track */
spu_Control( vout_GetSpu( p_vout ), SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
vlc_object_release( p_vout );
}
var_DelCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
vlc_object_kill( p_intf );
vlc_thread_join( p_intf );
vlc_object_release( p_intf->p_cmml_decoder );
vlc_mutex_destroy( &p_intf->lock );
vlc_object_release( p_intf );
}
/*****************************************************************************
* RunIntf: main loop
*****************************************************************************/
static void *RunIntf( vlc_object_t *p_obj )
{
decoder_sys_t *p_intf = (decoder_sys_t *)p_obj;
int canc = vlc_savecancel();
vout_thread_t * p_vout = NULL;
if( InitThread( p_intf ) < 0 )
{
msg_Err( p_intf, "can't initialize CMML interface" );
return NULL;
}
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "CMML intf initialized" );
#endif
/* Main loop */
while( vlc_object_alive (p_intf) )
{
/* if video output is dying, disassociate ourselves from it */
if( p_vout && !vlc_object_alive (p_vout) )
{
var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
vlc_object_release( p_vout );
p_vout = NULL;
}
/* find a video output if we currently don't have one */
if( p_vout == NULL )
{
p_vout = vlc_object_find( p_intf->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->lock );
/*
* keyboard event
*/
switch( p_intf->i_key_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;
}
p_intf->i_key_action = 0;
vlc_mutex_unlock( &p_intf->lock );
(void) DisplayPendingAnchor( p_intf, p_vout );
/* 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_input );
vlc_restorecancel( canc );
return NULL;
}
/*****************************************************************************
* DisplayPendingAnchor: get a pending anchor description/URL from the CMML
* decoder and display it on screen
*****************************************************************************/
static int DisplayPendingAnchor( intf_thread_t *p_intf, vout_thread_t *p_vout )
{
decoder_t *p_cmml_decoder;
char *psz_description = NULL;
char *psz_url = NULL;
vlc_value_t val;
p_cmml_decoder = p_intf->p_cmml_decoder;
if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val )
!= VLC_SUCCESS )
{
return true;
}
if( !val.p_address )
return true;
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 )
{
/* display anchor as subtitle on-screen */
if( DisplayAnchor( p_intf, p_vout, psz_description, psz_url )
!= VLC_SUCCESS )
{
/* text render unsuccessful: do nothing */
return false;
}
/* 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;
}
return true;
}
/*****************************************************************************
* InitThread:
*****************************************************************************/
static int InitThread( intf_thread_t * p_intf )
{
/* We might need some locking here */
if( vlc_object_alive (p_intf) )
{
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->lock );
p_intf->p_input = p_input;
p_intf->p_cmml_decoder = p_cmml_decoder;
p_intf->i_key_action = 0;
vlc_mutex_unlock( &p_intf->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 )
{
VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
VLC_UNUSED(oldval); VLC_UNUSED(newval);
VLC_UNUSED(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 )
{
VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
VLC_UNUSED(oldval); VLC_UNUSED(newval);
intf_thread_t *p_intf = (intf_thread_t *)p_data;
vlc_mutex_lock( &p_intf->lock );
/* FIXME: key presses might get lost here... */
p_intf->i_key_action = newval.i_int;
vlc_mutex_unlock( &p_intf->lock );
return VLC_SUCCESS;
}
/*****************************************************************************
* FollowAnchor: follow the current anchor being displayed to the user
*****************************************************************************/
static void FollowAnchor ( intf_thread_t *p_intf )
{
decoder_t *p_cmml_decoder;
char *psz_url = NULL;
vlc_value_t val;
msg_Dbg( p_intf, "User followed anchor" );
p_cmml_decoder = p_intf->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;
mtime_t i_seconds;
vlc_value_t time;
p_playlist = pl_Hold( p_intf );
/* Get new URL */
p_current_item = playlist_CurrentPlayingItem( p_playlist );
char *psz_uri = input_item_GetURI( p_current_item->p_input );
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "Current playlist item URL is \"%s\"", psz_uri );
#endif
psz_uri_to_load = XURL_Concat( psz_uri, psz_url );
free( psz_uri );
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "URL to load is \"%s\"", psz_uri_to_load );
#endif
if( var_Get( p_intf->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
{
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "calling browser_Open with \"%s\"", psz_url );
#endif
(void) browser_Open( psz_url );
playlist_Control( p_playlist, PLAYLIST_PAUSE, pl_Unlocked, 0 );
}
free( psz_uri_to_load );
pl_Release( p_intf );
}
}
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;
char *psz_uri = input_item_GetURI( p_current_item->p_input );
psz_url = XURL_GetWithoutFragment( psz_uri );
free( psz_uri );
/* Get current time as a string */
if( XURL_IsFileURL( psz_url ) == 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_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
VLC_UNUSED(p_intf);
return input_item_GetURI( p_current_item->p_input );
#endif
}
#ifdef CMML_INTF_USE_TIMED_URIS
/*
* 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;
if( asprintf( &psz_time, "%d", seconds ) == -1 )
return NULL;
return psz_time;
}
#endif
static
int GoBackCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
VLC_UNUSED(oldval); VLC_UNUSED(newval);
intf_thread_t *p_intf = (intf_thread_t *) p_data;
GoBack( p_intf );
return VLC_SUCCESS;
}
static
int GoForwardCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
VLC_UNUSED(oldval); VLC_UNUSED(newval);
intf_thread_t *p_intf = (intf_thread_t *) p_data;
GoForward( p_intf );
return VLC_SUCCESS;
}
static
int FollowAnchorCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
VLC_UNUSED(oldval); VLC_UNUSED(newval);
intf_thread_t *p_intf = (intf_thread_t *) p_data;
FollowAnchor( p_intf );
return VLC_SUCCESS;
}
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;
playlist_item_t *p_current_item;
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "Going back in navigation history" );
#endif
/* Find the playlist */
p_playlist = pl_Hold( p_intf );
/* 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" );
pl_Release( p_intf );
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 ) == false )
{
msg_Warn( p_intf, "can't go back: already at beginning of history" );
pl_Release( p_intf );
return;
}
p_current_item = playlist_CurrentPlayingItem( p_playlist );
/* 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
pl_Release( p_intf );
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 );
pl_Release( p_intf );
}
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;
playlist_item_t *p_current_item;
#ifdef CMML_INTF_DEBUG
msg_Dbg( p_intf, "Going forward in navigation history" );
#endif
/* Find the playlist */
p_playlist = pl_Hold( p_intf );
/* 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" );
pl_Release( p_intf );
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 ) == false )
{
msg_Warn( p_intf, "can't go forward: already at end of history" );
pl_Release( p_intf );
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
pl_Release( p_intf );
return;
}
p_current_item = playlist_CurrentPlayingItem( p_playlist );
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 );
pl_Release( p_intf );
}
static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri )
{
playlist_Stop( p_playlist );
(void) playlist_Add( p_playlist, psz_uri, psz_uri,
PLAYLIST_INSERT /* FIXME: used to be PLAYLIST_REPLACE */, PLAYLIST_END|PLAYLIST_GO, true /* FIXME: p_playlist->status.i_index */,
false);
}
/****************************************************************************
* 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 )
{
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, DEFAULT_CHAN,
psz_anchor_description, NULL, OSD_ALIGN_BOTTOM,
i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS )
{
/* Displayed successfully */
}
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "xarray.h"
#include <vlc_memory.h>
/* local prototypes */
XArray * xarray_New (unsigned int);
#define XARRAY_ASSERT_NOT_NULL(xarray) \
{ \
if (xarray == NULL) return XARRAY_ENULLPOINTER; \
}
#define XARRAY_BOUNDS_CHECK(xarray, index) \
{ \
if (xarray->last_valid_element != -1 && \
(int) index > xarray->last_valid_element) \
return XARRAY_EINDEXTOOLARGE; \
}
#define XARRAY_GROW_ARRAY(xarray) \
{ \
xarray->array = realloc_or_free (xarray->array, xarray->size * 2); \
if (xarray->array == NULL) return XARRAY_ENOMEM; \
}
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;
return new_xarray;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
************************************************************************/
#ifndef __XARRAY_H__
#define __XARRAY_H__
#define XARRAY_DEFAULT_SIZE 69
/* 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 */
int xarray_AddObject (XArray *xarray, void *object);
int xarray_InsertObject (XArray *xarray, void *object,
unsigned int at_index);
int xarray_RemoveLastObject (XArray *xarray);
int xarray_RemoveObject (XArray *xarray, unsigned int at_index);
int xarray_RemoveObjects (XArray *xarray, unsigned int at_index,
int count);
int xarray_RemoveObjectsAfter (XArray *xarray, unsigned int index);
int xarray_ReplaceObject (XArray *xarray, unsigned int index,
void *new_object);
/* Immutable methods */
XArray * xarray_New ();
int xarray_ObjectAtIndex (XArray *xarray, unsigned int index,
void **out_object);
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#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);
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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef __XSTRCAT_H__
#define __XSTRCAT_H__
# include <string.h>
# include <stdlib.h>
/* like strcat, but realloc's enough memory for the new string too */
static inline
char *xstrcat( char *psz_string, const 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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xlist.h"
#include <assert.h>
#undef XTAG_DEBUG
#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;
};
void xtag_free (XTag * 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, const char* attribute);
XTag * xtag_first_child (XTag * xtag, const char * name);
XTag * xtag_next_child (XTag * xtag, char * name);
int xtag_snprint (char * buf, int n, XTag * xtag);
/* 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);
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);
strncpy (ret, s, xi);
ret[xi] = '\0';
parser->start = &s[xi];
if (!xtag_assert_and_pass (parser, quote))
{
free( ret );
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 (name && tag->name && 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;
}
void xtag_free (XTag * xtag)
{
XList * l;
XAttribute * attr;
XTag * child;
if( !xtag )
return;
free( xtag->name );
free( xtag->pcdata );
for( l = xtag->attributes; l; l = l->next) {
if((attr = (XAttribute *)l->data) != NULL) {
free( attr->name );
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 );
}
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, const char * attribute)
{
XList * l;
XAttribute * attr;
if( !xtag )
return NULL;
for( l = xtag->attributes; l; l = l->next )
{
if( ( attr = (XAttribute *)l->data ) != NULL )
{
if( attr->name && attribute && !strcmp( attr->name, attribute ) )
return attr->value;
}
}
return NULL;
}
XTag* xtag_first_child (XTag * xtag, const char * name)
{
XList * l;
XTag * child;
if( !xtag )
return NULL;
if( ( l = xtag->children ) == NULL )
return NULL;
if( !name )
{
xtag->current_child = l;
return (XTag *)l->data;
}
for( ; l; l = l->next )
{
child = (XTag *)l->data;
if( child->name && name && !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 (child->name && name && !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 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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, const char * attribute);
XTag * xtag_first_child (XTag * xtag, const 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-2008 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include "xurl.h"
static char *streallocat( char *psz_string, const char *psz_to_append );
char *XURL_FindQuery ( char *psz_url );
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 );
}
/* 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 ) )
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 ) == 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 = strdup( psz_append );
}
}
return psz_return_value;
}
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 false;
}
else
{
#ifdef XURL_DEBUG
fprintf( stderr, "XURL_IsAbsolute(%s) returning true\n", psz_url );
#endif
return true;
}
}
bool XURL_HasFragment( char *psz_url )
{
if( XURL_FindFragment( psz_url ) == NULL )
return false;
else
return 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;
}
bool XURL_HasAbsolutePath( char *psz_url )
{
#ifdef XURL_WIN32_PATHING
if( psz_url[0] == '/' || psz_url[0] == '\\' )
#else
if( psz_url[0] == '/' )
#endif
return true;
else
return 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 = 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 = NULL,
*psz_hostname = NULL,
*psz_scheme_and_hostname = NULL;
psz_scheme = XURL_GetScheme( psz_url );
psz_hostname = XURL_GetHostname( psz_url );
if( psz_hostname && psz_scheme )
{
if( asprintf( &psz_scheme_and_hostname, "%s://%s", psz_scheme, psz_hostname ) == -1)
psz_scheme_and_hostname = NULL;
}
free( psz_hostname );
free( psz_scheme );
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 ) == 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 = malloc( i_scheme_length );
if( new_scheme == NULL )
return NULL;
strncpy( new_scheme, psz_url, i_scheme_length );
return new_scheme;
}
bool XURL_IsFileURL( char *psz_url )
{
bool b_return_value;
char *psz_scheme = XURL_GetScheme( psz_url );
if( strcasecmp( psz_scheme, "file" ) == 0 )
b_return_value = true;
else
b_return_value = false;
free( psz_scheme );
return b_return_value;
}
static char *XURL_FindPath( char *psz_url )
{
if( XURL_IsAbsolute( psz_url ) )
{
char *psz_start_of_hostname = XURL_FindHostname( psz_url );
if( psz_start_of_hostname != NULL )
return strchr( psz_start_of_hostname, '/' );
else
return NULL;
}
else
{
if( XURL_HasAbsolutePath( psz_url ) == true )
return psz_url;
else
return strdup (".");
}
}
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 = 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 = strdup( psz_path );
}
else
{
size_t i_characters_until_last_slash;
i_characters_until_last_slash = pc_last_slash - psz_path;
psz_path_head = xmalloc( i_characters_until_last_slash + 1 );
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 / */
streallocat( psz_path_head, "/" );
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 = 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 = 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, const char *psz_to_append )
{
size_t i_new_string_length = strlen( psz_string ) +
strlen( psz_to_append ) + 1;
psz_string = xrealloc( 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-2008 the VideoLAN team
*
* $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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef __XURL_H__
#define __XURL_H__
#include <vlc_common.h>
/* 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 );
bool XURL_IsAbsolute ( char *psz_url );
bool XURL_HasAbsolutePath ( char *psz_url );
bool XURL_IsFileURL ( char *psz_url );
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__ */
......@@ -372,21 +372,6 @@ modules/codec/avcodec/video.c
modules/codec/cc.c
modules/codec/cc.h
modules/codec/cdg.c
modules/codec/cmml/browser_open.c
modules/codec/cmml/browser_open.h
modules/codec/cmml/cmml.c
modules/codec/cmml/history.c
modules/codec/cmml/history.h
modules/codec/cmml/intf.c
modules/codec/cmml/xarray.c
modules/codec/cmml/xarray.h
modules/codec/cmml/xlist.c
modules/codec/cmml/xlist.h
modules/codec/cmml/xstrcat.h
modules/codec/cmml/xtag.c
modules/codec/cmml/xtag.h
modules/codec/cmml/xurl.c
modules/codec/cmml/xurl.h
modules/codec/cvdsub.c
modules/codec/dirac.c
modules/codec/dmo/buffer.c
......
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