Commit 756605b7 authored by Laurent Aimar's avatar Laurent Aimar

* vlm.*: move vlm to the core (now, vlm_New create only one instance)

 * http.c: begin support of vlm interraction (not yet documented).
parent c56ae63c
......@@ -313,6 +313,9 @@ typedef struct httpd_stream_t httpd_stream_t;
/* divers */
typedef struct vlc_meta_t vlc_meta_t;
typedef struct vlm_t vlm_t;
typedef struct vlm_message_t vlm_message_t;
/*****************************************************************************
* Variable callbacks
......
......@@ -2,7 +2,7 @@
* vlc_objects.h: vlc_object_t definition.
*****************************************************************************
* Copyright (C) 2002 VideoLAN
* $Id: vlc_objects.h,v 1.22 2004/01/25 18:17:08 zorglub Exp $
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
*
......@@ -47,6 +47,7 @@
#define VLC_OBJECT_PACKETIZER (-13)
#define VLC_OBJECT_ENCODER (-14)
#define VLC_OBJECT_DIALOGS (-15)
#define VLC_OBJECT_VLM (-16)
#define VLC_OBJECT_GENERIC (-666)
......
/*****************************************************************************
* .c: VLM interface plugin
* vlc_vlm.h: VLM interface plugin
*****************************************************************************
* Copyright (C) 2000, 2001 VideoLAN
* $Id$
......@@ -22,6 +22,9 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef _VLC_VLM_H
#define _VLC_VLM_H 1
/* VLM specific - structures and functions */
enum
{
......@@ -83,19 +86,17 @@ typedef struct
The parent node is ( name_of_the_command , NULL ), or
( name_of_the_command , message_error ) on error.
If a node has children, it should not have a value (=NULL).*/
typedef struct vlm_message
struct vlm_message_t
{
char *psz_name;
char *psz_value;
int i_child;
struct vlm_message **child;
} vlm_message_t;
vlm_message_t **child;
};
typedef struct
struct vlm_t
{
VLC_COMMON_MEMBERS
......@@ -114,12 +115,15 @@ typedef struct
int i_schedule;
vlm_schedule_t **schedule;
} vlm_t;
};
#define vlm_New( a ) __vlm_New( VLC_OBJECT(a) )
vlm_t *__vlm_New ( vlc_object_t * );
void vlm_Delete( vlm_t * );
int vlm_ExecuteCommand( vlm_t *, char *, vlm_message_t **);
void vlm_MessageDelete( vlm_message_t* );
VLC_EXPORT( vlm_t *, __vlm_New, ( vlc_object_t * ) );
VLC_EXPORT( void, vlm_Delete, ( vlm_t * ) );
VLC_EXPORT( int, vlm_ExecuteCommand, ( vlm_t *, char *, vlm_message_t **) );
VLC_EXPORT( void, vlm_MessageDelete, ( vlm_message_t* ) );
#endif
......@@ -25,12 +25,9 @@
/*****************************************************************************
* Preamble
*****************************************************************************/
/*
* TODO:
*
/* TODO:
* - clean up ?
* - doc ! (mouarf ;)
*
*/
#include <stdlib.h>
......@@ -41,6 +38,7 @@
#include <vlc/vout.h> /* for fullscreen */
#include "vlc_httpd.h"
#include "vlc_vlm.h"
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
......@@ -66,8 +64,8 @@
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Activate ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
#define HOST_TEXT N_( "Host address" )
#define HOST_LONGTEXT N_( \
......@@ -80,7 +78,7 @@ vlc_module_begin();
add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
add_string ( "http-src", NULL, NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE );
set_capability( "interface", 0 );
set_callbacks( Activate, Close );
set_callbacks( Open, Close );
vlc_module_end();
......@@ -121,6 +119,8 @@ static int HttpCallback( httpd_file_sys_t *p_args,
static char *uri_extract_value( char *psz_uri, char *psz_name,
char *psz_value, int i_value_max );
static int uri_test_param( char *psz_uri, char *psz_name );
static void uri_decode_url_encoded( char *psz );
static char *Find_end_MRL( char *psz );
......@@ -171,6 +171,7 @@ struct intf_sys_t
playlist_t *p_playlist;
input_thread_t *p_input;
vlm_t *p_vlm;
};
......@@ -178,7 +179,7 @@ struct intf_sys_t
/*****************************************************************************
* Activate: initialize and create stuff
*****************************************************************************/
static int Activate( vlc_object_t *p_this )
static int Open( vlc_object_t *p_this )
{
intf_thread_t *p_intf = (intf_thread_t*)p_this;
intf_sys_t *p_sys;
......@@ -213,6 +214,7 @@ static int Activate( vlc_object_t *p_this )
}
p_sys->p_playlist = NULL;
p_sys->p_input = NULL;
p_sys->p_vlm = NULL;
p_sys->p_httpd_host = httpd_HostNew( VLC_OBJECT(p_intf), psz_address, i_port );
if( p_sys->p_httpd_host == NULL )
......@@ -309,6 +311,11 @@ void Close ( vlc_object_t *p_this )
int i;
if( p_sys->p_vlm )
{
vlm_Delete( p_sys->p_vlm );
}
for( i = 0; i < p_sys->i_files; i++ )
{
httpd_FileDelete( p_sys->pp_files[i]->p_file );
......@@ -1110,6 +1117,59 @@ static mvar_t *mvar_FileSetNew( char *name, char *psz_dir )
return s;
}
static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm )
{
mvar_t *s = mvar_New( name, "set" );
vlm_message_t *msg;
int i;
fprintf( stderr," mvar_VlmSetNew: name=`%s'\n", name );
if( vlm == NULL )
return s;
if( vlm_ExecuteCommand( vlm, "show", &msg ) )
{
return s;
}
for( i = 0; i < msg->i_child; i++ )
{
/* Over media, schedule */
vlm_message_t *ch = msg->child[i];
int j;
for( j = 0; j < ch->i_child; j++ )
{
/* Over name */
vlm_message_t *el = ch->child[j];
vlm_message_t *inf, *desc;
mvar_t *set;
char psz[500];
int k;
sprintf( psz, "show %s", el->psz_name );
if( vlm_ExecuteCommand( vlm, psz, &inf ) )
continue;
desc = inf->child[0];
/* Add a node with name and info */
set = mvar_New( name, "set" );
mvar_AppendNewVar( set, "name", el->psz_name );
for( k = 0; k < desc->i_child; k++ )
{
mvar_AppendNewVar( set, desc->child[k]->psz_name, desc->child[k]->psz_value );
}
vlm_MessageDelete( inf );
mvar_AppendVar( s, set );
}
}
vlm_MessageDelete( msg );
return s;
}
static void SSInit( rpn_stack_t * );
static void SSClean( rpn_stack_t * );
static void EvaluateRPN( mvar_t *, rpn_stack_t *, char * );
......@@ -1260,6 +1320,15 @@ enum macroType
MVLC_CLOSE,
MVLC_SHUTDOWN,
MVLC_VLM_NEW,
MVLC_VLM_SETUP,
MVLC_VLM_DEL,
MVLC_VLM_PLAY,
MVLC_VLM_PAUSE,
MVLC_VLM_STOP,
MVLC_VLM_SEEK,
MVLC_FOREACH,
MVLC_IF,
MVLC_RPN,
......@@ -1304,6 +1373,15 @@ StrToMacroTypeTab [] =
{ "close", MVLC_CLOSE },
{ "shutdown", MVLC_SHUTDOWN },
/* vlm control */
{ "vlm_new", MVLC_VLM_NEW },
{ "vlm_setup", MVLC_VLM_SETUP },
{ "vlm_del", MVLC_VLM_DEL },
{ "vlm_play", MVLC_VLM_PLAY },
{ "vlm_pause", MVLC_VLM_PAUSE },
{ "vlm_stop", MVLC_VLM_STOP },
{ "vlm_seek", MVLC_VLM_SEEK },
{ "rpn", MVLC_RPN },
{ "foreach", MVLC_FOREACH },
......@@ -1843,6 +1921,104 @@ static void MacroDo( httpd_file_sys_t *p_args,
p_intf->p_vlc->b_die = VLC_TRUE;
break;
}
/* vlm */
case MVLC_VLM_NEW:
case MVLC_VLM_SETUP:
{
static const char *vlm_properties[11] =
{
"input", "output", "option", "enabled", "disabled",
"loop", "unloop", "append", "date", "period", "repeat",
};
vlm_message_t *vlm_answer;
char name[512];
char *psz = malloc( strlen( p_request ) + 1000 );
char *p = psz;
int i;
if( p_intf->p_sys->p_vlm == NULL )
p_intf->p_sys->p_vlm = vlm_New( p_intf );
uri_extract_value( p_request, "name", name, 512 );
if( StrToMacroType( control ) == MVLC_VLM_NEW )
{
char type[20];
uri_extract_value( p_request, "type", type, 20 );
p += sprintf( psz, "new %s %s", name, type );
}
else
{
p += sprintf( psz, "setup %s", name );
}
/* Parse the request */
for( i = 0; i < 11; i++ )
{
char val[512];
uri_extract_value( p_request, vlm_properties[i], val, 512 );
if( strlen( val ) > 0 )
{
p += sprintf( p, " %s %s", vlm_properties[i], val );
}
else if( uri_test_param( p_request, vlm_properties[i] ) )
{
p += sprintf( p, " %s", vlm_properties[i] );
}
}
vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
/* FIXME do a vlm_answer -> var stack conversion */
vlm_MessageDelete( vlm_answer );
free( psz );
break;
}
case MVLC_VLM_DEL:
{
vlm_message_t *vlm_answer;
char name[512];
char psz[512+10];
if( p_intf->p_sys->p_vlm == NULL )
p_intf->p_sys->p_vlm = vlm_New( p_intf );
uri_extract_value( p_request, "name", name, 512 );
sprintf( psz, "del %s", name );
vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
/* FIXME do a vlm_answer -> var stack conversion */
vlm_MessageDelete( vlm_answer );
break;
}
case MVLC_VLM_PLAY:
case MVLC_VLM_PAUSE:
case MVLC_VLM_STOP:
case MVLC_VLM_SEEK:
{
vlm_message_t *vlm_answer;
char name[512];
char psz[512+10];
if( p_intf->p_sys->p_vlm == NULL )
p_intf->p_sys->p_vlm = vlm_New( p_intf );
uri_extract_value( p_request, "name", name, 512 );
if( StrToMacroType( control ) == MVLC_VLM_PLAY )
sprintf( psz, "control %s play", name );
else if( StrToMacroType( control ) == MVLC_VLM_PAUSE )
sprintf( psz, "control %s pause", name );
else if( StrToMacroType( control ) == MVLC_VLM_STOP )
sprintf( psz, "control %s stop", name );
else if( StrToMacroType( control ) == MVLC_VLM_SEEK )
{
char percent[20];
uri_extract_value( p_request, "percent", percent, 512 );
sprintf( psz, "control %s seek %s", name, percent );
}
vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
/* FIXME do a vlm_answer -> var stack conversion */
vlm_MessageDelete( vlm_answer );
break;
}
default:
PRINTS( "<!-- control param(%s) unsuported -->", control );
break;
......@@ -1939,6 +2115,7 @@ static void MacroDo( httpd_file_sys_t *p_args,
case MVLC_RPN:
EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
break;
case MVLC_UNKNOWN:
default:
PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
......@@ -2112,6 +2289,14 @@ static void Execute( httpd_file_sys_t *p_args,
{
index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input );
}
else if( !strcmp( m.param2, "vlm" ) )
{
if( p_intf->p_sys->p_vlm == NULL )
{
p_intf->p_sys->p_vlm = vlm_New( p_intf );
}
index = mvar_VlmSetNew( m.param1, p_intf->p_sys->p_vlm );
}
#if 0
else if( !strcmp( m.param2, "hosts" ) )
{
......@@ -2316,6 +2501,22 @@ static int HttpCallback( httpd_file_sys_t *p_args,
/****************************************************************************
* uri parser
****************************************************************************/
static int uri_test_param( char *psz_uri, char *psz_name )
{
char *p = psz_uri;
while( (p = strstr( p, psz_name )) )
{
/* Verify that we are dealing with a post/get argument */
if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
{
return VLC_TRUE;
}
p++;
}
return VLC_FALSE;
}
static char *uri_extract_value( char *psz_uri, char *psz_name,
char *psz_value, int i_value_max )
{
......
......@@ -64,7 +64,7 @@
#include "network.h"
#include "vlm.h"
#include "vlc_vlm.h"
#if defined( WIN32 ) || defined( UNDER_CE )
#define SOCKET_CLOSE(a) closesocket(a)
......
SOURCES_telnet = telnet.c vlm.c vlm.h
......@@ -2,7 +2,7 @@
* objects.c: vlc_object_t handling
*****************************************************************************
* Copyright (C) 2004 VideoLAN
* $Id: objects.c,v 1.46 2004/03/03 13:25:24 fenrir Exp $
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
*
......@@ -52,6 +52,7 @@
#include "vlc_codec.h"
#include "vlc_httpd.h"
#include "vlc_vlm.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
......@@ -155,6 +156,10 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type )
i_size = sizeof( httpd_t );
psz_type = "http daemon";
break;
case VLC_OBJECT_VLM:
i_size = sizeof( vlm_t );
psz_type = "vlm dameon";
break;
default:
i_size = i_type > 0
? i_type > (int)sizeof(vlc_object_t)
......
......@@ -29,28 +29,28 @@
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include <vlc/input.h>
#ifdef HAVE_TIME_H
# include <time.h> /* ctime() */
#endif
#include "vlm.h"
#include "vlc_vlm.h"
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static char *vlm_Save( vlm_t * );
static int vlm_Load( vlm_t *, char *);
static vlm_media_t *vlm_MediaNew( vlm_t *, char *, int );
static int vlm_MediaDelete( vlm_t *, vlm_media_t *, char * );
static vlm_media_t *vlm_MediaSearch( vlm_t *, char * );
static int vlm_MediaSetup( vlm_media_t *, char *, char * );
static int vlm_MediaControl( vlm_t *, vlm_media_t *, char *, char * );
static vlm_message_t *vlm_Show( vlm_t *, vlm_media_t *, vlm_schedule_t *, char * );
static vlm_message_t *vlm_Help( vlm_t *, char * );
static vlm_media_t *vlm_MediaNew ( vlm_t *, char *, int );
static int vlm_MediaDelete ( vlm_t *, vlm_media_t *, char * );
static vlm_media_t *vlm_MediaSearch ( vlm_t *, char * );
static int vlm_MediaSetup ( vlm_media_t *, char *, char * );
static int vlm_MediaControl( vlm_t *, vlm_media_t *, char *, char * );
static vlm_message_t* vlm_MessageNew( char * , char * );
static vlm_message_t* vlm_MessageAdd( vlm_message_t*, vlm_message_t* );
......@@ -59,10 +59,118 @@ static int vlm_ScheduleDelete( vlm_t *, vlm_schedule_t *, char *);
static int vlm_ScheduleSetup( vlm_schedule_t *, char *, char *);
static vlm_schedule_t *vlm_ScheduleSearch( vlm_t *, char *);
static int ExecuteCommand( vlm_t *, char * , vlm_message_t **);
static int Manage( vlc_object_t* );
/*****************************************************************************
* vlm_New:
*****************************************************************************/
vlm_t *__vlm_New ( vlc_object_t *p_this )
{
vlc_value_t lockval;
vlm_t *vlm = NULL;
/* to be sure to avoid multiple creation */
var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX );
var_Get( p_this->p_libvlc, "vlm_mutex", &lockval );
vlc_mutex_lock( lockval.p_address );
if( !(vlm = vlc_object_find( p_this, VLC_OBJECT_VLM, FIND_ANYWHERE )) )
{
msg_Info( p_this, "creating vlm" );
if( ( vlm = vlc_object_create( p_this, VLC_OBJECT_VLM ) ) == NULL )
{
vlc_mutex_unlock( lockval.p_address );
return NULL;
}
vlc_mutex_init( p_this->p_vlc, &vlm->lock );
vlm->i_media = 0;
vlm->media = NULL;
vlm->i_schedule = 0;
vlm->schedule = NULL;
vlc_object_yield( vlm );
vlc_object_attach( vlm, p_this->p_vlc );
}
vlc_mutex_unlock( lockval.p_address );
if( vlc_thread_create( vlm, "vlm thread",
Manage, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
{
vlc_mutex_destroy( &vlm->lock );
vlc_object_destroy( vlm );
return NULL;
}
return vlm;
}
/*****************************************************************************
* vlm_Delete:
*****************************************************************************/
void vlm_Delete( vlm_t *vlm )
{
vlc_value_t lockval;
int i;
var_Get( vlm->p_libvlc, "vlm_mutex", &lockval );
vlc_mutex_lock( lockval.p_address );
vlc_object_release( vlm );
if( vlm->i_refcount > 0 )
{
vlc_mutex_unlock( lockval.p_address );
return;
}
vlm->b_die = VLC_TRUE;
vlc_thread_join( vlm );
vlc_mutex_destroy( &vlm->lock );
for( i = 0; i < vlm->i_media; i++ )
{
vlm_media_t *media = vlm->media[i];
vlm_MediaDelete( vlm, media, NULL );
}
if( vlm->media ) free( vlm->media );
for( i = 0; i < vlm->i_schedule; i++ )
{
vlm_ScheduleDelete( vlm, vlm->schedule[i], NULL );
}
if( vlm->schedule ) free( vlm->schedule );
vlc_object_detach( vlm );
vlc_object_destroy( vlm );
vlc_mutex_unlock( lockval.p_address );
}
/*****************************************************************************
* vlm_ExecuteCommand:
*****************************************************************************/
int vlm_ExecuteCommand( vlm_t *vlm, char *command, vlm_message_t **message)
{
int result;
vlc_mutex_lock( &vlm->lock );
result = ExecuteCommand( vlm, command, message );
vlc_mutex_unlock( &vlm->lock );
return result;
}
/*****************************************************************************
*
*****************************************************************************/
#if 1
static char *FindEndCommand( char *psz )
{
......@@ -183,19 +291,8 @@ static char *FindEndCommand( char *psz )
#endif
int vlm_ExecuteCommand( vlm_t *vlm, char *command, vlm_message_t **message)
{
int result;
vlc_mutex_lock( &vlm->lock );
result = ExecuteCommand( vlm, command, message );
vlc_mutex_unlock( &vlm->lock );
return result;
}
/* Execute a command which ends by '\0' (string) */
int ExecuteCommand( vlm_t *vlm, char *command , vlm_message_t **p_message)
static int ExecuteCommand( vlm_t *vlm, char *command , vlm_message_t **p_message)
{
int i_return = 0;
int i_command = 0;
......@@ -1009,7 +1106,7 @@ static int vlm_MediaControl( vlm_t *vlm, vlm_media_t *media, char *psz_name, cha
{
int i;
if( media->p_input )
if( media->p_input );
{
input_StopThread( media->p_input );
input_DestroyThread( media->p_input );
......@@ -1690,61 +1787,6 @@ static char *vlm_Save( vlm_t *vlm )
return save;
}
/*****************************************************************************
* vlm_New:
*****************************************************************************/
vlm_t *__vlm_New ( vlc_object_t *p_object )
{
vlm_t *vlm = vlc_object_create( p_object , sizeof( vlm_t ) );
vlc_mutex_init( p_object->p_vlc, &vlm->lock );
vlm->i_media = 0;
vlm->media = NULL;
vlm->i_schedule = 0;
vlm->schedule = NULL;
if( vlc_thread_create( vlm, "vlm thread",
Manage, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
{
vlc_mutex_destroy( &vlm->lock );
vlc_object_destroy( vlm );
return NULL;
}
return vlm;
}
/*****************************************************************************
* vlm_Delete:
*****************************************************************************/
void vlm_Delete( vlm_t *vlm )
{
int i;
vlm->b_die = VLC_TRUE;
vlc_thread_join( vlm );
vlc_mutex_destroy( &vlm->lock );
for( i = 0; i < vlm->i_media; i++ )
{
vlm_media_t *media = vlm->media[i];
vlm_MediaDelete( vlm, media, NULL );
}
if( vlm->media ) free( vlm->media );
for( i = 0; i < vlm->i_schedule; i++ )
{
vlm_ScheduleDelete( vlm, vlm->schedule[i], NULL );
}
if( vlm->schedule ) free( vlm->schedule );
vlc_object_destroy( vlm );
}
static vlm_schedule_t *vlm_ScheduleNew( vlm_t *vlm , char *psz_name )
{
vlm_schedule_t *sched= malloc( sizeof( vlm_schedule_t ));
......@@ -1891,7 +1933,9 @@ static int vlm_ScheduleSetup( vlm_schedule_t *schedule, char *psz_cmd, char *psz
{
struct tm time;
char *p;
char *psz_time = NULL, *psz_date = NULL;
time_t date;
int i,j,k;
/* First, if date or period are modified, repeat should be equal to -1 */
schedule->i_repeat = -1;
......@@ -1908,16 +1952,20 @@ static int vlm_ScheduleSetup( vlm_schedule_t *schedule, char *psz_cmd, char *psz
/* date should be year/month/day-hour:minutes:seconds */
p = strchr( psz_value , '-' );
if( p == NULL && sscanf( psz_value, "%d:%d:%d" , &time.tm_hour, &time.tm_min, &time.tm_sec ) != 3 ) /* it must be a hour:minutes:seconds */
if( p )
{
return 1;
psz_date = psz_value;
psz_time = p + 1;
*p = '\0';
}
else
{
int i,j,k;
psz_time = psz_value;
}
switch( sscanf( p + 1, "%d:%d:%d" , &i, &j, &k ) )
switch( sscanf( psz_time, "%d:%d:%d" , &i, &j, &k ) )
{
case 1:
time.tm_sec = i;
......@@ -1934,10 +1982,9 @@ static int vlm_ScheduleSetup( vlm_schedule_t *schedule, char *psz_cmd, char *psz
default:
return 1;
}
*p = '\0';
switch( sscanf( psz_value, "%d/%d/%d" , &i, &j, &k ) )
if( psz_date )
{
switch( sscanf( psz_date, "%d/%d/%d" , &i, &j, &k ) )
{
case 1:
time.tm_mday = i;
......@@ -2086,21 +2133,6 @@ static int Manage( vlc_object_t* p_object )
}
void vlm_MessageDelete( vlm_message_t* message )
{
int i;
if( message->psz_name ) free( message->psz_name );
if( message->psz_value ) free( message->psz_value );
for( i = 0; i < message->i_child; i++)
{
vlm_MessageDelete( message->child[i] );
}
free( message );
}
static vlm_message_t* vlm_MessageNew( char *psz_name , char *psz_value )
{
vlm_message_t *message = malloc( sizeof(vlm_message_t) );
......@@ -2129,6 +2161,21 @@ static vlm_message_t* vlm_MessageNew( char *psz_name , char *psz_value )
return message;
}
void vlm_MessageDelete( vlm_message_t* message )
{
int i;
if( message->psz_name ) free( message->psz_name );
if( message->psz_value ) free( message->psz_value );
for( i = 0; i < message->i_child; i++)
{
vlm_MessageDelete( message->child[i] );
}
free( message );
}
/* add a child */
static vlm_message_t* vlm_MessageAdd( vlm_message_t* message , vlm_message_t* child )
{
......
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