Add a new type of VLC Lua module: Interfaces.
authorAntoine Cellerier <dionoea@videolan.org>
Sun, 4 Nov 2007 21:42:27 +0000 (21:42 +0000)
committerAntoine Cellerier <dionoea@videolan.org>
Sun, 4 Nov 2007 21:42:27 +0000 (21:42 +0000)
Two things led me to add this new type of Lua modules:
 * Most interfaces duplicate code from one another (rc and telnet both deal
   with sockets, rc and hotkeys duplicate VLC interfacing code)
 * It's easier to code in Lua than in C (for high level stuff)
(* Users can code their own module easily, all they need is a text editor.)

Most of the changes in modules/misc/ are Lua wrappers for different C APIs
in VLC. I'd say that 90% of those wrappers won't change, the other 10% might
 need small changes (mostly to simplify the C code).

The VLC Lua "interface" module will look for lua scripts in the
*share*/luaintf/ directory. Files in *share*/luaintf/modules/ are lua modules
which can be used by multiple VLC Lua modules (using require "modulename").

This commit includes 4 Lua interface modules:
 * dummy.lua: the default module. Prints error messages and quits.
 * rc.lua: equivalent to the legacy rc.c module. Also includes a bunch of
   new features (+ multiple socket connections at a time work!). See file for
   more details.
 * telnet.lua: equivalent to the legacy telnet.c module. See file for more
   details.
 * hotkeys.lua: a rewrite of the hotkey handling module. This module is still
   experimental. I'll need to change VLC core hotkeys stuff to make it work
   like I want (ie: 1 hotkey triggers 1 action instead of the current 1 action
   can have 1 hotkey). This version executes 3 dummy actions when keys 'q',
   'w' or 'x' are pressed.

What's left to do:
 * Port the http interface plugin to this framework (at least for the
   macros/rpn part ... using <?vlc some lua code ?> à la PHP would be way
   easier than what we currently have).
 * Finish work on the hotkeys module.
 * Code a real telnet/rc module with autocompletion and all the cool features
   usually found in a telnet/terminal interface.
 * Trash the legacy C modules.

Stuff to test (which I can't test myself):
 * Win32 and Mac OS X specific changes to Makefile.am
 * Console interface under Win32. I expect it not to work.

Other stuff included in this changeset are:
 * Code cleanup (I'm sure that it's still possible to simplify some of the old lua bindings code).
 * That's pretty much it in fact :/

24 files changed:
Makefile.am
modules/misc/lua/Modules.am
modules/misc/lua/callbacks.c [new file with mode: 0644]
modules/misc/lua/configuration.c [new file with mode: 0644]
modules/misc/lua/intf.c [new file with mode: 0644]
modules/misc/lua/luameta.c [deleted file]
modules/misc/lua/luaplaylist.c [deleted file]
modules/misc/lua/meta.c [new file with mode: 0644]
modules/misc/lua/net.c [new file with mode: 0644]
modules/misc/lua/objects.c [new file with mode: 0644]
modules/misc/lua/playlist.c [new file with mode: 0644]
modules/misc/lua/variables.c [new file with mode: 0644]
modules/misc/lua/vlc.c [new file with mode: 0644]
modules/misc/lua/vlc.h [new file with mode: 0644]
modules/misc/lua/vlclua.c [deleted file]
modules/misc/lua/vlclua.h [deleted file]
modules/misc/lua/vlm.c [new file with mode: 0644]
share/Makefile.am
share/luaintf/dummy.lua [new file with mode: 0644]
share/luaintf/hotkeys.lua [new file with mode: 0644]
share/luaintf/modules/common.lua [new file with mode: 0644]
share/luaintf/modules/host.lua [new file with mode: 0644]
share/luaintf/rc.lua [new file with mode: 0644]
share/luaintf/telnet.lua [new file with mode: 0644]

index 7b7874a..42fe216 100644 (file)
@@ -433,6 +433,14 @@ VLC-release.app: vlc
        for i in $(srcdir)/share/luameta/*.* ; do \
          $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luameta/`basename $${i}` ; \
        done ; \
+       $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf
+       for i in $(srcdir)/share/luaintf/*.* ; do \
+         $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/`basename $${i}` ; \
+       done ; \
+       $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules
+       for i in $(srcdir)/share/luaintf/modules/*.* ; do \
+         $(INSTALL) -m 644 $${i} $(top_builddir)/VLC-release.app/Contents/MacOS/share/luaintf/modules/`basename $${i}` ; \
+       done ; \
        $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/dialogs
        $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/js
        $(INSTALL) -d $(top_builddir)/VLC-release.app/Contents/MacOS/share/http/old
@@ -733,6 +741,14 @@ package-win32-base-debug: package-win-common
        for i in $(srcdir)/share/luameta/*.* ; do \
          $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luameta/`basename $${i}` ; \
        done ;
+       $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf
+       for i in $(srcdir)/share/luaintf/*.* ; do \
+         $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/`basename $${i}` ; \
+       done ;
+       $(INSTALL) -d $(top_builddir)/vlc-${VERSION}/share/luaintf/modules
+       for i in $(srcdir)/share/luaintf/modules/*.* ; do \
+         $(INSTALL) -m 644 $${i} $(top_builddir)/vlc-${VERSION}/share/luaintf/modules/`basename $${i}` ; \
+       done ;
 
        mkdir -p "$(top_builddir)/vlc-${VERSION}/osdmenu"
        cp $(srcdir)/share/osdmenu/*.* "$(top_builddir)/vlc-${VERSION}/osdmenu"
index d22ed9b..f01c536 100644 (file)
@@ -1 +1 @@
-SOURCES_lua = luaplaylist.c luameta.c vlclua.c vlclua.h
+SOURCES_lua = playlist.c meta.c intf.c vlc.c vlc.h callbacks.c objects.c variables.c configuration.c net.c vlm.c
diff --git a/modules/misc/lua/callbacks.c b/modules/misc/lua/callbacks.c
new file mode 100644 (file)
index 0000000..10c2732
--- /dev/null
@@ -0,0 +1,277 @@
+/*****************************************************************************
+ * callbacks.c: Generic lua<->vlc callbacks interface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+typedef struct
+{
+    int i_index;
+    int i_type;
+    lua_State *L;
+}   vlclua_callback_t;
+
+static int vlclua_callback( vlc_object_t *p_this, char const *psz_var,
+                            vlc_value_t oldval, vlc_value_t newval,
+                            void *p_data )
+{
+    vlclua_callback_t *p_callback = (vlclua_callback_t*)p_data;
+    lua_State *L = p_callback->L;
+
+    /* <empty stack> */
+    lua_getglobal( L, "vlc" );
+    /* vlc */
+    lua_getfield( L, -1, "callbacks" );
+    /* vlc callbacks */
+    lua_remove( L, -2 );
+    /* callbacks */
+    lua_pushinteger( L, p_callback->i_index );
+    /* callbacks index */
+    lua_gettable( L, -2 );
+    /* callbacks callbacks[index] */
+    lua_remove( L, -2 );
+    /* callbacks[index] */
+    lua_getfield( L, -1, "callback" );
+    /* callbacks[index] callback */
+    lua_pushstring( L, psz_var );
+    /* callbacks[index] callback var */
+    vlclua_pushvalue( L, p_callback->i_type, oldval );
+    /* callbacks[index] callback var oldval */
+    vlclua_pushvalue( L, p_callback->i_type, newval );
+    /* callbacks[index] callback var oldval newval */
+    lua_getfield( L, -5, "data" );
+    /* callbacks[index] callback var oldval newval data */
+    lua_remove( L, -6 );
+    /* callback var oldval newval data */
+
+    if( lua_pcall( L, 4, 0, 0 ) )
+    {
+        /* errormessage */
+        const char *psz_err = lua_tostring( L, -1 );
+        msg_Err( p_this, "Error while runing lua interface callback: %s",
+                 psz_err );
+        /* empty the stack (should only contain the error message) */
+        lua_settop( L, 0 );
+        return VLC_EGENERIC;
+    }
+
+    /* empty the stack (should already be empty) */
+    lua_settop( L, 0 );
+
+    return VLC_SUCCESS;
+}
+
+int vlclua_add_callback( lua_State *L )
+{
+    vlclua_callback_t *p_callback;
+    static int i_index = 0;
+    vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+    const char *psz_var = luaL_checkstring( L, 2 );
+    lua_settop( L, 4 ); /* makes sure that optional data arg is set */
+    if( !lua_isfunction( L, 3 ) )
+        return vlclua_error( L );
+    i_index++;
+
+    p_callback = (vlclua_callback_t*)malloc( sizeof( vlclua_callback_t ) );
+    if( !p_callback )
+        return vlclua_error( L );
+
+    /* obj var func data */
+    lua_getglobal( L, "vlc" );
+    /* obj var func data vlc */
+    lua_getfield( L, -1, "callbacks" );
+    if( lua_isnil( L, -1 ) )
+    {
+        lua_pop( L, 1 );
+        lua_newtable( L );
+        lua_setfield( L, -2, "callbacks" );
+        lua_getfield( L, -1, "callbacks" );
+    }
+    /* obj var func data vlc callbacks */
+    lua_remove( L, -2 );
+    /* obj var func data callbacks */
+    lua_pushinteger( L, i_index );
+    /* obj var func data callbacks index */
+    lua_insert( L, -4 );
+    /* obj var index func data callbacks */
+    lua_insert( L, -4 );
+    /* obj var callbacks index func data */
+    lua_createtable( L, 0, 0 );
+    /* obj var callbacks index func data cbtable */
+    lua_insert( L, -2 );
+    /* obj var callbacks index func cbtable data */
+    lua_setfield( L, -2, "data" );
+    /* obj var callbacks index func cbtable */
+    lua_insert( L, -2 );
+    /* obj var callbacks index cbtable func */
+    lua_setfield( L, -2, "callback" );
+    /* obj var callbacks index cbtable */
+    lua_pushlightuserdata( L, p_obj ); /* will be needed in vlclua_del_callback */
+    /* obj var callbacks index cbtable p_obj */
+    lua_setfield( L, -2, "private1" );
+    /* obj var callbacks index cbtable */
+    lua_pushvalue( L, 2 ); /* will be needed in vlclua_del_callback */
+    /* obj var callbacks index cbtable var */
+    lua_setfield( L, -2, "private2" );
+    /* obj var callbacks index cbtable */
+    lua_pushlightuserdata( L, p_callback ); /* will be needed in vlclua_del_callback */
+    /* obj var callbacks index cbtable p_callback */
+    lua_setfield( L, -2, "private3" );
+    /* obj var callbacks index cbtable */
+    lua_settable( L, -3 );
+    /* obj var callbacks */
+    lua_pop( L, 3 );
+    /* <empty stack> */
+
+    /* Do not move this before the lua specific code (it somehow changes
+     * the function in the stack to nil) */
+    p_callback->i_index = i_index;
+    p_callback->i_type = var_Type( p_obj, psz_var );
+    p_callback->L = lua_newthread( L );
+
+    var_AddCallback( p_obj, psz_var, vlclua_callback, p_callback );
+    return 0;
+}
+
+int vlclua_del_callback( lua_State *L )
+{
+    vlclua_callback_t *p_callback;
+    vlc_bool_t b_found = VLC_FALSE;
+    vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+    const char *psz_var = luaL_checkstring( L, 2 );
+    lua_settop( L, 4 ); /* makes sure that optional data arg is set */
+    if( !lua_isfunction( L, 3 ) )
+        return vlclua_error( L );
+
+    /* obj var func data */
+    lua_getglobal( L, "vlc" );
+    /* obj var func data vlc */
+    lua_getfield( L, -1, "callbacks" );
+    if( lua_isnil( L, -1 ) )
+        return luaL_error( L, "Couldn't find matching callback." );
+    /* obj var func data vlc callbacks */
+    lua_remove( L, -2 );
+    /* obj var func data callbacks */
+    lua_pushnil( L );
+    /* obj var func data callbacks index */
+    while( lua_next( L, -2 ) )
+    {
+        /* obj var func data callbacks index value */
+        if( lua_isnumber( L, -2 ) )
+        {
+            lua_getfield( L, -1, "private2" );
+            /* obj var func data callbacks index value private2 */
+            if( lua_equal( L, 2, -1 ) ) /* var name is equal */
+            {
+                lua_pop( L, 1 );
+                /* obj var func data callbacks index value */
+                lua_getfield( L, -1, "callback" );
+                /* obj var func data callbacks index value callback */
+                if( lua_equal( L, 3, -1 ) ) /* callback function is equal */
+                {
+                    lua_pop( L, 1 );
+                    /* obj var func data callbacks index value */
+                    lua_getfield( L, -1, "data" ); /* callback data is equal */
+                    /* obj var func data callbacks index value data */
+                    if( lua_equal( L, 4, -1 ) )
+                    {
+                        vlc_object_t *p_obj2;
+                        lua_pop( L, 1 );
+                        /* obj var func data callbacks index value */
+                        lua_getfield( L, -1, "private1" );
+                        /* obj var func data callbacks index value private1 */
+                        p_obj2 = (vlc_object_t*)luaL_checklightuserdata( L, -1 );
+                        if( p_obj2 == p_obj ) /* object is equal */
+                        {
+                            lua_pop( L, 1 );
+                            /* obj var func data callbacks index value */
+                            lua_getfield( L, -1, "private3" );
+                            /* obj var func data callbacks index value private3 */
+                            p_callback = (vlclua_callback_t*)luaL_checklightuserdata( L, -1 );
+                            lua_pop( L, 2 );
+                            /* obj var func data callbacks index */
+                            b_found = VLC_TRUE;
+                            break;
+                        }
+                        else
+                        {
+                            /* obj var func data callbacks index value private1 */
+                            lua_pop( L, 1 );
+                            /* obj var func data callbacks index value */
+                        }
+                    }
+                    else
+                    {
+                        /* obj var func data callbacks index value data */
+                        lua_pop( L, 1 );
+                        /* obj var func data callbacks index value */
+                    }
+                }
+                else
+                {
+                    /* obj var func data callbacks index value callback */
+                    lua_pop( L, 1 );
+                    /* obj var func data callbacks index value */
+                }
+            }
+            else
+            {
+                /* obj var func data callbacks index value private2 */
+                lua_pop( L, 1 );
+                /* obj var func data callbacks index value */
+            }
+        }
+        /* obj var func data callbacks index value */
+        lua_pop( L, 1 );
+        /* obj var func data callbacks index */
+    }
+    if( b_found == VLC_FALSE )
+        /* obj var func data callbacks */
+        return luaL_error( L, "Couldn't find matching callback." );
+    /* else */
+        /* obj var func data callbacks index*/
+
+    var_DelCallback( p_obj, psz_var, vlclua_callback, p_callback );
+    free( p_callback );
+
+    /* obj var func data callbacks index */
+    lua_pushnil( L );
+    /* obj var func data callbacks index nil */
+    lua_settable( L, -3 ); /* delete the callback table entry */
+    /* obj var func data callbacks */
+    lua_pop( L, 5 );
+    /* <empty stack> */
+    return 0;
+}
diff --git a/modules/misc/lua/configuration.c b/modules/misc/lua/configuration.c
new file mode 100644 (file)
index 0000000..9286a6c
--- /dev/null
@@ -0,0 +1,106 @@
+/*****************************************************************************
+ * configuration.c: Generic lua<->vlc config inteface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Config handling
+ *****************************************************************************/
+int vlclua_config_get( lua_State *L )
+{
+    vlc_object_t * p_this = vlclua_get_this( L );
+    const char *psz_name;
+    psz_name = luaL_checkstring( L, 1 );
+    switch( config_GetType( p_this, psz_name ) )
+    {
+        case VLC_VAR_MODULE:
+        case VLC_VAR_STRING:
+        case VLC_VAR_FILE:
+        case VLC_VAR_DIRECTORY:
+            lua_pushstring( L, config_GetPsz( p_this, psz_name ) );
+            break;
+
+        case VLC_VAR_INTEGER:
+            lua_pushinteger( L, config_GetInt( p_this, psz_name ) );
+            break;
+
+        case VLC_VAR_BOOL:
+            lua_pushboolean( L, config_GetInt( p_this, psz_name ) );
+            break;
+
+        case VLC_VAR_FLOAT:
+            lua_pushnumber( L, config_GetFloat( p_this, psz_name ) );
+            break;
+
+        default:
+            return vlclua_error( L );
+
+    }
+    return 1;
+}
+
+int vlclua_config_set( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    const char *psz_name;
+    psz_name = luaL_checkstring( L, 1 );
+    switch( config_GetType( p_this, psz_name ) )
+    {
+        case VLC_VAR_MODULE:
+        case VLC_VAR_STRING:
+        case VLC_VAR_FILE:
+        case VLC_VAR_DIRECTORY:
+            config_PutPsz( p_this, psz_name, luaL_checkstring( L, 2 ) );
+            break;
+
+        case VLC_VAR_INTEGER:
+            config_PutInt( p_this, psz_name, luaL_checkint( L, 2 ) );
+            break;
+
+        case VLC_VAR_BOOL:
+            config_PutInt( p_this, psz_name, luaL_checkboolean( L, 2 ) );
+            break;
+
+        case VLC_VAR_FLOAT:
+            config_PutFloat( p_this, psz_name,
+                             luaL_checknumber( L, 2 ) );
+            break;
+
+        default:
+            return vlclua_error( L );
+    }
+    return 0;
+}
diff --git a/modules/misc/lua/intf.c b/modules/misc/lua/intf.c
new file mode 100644 (file)
index 0000000..01d36a9
--- /dev/null
@@ -0,0 +1,897 @@
+/*****************************************************************************
+ * intf.c: Generic lua inteface functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_meta.h>
+#include <vlc_charset.h>
+
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
+#include <vlc_aout.h>
+#include <vlc_vout.h>
+#include <vlc_osd.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+struct intf_sys_t
+{
+    char *psz_filename;
+    lua_State *L;
+};
+
+/*****************************************************************************
+ * Internal lua<->vlc utils
+ *****************************************************************************/
+static inline playlist_t *vlclua_get_playlist_internal( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    return pl_Yield( p_this );
+}
+
+static input_thread_t * vlclua_get_input_internal( lua_State *L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    input_thread_t *p_input = p_playlist->p_input;
+    if( p_input ) vlc_object_yield( p_input );
+    vlc_object_release( p_playlist );
+    return p_input;
+}
+
+/* FIXME: This is high level logic. Should be implemented in lua */
+#define vlclua_var_toggle_or_set(a,b,c) \
+        __vlclua_var_toggle_or_set(a,VLC_OBJECT(b),c)
+static int __vlclua_var_toggle_or_set( lua_State *L, vlc_object_t *p_obj,
+                                       const char *psz_name )
+{
+    vlc_bool_t b_bool;
+    if( lua_gettop( L ) > 1 ) return vlclua_error( L );
+
+    if( lua_gettop( L ) == 0 )
+        b_bool = !var_GetBool( p_obj, psz_name );
+    else /* lua_gettop( L ) == 1 */
+    {
+        b_bool = luaL_checkboolean( L, -1 )?VLC_TRUE:VLC_FALSE;
+        lua_pop( L, 1 );
+    }
+
+    if( b_bool != var_GetBool( p_obj, psz_name ) )
+        var_SetBool( p_obj, psz_name, b_bool );
+
+    lua_pushboolean( L, b_bool );
+    return 1;
+}
+
+/*****************************************************************************
+ * Libvlc TODO: move to vlc.c
+ *****************************************************************************/
+static int vlclua_get_libvlc( lua_State *L )
+{
+    vlclua_push_vlc_object( L, vlclua_get_this( L )->p_libvlc,
+                            NULL );
+    return 1;
+}
+
+/*****************************************************************************
+ * Input handling
+ *****************************************************************************/
+static int vlclua_get_input( lua_State *L )
+{
+    input_thread_t *p_input = vlclua_get_input_internal( L );
+    if( p_input )
+    {
+        vlclua_push_vlc_object( L, p_input, vlclua_gc_release );
+    }
+    else lua_pushnil( L );
+    return 1;
+}
+
+static int vlclua_input_info( lua_State *L )
+{
+    input_thread_t * p_input = vlclua_get_input_internal( L );
+    int i_cat;
+    int i;
+    if( !p_input ) return vlclua_error( L );
+    vlc_mutex_lock( &input_GetItem(p_input)->lock );
+    i_cat = input_GetItem(p_input)->i_categories;
+    lua_createtable( L, 0, i_cat );
+    for( i = 0; i < i_cat; i++ )
+    {
+        info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
+        int i_infos = p_category->i_infos;
+        int j;
+        lua_pushstring( L, p_category->psz_name );
+        lua_createtable( L, 0, i_infos );
+        for( j = 0; j < i_infos; j++ )
+        {
+            info_t *p_info = p_category->pp_infos[j];
+            lua_pushstring( L, p_info->psz_name );
+            lua_pushstring( L, p_info->psz_value );
+            lua_settable( L, -3 );
+        }
+        lua_settable( L, -3 );
+    }
+    vlc_object_release( p_input );
+    return 1;
+}
+
+static int vlclua_is_playing( lua_State *L )
+{
+    input_thread_t * p_input = vlclua_get_input_internal( L );
+    lua_pushboolean( L, !!p_input );
+    return 1;
+}
+
+static int vlclua_get_title( lua_State *L )
+{
+    input_thread_t *p_input = vlclua_get_input_internal( L );
+    if( !p_input )
+        lua_pushnil( L );
+    else
+    {
+        lua_pushstring( L, input_GetItem(p_input)->psz_name );
+        vlc_object_release( p_input );
+    }
+    return 1;
+}
+
+/*****************************************************************************
+ * Vout control
+ *****************************************************************************/
+static int vlclua_fullscreen( lua_State *L )
+{
+    vout_thread_t *p_vout;
+    int i_ret;
+
+    input_thread_t * p_input = vlclua_get_input_internal( L );
+    if( !p_input ) return vlclua_error( L );
+
+    p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
+    if( !p_vout ) return vlclua_error( L );
+
+    i_ret = vlclua_var_toggle_or_set( L, p_vout, "fullscreen" );
+    vlc_object_release( p_vout );
+    vlc_object_release( p_input );
+    return i_ret;
+}
+
+static int vlc_osd_icon_from_string( const char *psz_name )
+{
+    static const struct
+    {
+        int i_icon;
+        const char *psz_name;
+    } pp_icons[] =
+        { { OSD_PAUSE_ICON, "pause" },
+          { OSD_PLAY_ICON, "play" },
+          { OSD_SPEAKER_ICON, "speaker" },
+          { OSD_MUTE_ICON, "mute" },
+          { 0, NULL } };
+    int i;
+    for( i = 0; pp_icons[i].psz_name; i++ )
+    {
+        if( !strcmp( psz_name, pp_icons[i].psz_name ) )
+            return pp_icons[i].i_icon;
+    }
+    return 0;
+}
+
+static int vlclua_osd_icon( lua_State *L )
+{
+    const char *psz_icon = luaL_checkstring( L, 1 );
+    int i_icon = vlc_osd_icon_from_string( psz_icon );
+    int i_chan = luaL_optint( L, 2, DEFAULT_CHAN );
+    if( !i_icon )
+        return luaL_error( L, "\"%s\" is not a valid osd icon.", psz_icon );
+    else
+    {
+        vlc_object_t *p_this = vlclua_get_this( L );
+        vout_OSDIcon( p_this, i_chan, i_icon );
+        return 0;
+    }
+}
+
+static int vlclua_osd_message( lua_State *L )
+{
+    const char *psz_message = luaL_checkstring( L, 1 );
+    int i_chan = luaL_optint( L, 2, DEFAULT_CHAN );
+    vlc_object_t *p_this = vlclua_get_this( L );
+    vout_OSDMessage( p_this, i_chan, psz_message );
+    return 0;
+}
+
+static int vlc_osd_slider_type_from_string( const char *psz_name )
+{
+    static const struct
+    {
+        int i_type;
+        const char *psz_name;
+    } pp_types[] =
+        { { OSD_HOR_SLIDER, "horizontal" },
+          { OSD_VERT_SLIDER, "vertical" },
+          { 0, NULL } };
+    int i;
+    for( i = 0; pp_types[i].psz_name; i++ )
+    {
+        if( !strcmp( psz_name, pp_types[i].psz_name ) )
+            return pp_types[i].i_type;
+    }
+    return 0;
+}
+
+static int vlclua_osd_slider( lua_State *L )
+{
+    int i_position = luaL_checkint( L, 1 );
+    const char *psz_type = luaL_checkstring( L, 2 );
+    int i_type = vlc_osd_slider_type_from_string( psz_type );
+    int i_chan = luaL_optint( L, 3, DEFAULT_CHAN );
+    if( !i_type )
+        return luaL_error( L, "\"%s\" is not a valid slider type.",
+                           psz_type );
+    else
+    {
+        vlc_object_t *p_this = vlclua_get_this( L );
+        vout_OSDSlider( p_this, i_chan, i_position, i_type );
+        return 0;
+    }
+}
+
+static int vlclua_spu_channel_register( lua_State *L )
+{
+    int i_chan;
+    vlc_object_t *p_this = vlclua_get_this( L );
+    vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT,
+                                             FIND_ANYWHERE );
+    if( !p_vout )
+        return luaL_error( L, "Unable to find vout." );
+
+    spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER, &i_chan );
+    vlc_object_release( p_vout );
+    lua_pushinteger( L, i_chan );
+    return 1;
+}
+
+static int vlclua_spu_channel_clear( lua_State *L )
+{
+    int i_chan = luaL_checkint( L, 1 );
+    vlc_object_t *p_this = vlclua_get_this( L );
+    vout_thread_t *p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT,
+                                             FIND_ANYWHERE );
+    if( !p_vout )
+        return luaL_error( L, "Unable to find vout." );
+
+    spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, i_chan );
+    vlc_object_release( p_vout );
+    return 0;
+}
+
+/*****************************************************************************
+ * Playlist control
+ *****************************************************************************/
+static int vlclua_get_playlist( lua_State *L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    if( p_playlist )
+    {
+        vlclua_push_vlc_object( L, p_playlist, vlclua_gc_release );
+    }
+    else lua_pushnil( L );
+    return 1;
+}
+
+static int vlclua_playlist_prev( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Prev( p_playlist );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
+static int vlclua_playlist_next( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Next( p_playlist );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
+static int vlclua_playlist_play( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Play( p_playlist );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
+static int vlclua_playlist_stop( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Stop( p_playlist );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
+static int vlclua_playlist_clear( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_Stop( p_playlist ); /* Isn't this already implied by Clear? */
+    playlist_Clear( p_playlist, VLC_FALSE );
+    vlc_object_release( p_playlist );
+    return 0;
+}
+
+static int vlclua_playlist_repeat( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "repeat" );
+    vlc_object_release( p_playlist );
+    return i_ret;
+}
+
+static int vlclua_playlist_loop( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "loop" );
+    vlc_object_release( p_playlist );
+    return i_ret;
+}
+
+static int vlclua_playlist_random( lua_State * L )
+{
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    int i_ret = vlclua_var_toggle_or_set( L, p_playlist, "random" );
+    vlc_object_release( p_playlist );
+    return i_ret;
+}
+
+static int vlclua_playlist_goto( lua_State * L )
+{
+    /* XXX: logic copied from rc.c ... i'm not sure that it's ok as it
+     *      implies knowledge of the playlist internals. */
+    playlist_t *p_playlist;
+    int i_size;
+    playlist_item_t *p_item, *p_parent;
+
+    int i_pos;
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    i_pos = luaL_checkint( L, -1 );
+    lua_pop( L, 1 );
+    if( i_pos <= 0 ) return 0;
+
+    p_playlist = vlclua_get_playlist_internal( L );
+    /* The playlist stores 2 times the same item: onelevel & category */
+    i_size = p_playlist->items.i_size / 2;
+
+    if( i_pos > i_size )
+    {
+        vlc_object_release( p_playlist );
+        return 0;
+    }
+
+    p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1];
+    while( p_parent->p_parent )
+        p_parent = p_parent->p_parent;
+    playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
+                      p_parent, p_item );
+
+    vlc_object_release( p_playlist );
+    lua_pushboolean( L, 1 );
+    return 1;
+}
+
+static int vlclua_playlist_add( lua_State *L )
+{
+    int i_count;
+    vlc_object_t *p_this = vlclua_get_this( L );
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    i_count = vlclua_playlist_add_internal( p_this, L, p_playlist,
+                                            NULL, VLC_TRUE );
+    vlc_object_release( p_playlist );
+    lua_pushinteger( L, i_count );
+    return 1;
+}
+
+static int vlclua_playlist_enqueue( lua_State *L )
+{
+    int i_count;
+    vlc_object_t *p_this = vlclua_get_this( L );
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    i_count = vlclua_playlist_add_internal( p_this, L, p_playlist,
+                                            NULL, VLC_FALSE );
+    vlc_object_release( p_playlist );
+    lua_pushinteger( L, i_count );
+    return 1;
+}
+
+static int vlclua_playlist_get( lua_State *L )
+{
+    /* TODO: make it possible to get the tree playlist */
+    playlist_t *p_playlist = vlclua_get_playlist_internal( L );
+    playlist_item_t *p_root;
+    int i;
+    if( lua_isboolean( L, 1 ) && lua_toboolean( L, 1 ) )
+        p_root = p_playlist->p_ml_onelevel; /* media library */
+    else
+        p_root = p_playlist->p_local_onelevel; /* local/normal playlist */
+    lua_createtable( L, p_root->i_children, 0 );
+    for( i = 0; i < p_root->i_children; i++ )
+    {
+        playlist_item_t *p_item = p_root->pp_children[i];
+        input_item_t *p_input = p_item->p_input;
+        lua_pushinteger( L, i+1 );
+        lua_newtable( L );
+        lua_pushstring( L, p_input->psz_name );
+        lua_setfield( L, -2, "name" );
+        lua_pushstring( L, p_input->psz_uri );
+        lua_setfield( L, -2, "path" );
+        lua_pushnumber( L, ((double)p_input->i_duration)*1e-6 );
+        lua_setfield( L, -2, "duration" );
+        lua_pushinteger( L, p_input->i_nb_played );
+        lua_setfield( L, -2, "nb_played" );
+        /* TODO: add (optional) info categories, meta, options, es */
+        lua_settable( L, -3 );
+    }
+    vlc_object_release( p_playlist );
+    return 1;
+}
+
+static int vlclua_playlist_sort( lua_State *L )
+{
+    /* allow setting the different sort keys */
+    return 0;
+}
+
+/* FIXME: split this in 3 different functions? */
+static int vlclua_playlist_status( lua_State *L )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)vlclua_get_this( L );
+    playlist_t *p_playlist = pl_Yield( p_intf );
+    int i_count = 0;
+    lua_settop( L, 0 );
+    if( p_playlist->p_input )
+    {
+        char *psz_uri =
+            input_item_GetURI( input_GetItem( p_playlist->p_input ) );
+        lua_pushstring( L, psz_uri );
+        free( psz_uri );
+        lua_pushnumber( L, config_GetInt( p_intf, "volume" ) );
+        vlc_mutex_lock( &p_playlist->object_lock );
+        switch( p_playlist->status.i_status )
+        {
+            case PLAYLIST_STOPPED:
+                lua_pushstring( L, "stopped" );
+                break;
+            case PLAYLIST_RUNNING:
+                lua_pushstring( L, "running" );
+                break;
+            case PLAYLIST_PAUSED:
+                lua_pushstring( L, "paused" );
+                break;
+            default:
+                lua_pushstring( L, "unknown" );
+                break;
+        }
+        vlc_mutex_unlock( &p_playlist->object_lock );
+        i_count += 3;
+    }
+    vlc_object_release( p_playlist );
+    return i_count;
+}
+
+
+static int vlclua_lock_and_wait( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    int b_quit = vlc_object_lock_and_wait( p_this );
+    lua_pushboolean( L, b_quit );
+    return 1;
+}
+
+static int vlclua_signal( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    vlc_object_signal( p_this );
+    return 0;
+}
+
+static int vlclua_mdate( lua_State *L )
+{
+    lua_pushnumber( L, mdate() );
+    return 1;
+}
+
+static int vlclua_intf_should_die( lua_State *L )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)vlclua_get_this( L );
+    lua_pushboolean( L, intf_ShouldDie( p_intf ) );
+    return 1;
+}
+
+static luaL_Reg p_reg[] =
+{
+    { "input_info", vlclua_input_info },
+    { "is_playing", vlclua_is_playing },
+    { "get_title", vlclua_get_title },
+
+    { "fullscreen", vlclua_fullscreen },
+
+    { "mdate", vlclua_mdate },
+
+    { "module_command", vlclua_module_command },
+    { "libvlc_command", vlclua_libvlc_command },
+
+    { "decode_uri", vlclua_decode_uri },
+    { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
+
+    { "lock_and_wait", vlclua_lock_and_wait },
+    { "signal", vlclua_signal },
+
+    { "version", vlclua_version },
+    { "should_die", vlclua_intf_should_die },
+    { "quit", vlclua_quit },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_object[] =
+{
+    { "input", vlclua_get_input },              /* This is fast */
+    { "playlist", vlclua_get_playlist },        /* This is fast */
+    { "libvlc", vlclua_get_libvlc },            /* This is fast */
+
+    { "find", vlclua_object_find },             /* This is slow */
+    { "find_name", vlclua_object_find_name },   /* This is slow */
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_var[] =
+{
+    { "get", vlclua_var_get },
+    { "get_list", vlclua_var_get_list },
+    { "set", vlclua_var_set },
+    { "add_callback", vlclua_add_callback },
+    { "del_callback", vlclua_del_callback },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_config[] =
+{
+    { "get", vlclua_config_get },
+    { "set", vlclua_config_set },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_msg[] =
+{
+    { "dbg", vlclua_msg_dbg },
+    { "warn", vlclua_msg_warn },
+    { "err", vlclua_msg_err },
+    { "info", vlclua_msg_info },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_playlist[] =
+{
+    { "prev", vlclua_playlist_prev },
+    { "next", vlclua_playlist_next },
+    { "play", vlclua_playlist_play },
+    { "stop", vlclua_playlist_stop },
+    { "clear", vlclua_playlist_clear },
+    { "repeat_", vlclua_playlist_repeat },
+    { "loop", vlclua_playlist_loop },
+    { "random", vlclua_playlist_random },
+    { "goto", vlclua_playlist_goto },
+    { "status", vlclua_playlist_status },
+    { "add", vlclua_playlist_add },
+    { "enqueue", vlclua_playlist_enqueue },
+    { "get", vlclua_playlist_get },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_volume[] =
+{
+    { "get", vlclua_volume_get },
+    { "set", vlclua_volume_set },
+    { "up", vlclua_volume_up },
+    { "down", vlclua_volume_down },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_osd[] =
+{
+    { "icon", vlclua_osd_icon },
+    { "message", vlclua_osd_message },
+    { "slider", vlclua_osd_slider },
+    { "channel_register", vlclua_spu_channel_register },
+    { "channel_clear", vlclua_spu_channel_clear },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_net[] =
+{
+    { "url_parse", vlclua_url_parse },
+    { "listen_tcp", vlclua_net_listen_tcp },
+    { "listen_close", vlclua_net_listen_close },
+    { "accept", vlclua_net_accept },
+    { "close", vlclua_net_close },
+    { "send", vlclua_net_send },
+    { "recv", vlclua_net_recv },
+    { "select", vlclua_net_select },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_fd[] =
+{
+/*    { "open", vlclua_fd_open },*/
+    { "read", vlclua_fd_read },
+    { "write", vlclua_fd_write },
+
+    { "new_fd_set", vlclua_fd_set_new },
+    { "fd_clr", vlclua_fd_clr },
+    { "fd_isset", vlclua_fd_isset },
+    { "fd_set", vlclua_fd_set },
+    { "fd_zero", vlclua_fd_zero },
+
+    { NULL, NULL }
+};
+
+static luaL_Reg p_reg_vlm[] =
+{
+    { "new", vlclua_vlm_new },
+    { "delete", vlclua_vlm_delete },
+    { "execute_command", vlclua_vlm_execute_command },
+
+    { NULL, NULL }
+};
+
+
+static void Run( intf_thread_t *p_intf );
+
+static char *FindFile( intf_thread_t *p_intf, const char *psz_name )
+{
+    char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
+    char **ppsz_dir;
+    vlclua_dir_list( VLC_OBJECT(p_intf), "luaintf", ppsz_dir_list );
+    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+    {
+        char *psz_filename;
+        FILE *fp;
+        if( asprintf( &psz_filename, "%s"DIR_SEP"%s.lua", *ppsz_dir,
+                      psz_name ) < 0 )
+        {
+            return NULL;
+        }
+        fp = fopen( psz_filename, "r" );
+        if( fp )
+        {
+            fclose( fp );
+            return psz_filename;
+        }
+        free( psz_filename );
+    }
+    return NULL;
+}
+
+static inline void luaL_register_submodule( lua_State *L, const char *psz_name,
+                                            const luaL_Reg *l )
+{
+    lua_newtable( L );
+    luaL_register( L, NULL, l );
+    lua_setfield( L, -2, psz_name );
+}
+
+static struct
+{
+    const char *psz_shortcut;
+    const char *psz_name;
+} pp_shortcuts[] = {
+    { "luarc", "rc" },
+    /* { "rc", "rc" }, */
+    { "luahotkeys", "hotkeys" },
+    /* { "hotkeys", "hotkeys" }, */
+    { "luatelnet", "telnet" },
+    /* { "telnet", "telnet" }, */
+    { NULL, NULL } };
+
+static vlc_bool_t WordInList( const char *psz_list, const char *psz_word )
+{
+    const char *psz_str = strstr( psz_list, psz_word );
+    int i_len = strlen( psz_word );
+    while( psz_str )
+    {
+        if( (psz_str == psz_list || *(psz_str-1) == ',' )
+         /* it doesn't start in middle of a word */
+         /* it doest end in middle of a word */
+         && ( psz_str[i_len] == '\0' || psz_str[i_len] == ',' ) )
+            return VLC_TRUE;
+        psz_str = strstr( psz_str, psz_word );
+    }
+    return VLC_FALSE;
+}
+
+static const char *GetModuleName( intf_thread_t *p_intf )
+{
+    int i;
+    const char *psz_intf;
+    if( *p_intf->psz_intf == '$' )
+        psz_intf = var_GetString( p_intf, p_intf->psz_intf+1 );
+    else
+        psz_intf = p_intf->psz_intf;
+    for( i = 0; pp_shortcuts[i].psz_name; i++ )
+    {
+        if( WordInList( psz_intf, pp_shortcuts[i].psz_shortcut ) )
+            return pp_shortcuts[i].psz_name;
+    }
+
+    return config_GetPsz( p_intf, "lua-intf" );
+}
+
+int E_(Open_LuaIntf)( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_sys_t *p_sys;
+    lua_State *L;
+
+    const char *psz_name = GetModuleName( p_intf );
+    const char *psz_config;
+    vlc_bool_t b_config_set = VLC_FALSE;
+    if( !psz_name ) psz_name = "dummy";
+
+    p_intf->p_sys = (intf_sys_t*)malloc( sizeof(intf_sys_t*) );
+    if( !p_intf->p_sys )
+    {
+        return VLC_ENOMEM;
+    }
+    p_sys = p_intf->p_sys;
+    p_sys->psz_filename = FindFile( p_intf, psz_name );
+    if( !p_sys->psz_filename )
+    {
+        msg_Err( p_intf, "Couldn't find lua interface script \"%s\".",
+                 psz_name );
+        return VLC_EGENERIC;
+    }
+    msg_Dbg( p_intf, "Found lua interface script: %s", p_sys->psz_filename );
+
+    L = luaL_newstate();
+    if( !L )
+    {
+        msg_Err( p_intf, "Could not create new Lua State" );
+        free( p_sys );
+        return VLC_EGENERIC;
+    }
+
+    luaL_openlibs( L ); /* FIXME: we don't want to have all the libs */
+
+    /* register our functions */
+    luaL_register( L, "vlc", p_reg );
+    /* store a pointer to p_intf */
+    lua_pushlightuserdata( L, p_intf );
+    lua_setfield( L, -2, "private" );
+    /* register submodules */
+    luaL_register_submodule( L, "object", p_reg_object );
+    luaL_register_submodule( L, "var", p_reg_var );
+    luaL_register_submodule( L, "config", p_reg_config );
+    luaL_register_submodule( L, "msg", p_reg_msg );
+    luaL_register_submodule( L, "playlist", p_reg_playlist );
+    luaL_register_submodule( L, "volume", p_reg_volume );
+    luaL_register_submodule( L, "osd", p_reg_osd );
+    luaL_register_submodule( L, "net", p_reg_net );
+    luaL_register_submodule( L, "fd", p_reg_fd );
+    luaL_register_submodule( L, "vlm", p_reg_vlm );
+    /* clean up */
+    lua_pop( L, 1 );
+
+    /* <gruik> */
+    /* Setup the module search path */
+    {
+    char *psz_command;
+    char *psz_char = strrchr(p_sys->psz_filename,DIR_SEP_CHAR);
+    *psz_char = '\0';
+    /* FIXME: don't use luaL_dostring */
+    if( asprintf( &psz_command,
+                  "package.path = \"%s"DIR_SEP"modules"DIR_SEP"?.lua;\"..package.path",
+                  p_sys->psz_filename ) < 0 )
+        return VLC_EGENERIC;
+    *psz_char = DIR_SEP_CHAR;
+    if( luaL_dostring( L, psz_command ) )
+        return VLC_EGENERIC;
+    }
+    /* </gruik> */
+
+    psz_config = config_GetPsz( p_intf, "lua-config" );
+    if( psz_config && *psz_config )
+    {
+        char *psz_buffer;
+        if( asprintf( &psz_buffer, "config={%s}", psz_config ) != -1 )
+        {
+            printf("%s\n", psz_buffer);
+            if( luaL_dostring( L, psz_buffer ) == 1 )
+                msg_Err( p_intf, "Error while parsing \"lua-config\"." );
+            free( psz_buffer );
+            lua_getglobal( L, "config" );
+            if( lua_istable( L, -1 ) )
+            {
+                lua_getfield( L, -1, psz_name );
+                if( lua_istable( L, -1 ) )
+                {
+                    lua_setglobal( L, "config" );
+                    b_config_set = VLC_TRUE;
+                }
+            }
+        }
+    }
+    if( b_config_set == VLC_FALSE )
+    {
+        lua_newtable( L );
+        lua_setglobal( L, "config" );
+    }
+
+    p_sys->L = L;
+
+    p_intf->pf_run = Run;
+    p_intf->psz_header = strdup( psz_name ); /* Do I need to clean that up myself in E_(Close_LuaIntf)? */
+
+    return VLC_SUCCESS;
+}
+
+void E_(Close_LuaIntf)( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)p_this;
+
+    lua_close( p_intf->p_sys->L );
+    free( p_intf->p_sys );
+}
+
+static void Run( intf_thread_t *p_intf )
+{
+    lua_State *L = p_intf->p_sys->L;
+
+    if( luaL_dofile( L, p_intf->p_sys->psz_filename ) )
+    {
+        msg_Err( p_intf, "Error loading script %s: %s",
+                 p_intf->p_sys->psz_filename,
+                 lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        p_intf->b_die = VLC_TRUE;
+        return;
+    }
+    p_intf->b_die = VLC_TRUE;
+}
diff --git a/modules/misc/lua/luameta.c b/modules/misc/lua/luameta.c
deleted file mode 100644 (file)
index 4374ada..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*****************************************************************************
- * luameta.c: Get meta/artwork using lua scripts
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- *          Pierre d'Herbemont <pdherbemont # videolan.org>
- *
- * 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
- *****************************************************************************/
-#ifndef  _GNU_SOURCE
-#   define  _GNU_SOURCE
-#endif
-
-#include <vlc/vlc.h>
-#include <vlc_input.h>
-#include <vlc_playlist.h>
-#include <vlc_meta.h>
-#include <vlc_url.h>
-#include <vlc_strings.h>
-#include <vlc_stream.h>
-#include <vlc_charset.h>
-
-#include "vlclua.h"
-
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
-                       lua_State * p_state, void * user_data );
-static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
-                      lua_State * p_state, void * user_data );
-static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item );
-
-
-/*****************************************************************************
- * Lua function bridge
- *****************************************************************************/
-/* Functions to register */
-static luaL_Reg p_reg[] =
-{
-    { "stream_new", vlclua_stream_new },
-    { "stream_read", vlclua_stream_read },
-    { "stream_readline", vlclua_stream_readline },
-    { "stream_delete", vlclua_stream_delete },
-    { "decode_uri", vlclua_decode_uri },
-    { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
-    { "msg_dbg", vlclua_msg_dbg },
-    { "msg_warn", vlclua_msg_warn },
-    { "msg_err", vlclua_msg_err },
-    { "msg_info", vlclua_msg_info },
-    { NULL, NULL }
-};
-
-/*****************************************************************************
- * Init lua
- *****************************************************************************/
-static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item )
-{
-    lua_State * p_state = luaL_newstate();
-    if( !p_state )
-    {
-        msg_Err( p_this, "Could not create new Lua State" );
-        return NULL;
-    }
-    char *psz_meta;
-
-    /* Load Lua libraries */
-    luaL_openlibs( p_state ); /* XXX: Don't open all the libs? */
-
-    luaL_register( p_state, "vlc", p_reg );
-
-    lua_pushlightuserdata( p_state, p_this );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
-
-    psz_meta = input_item_GetName( p_item );
-    lua_pushstring( p_state, psz_meta );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "name" );
-    free( psz_meta );
-
-    psz_meta = input_item_GetArtist( p_item );
-    lua_pushstring( p_state, psz_meta );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "artist" );
-    free( psz_meta );
-
-    psz_meta = input_item_GetTitle( p_item ) ;
-    lua_pushstring( p_state, psz_meta );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "title" );
-    free( psz_meta );
-
-    psz_meta = input_item_GetAlbum( p_item );
-    lua_pushstring( p_state, psz_meta );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "album" );
-    free( psz_meta );
-
-    psz_meta = input_item_GetArtURL( p_item );
-    lua_pushstring( p_state, psz_meta );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "arturl" );
-    free( psz_meta );
-    /* XXX: all should be passed ( could use macro ) */
-
-    return p_state;
-}
-
-/*****************************************************************************
- * Called through lua_scripts_batch_execute to call 'fetch_art' on the script
- * pointed by psz_filename.
- *****************************************************************************/
-static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
-                      lua_State * p_state, void * user_data )
-{
-    int i_ret = VLC_EGENERIC;
-    input_item_t * p_input = user_data;
-    int s;
-
-    /* Ugly hack to delete previous versions of the fetchart()
-    * functions. */
-    lua_pushnil( p_state );
-    lua_setglobal( p_state, "fetch_art" );
-
-    /* Load and run the script(s) */
-    if( luaL_dofile( p_state, psz_filename ) )
-    {
-        msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-    lua_getglobal( p_state, "fetch_art" );
-
-    if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
-    {
-        msg_Warn( p_this, "Error while runing script %s, "
-                  "function fetch_art() not found", psz_filename );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-    if( lua_pcall( p_state, 0, 1, 0 ) )
-    {
-        msg_Warn( p_this, "Error while runing script %s, "
-                  "function fetch_art(): %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-    if((s = lua_gettop( p_state )))
-    {
-        const char * psz_value;
-
-        if( lua_isstring( p_state, s ) )
-        {
-            psz_value = lua_tostring( p_state, s );
-            if( psz_value && *psz_value != 0 )
-            {
-                lua_Dbg( p_this, "setting arturl: %s", psz_value );
-                input_item_SetArtURL ( p_input, psz_value );
-                i_ret = VLC_SUCCESS;
-            }
-        }
-        else if( !lua_isnil( p_state, s ) )
-        {
-            msg_Err( p_this, "Lua art fetcher script %s: "
-                 "didn't return a string", psz_filename );
-        }
-    }
-    else
-    {
-        msg_Err( p_this, "Script went completely foobar" );
-    }
-
-    return i_ret;
-}
-
-/*****************************************************************************
- * Called through lua_scripts_batch_execute to call 'fetch_meta' on the script
- * pointed by psz_filename.
- *****************************************************************************/
-static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
-                       lua_State * p_state, void * user_data )
-{
-    input_item_t * p_input = user_data;
-    int t;
-
-    /* Ugly hack to delete previous versions of the fetchmeta()
-    * functions. */
-    lua_pushnil( p_state );
-    lua_setglobal( p_state, "fetch_meta" );
-
-    /* Load and run the script(s) */
-    if( luaL_dofile( p_state, psz_filename ) )
-    {
-        msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-    lua_getglobal( p_state, "fetch_meta" );
-
-    if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
-    {
-        msg_Warn( p_this, "Error while runing script %s, "
-                  "function fetch_meta() not found", psz_filename );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-    if( lua_pcall( p_state, 0, 1, 0 ) )
-    {
-        msg_Warn( p_this, "Error while runing script %s, "
-                  "function fetch_meta(): %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-
-
-    if((t = lua_gettop( p_state )))
-    {
-        if( lua_istable( p_state, t ) )
-        {
-            vlclua_read_meta_data( p_this, p_state, t, t+1, p_input );
-            vlclua_read_custom_meta_data( p_this, p_state, t, t+1, p_input );
-        }
-        else
-        {
-            msg_Err( p_this, "Lua playlist script %s: "
-                 "didn't return a table", psz_filename );
-        }
-    }
-    else
-    {
-        msg_Err( p_this, "Script went completely foobar" );
-    }
-
-    /* We tell the batch thing to continue, hence all script
-     * will get the change to add its meta. This behaviour could
-     * be changed. */
-    return VLC_EGENERIC;
-}
-
-/*****************************************************************************
- * Module entry point for meta.
- *****************************************************************************/
-int E_(FindMeta)( vlc_object_t *p_this )
-{
-    meta_engine_t *p_me = (meta_engine_t *)p_this;
-    input_item_t *p_item = p_me->p_item;
-    lua_State *p_state = vlclua_meta_init( p_this, p_item );
-
-    int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, p_state, p_item );
-    lua_close( p_state );
-    return i_ret;
-}
-
-/*****************************************************************************
- * Module entry point for art.
- *****************************************************************************/
-int E_(FindArt)( vlc_object_t *p_this )
-{
-    playlist_t *p_playlist = (playlist_t *)p_this;
-    input_item_t *p_item = (input_item_t *)(p_playlist->p_private);
-    lua_State *p_state = vlclua_meta_init( p_this, p_item );
-
-    int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, p_state, p_item );
-    lua_close( p_state );
-    return i_ret;
-}
-
diff --git a/modules/misc/lua/luaplaylist.c b/modules/misc/lua/luaplaylist.c
deleted file mode 100644 (file)
index dd883d6..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-/*****************************************************************************
- * luaplaylist.c :  Lua playlist demux module
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- *
- * 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
- *****************************************************************************/
-#include <vlc/vlc.h>
-#include <vlc_demux.h>
-#include <vlc_url.h>
-#include <vlc_strings.h>
-#include <vlc_charset.h>
-
-#include <errno.h>                                                 /* ENOMEM */
-#ifdef HAVE_SYS_STAT_H
-#   include <sys/stat.h>
-#endif
-
-#include "vlclua.h"
-
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-static int Demux( demux_t *p_demux );
-static int Control( demux_t *p_demux, int i_query, va_list args );
-
-/*****************************************************************************
- *
- *****************************************************************************/
-struct demux_sys_t
-{
-    lua_State *p_state;
-    char *psz_filename;
-};
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-static int vlclua_demux_peek( lua_State *p_state )
-{
-    demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    int n;
-    byte_t *p_peek;
-    int i_peek;
-    if( !i ) return 0;
-    n = lua_tonumber( p_state, 1 );
-    lua_pop( p_state, i );
-    i_peek = stream_Peek( p_demux->s, &p_peek, n );
-    lua_pushlstring( p_state, (const char *)p_peek, i_peek );
-    return 1;
-}
-
-static int vlclua_demux_read( lua_State *p_state )
-{
-    demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    int n;
-    byte_t *p_read;
-    int i_read;
-    if( !i ) return 0;
-    n = lua_tonumber( p_state, 1 );
-    lua_pop( p_state, i );
-    i_read = stream_Read( p_demux->s, &p_read, n );
-    lua_pushlstring( p_state, (const char *)p_read, i_read );
-    return 1;
-}
-
-static int vlclua_demux_readline( lua_State *p_state )
-{
-    demux_t *p_demux = (demux_t *)vlclua_get_this( p_state );
-    char *psz_line = stream_ReadLine( p_demux->s );
-    if( psz_line )
-    {
-        lua_pushstring( p_state, psz_line );
-        free( psz_line );
-    }
-    else
-    {
-        lua_pushnil( p_state );
-    }
-    return 1;
-}
-
-
-/* Functions to register */
-static luaL_Reg p_reg[] =
-{
-    { "peek", vlclua_demux_peek },
-    { "decode_uri", vlclua_decode_uri },
-    { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
-    { "msg_dbg", vlclua_msg_dbg },
-    { "msg_warn", vlclua_msg_warn },
-    { "msg_err", vlclua_msg_err },
-    { "msg_info", vlclua_msg_info },
-    { NULL, NULL }
-};
-
-/* Functions to register for parse() function call only */
-static luaL_Reg p_reg_parse[] =
-{
-    { "read", vlclua_demux_read },
-    { "readline", vlclua_demux_readline },
-    { NULL, NULL }
-};
-
-/*****************************************************************************
- * Called through lua_scripts_batch_execute to call 'probe' on
- * the script pointed by psz_filename.
- *****************************************************************************/
-static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
-                            lua_State * p_state, void * user_data )
-{
-    demux_t * p_demux = (demux_t *)p_this;
-
-    p_demux->p_sys->psz_filename = strdup(psz_filename);
-    /* Ugly hack to delete previous versions of the probe() and parse()
-    * functions. */
-    lua_pushnil( p_state );
-    lua_pushnil( p_state );
-    lua_setglobal( p_state, "probe" );
-    lua_setglobal( p_state, "parse" );
-    /* Load and run the script(s) */
-    if( luaL_dofile( p_state, psz_filename ) )
-    {
-        msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-    lua_getglobal( p_state, "probe" );
-    if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
-    {
-        msg_Warn( p_demux, "Error while runing script %s, "
-                  "function probe() not found", psz_filename );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-    if( lua_pcall( p_state, 0, 1, 0 ) )
-    {
-        msg_Warn( p_demux, "Error while runing script %s, "
-                  "function probe(): %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        lua_pop( p_state, 1 );
-        return VLC_EGENERIC;
-    }
-    if( lua_gettop( p_state ) )
-    {
-        int i_ret = VLC_EGENERIC;
-        if( lua_toboolean( p_state, 1 ) )
-        {
-            msg_Dbg( p_demux, "Lua playlist script %s's "
-                     "probe() function was successful", psz_filename );
-            i_ret = VLC_SUCCESS;
-        }
-        lua_pop( p_state, 1 );
-        return i_ret;
-    }
-    return VLC_EGENERIC;
-}
-
-/*****************************************************************************
- * Import_LuaPlaylist: main import function
- *****************************************************************************/
-int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
-{
-    demux_t *p_demux = (demux_t *)p_this;
-    lua_State *p_state;
-
-    p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
-    if( !p_demux->p_sys )
-    {
-        return VLC_ENOMEM;
-    }
-
-    p_demux->p_sys->psz_filename = NULL;
-
-    p_demux->pf_control = Control;
-    p_demux->pf_demux = Demux;
-
-    /* Initialise Lua state structure */
-    p_state = luaL_newstate();
-    if( !p_state )
-    {
-        msg_Err( p_demux, "Could not create new Lua State" );
-        free( p_demux->p_sys );
-        return VLC_EGENERIC;
-    }
-    p_demux->p_sys->p_state = p_state;
-
-    /* Load Lua libraries */
-    luaL_openlibs( p_state ); /* FIXME: Don't open all the libs? */
-
-    luaL_register( p_state, "vlc", p_reg );
-    lua_pushlightuserdata( p_state, p_demux );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "private" );
-    lua_pushstring( p_state, p_demux->psz_path );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "path" );
-    lua_pushstring( p_state, p_demux->psz_access );
-    lua_setfield( p_state, lua_gettop( p_state ) - 1, "access" );
-
-    lua_pop( p_state, 1 );
-
-    return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
-                                         p_state, NULL );
-}
-
-
-
-/*****************************************************************************
- * Deactivate: frees unused data
- *****************************************************************************/
-void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
-{
-    demux_t *p_demux = (demux_t *)p_this;
-    lua_close( p_demux->p_sys->p_state );
-    free( p_demux->p_sys->psz_filename );
-    free( p_demux->p_sys );
-}
-
-static inline void read_options( demux_t *p_demux, lua_State *p_state,
-                                 int o, int t, int *pi_options,
-                                 char ***pppsz_options )
-{
-    lua_getfield( p_state, o, "options" );
-    if( lua_istable( p_state, t ) )
-    {
-        lua_pushnil( p_state );
-        while( lua_next( p_state, t ) )
-        {
-            if( lua_isstring( p_state, t+2 ) )
-            {
-                char *psz_option = strdup( lua_tostring( p_state, t+2 ) );
-                msg_Dbg( p_demux, "Option: %s", psz_option );
-                INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
-                             psz_option );
-            }
-            else
-            {
-                msg_Warn( p_demux, "Option should be a string" );
-            }
-            lua_pop( p_state, 1 ); /* pop option */
-        }
-    }
-    lua_pop( p_state, 1 ); /* pop "options" */
-}
-
-
-static int Demux( demux_t *p_demux )
-{
-    input_item_t *p_input;
-    lua_State *p_state = p_demux->p_sys->p_state;
-    char *psz_filename = p_demux->p_sys->psz_filename;
-    int t;
-
-    playlist_t *p_playlist = pl_Yield( p_demux );
-    input_thread_t *p_input_thread = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
-    input_item_t *p_current_input = input_GetItem( p_input_thread );
-
-    luaL_register( p_state, "vlc", p_reg_parse );
-
-    lua_getglobal( p_state, "parse" );
-
-    if( !lua_isfunction( p_state, lua_gettop( p_state ) ) )
-    {
-        msg_Warn( p_demux, "Error while runing script %s, "
-                  "function parse() not found", psz_filename );
-        E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
-        return VLC_EGENERIC;
-    }
-
-    if( lua_pcall( p_state, 0, 1, 0 ) )
-    {
-        msg_Warn( p_demux, "Error while runing script %s, "
-                  "function parse(): %s", psz_filename,
-                  lua_tostring( p_state, lua_gettop( p_state ) ) );
-        E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
-        return VLC_EGENERIC;
-    }
-
-    /* Check that the Lua stack is big enough and grow it if needed.
-     * Should be ok since LUA_MINSTACK is 20 but we never know. */
-    lua_checkstack( p_state, 8 );
-
-    if( ( t = lua_gettop( p_state ) ) )
-    {
-
-        if( lua_istable( p_state, t ) )
-        {
-            lua_pushnil( p_state );
-            while( lua_next( p_state, t ) )
-            {
-                if( lua_istable( p_state, t+2 ) )
-                {
-                    lua_getfield( p_state, t+2, "path" );
-                    if( lua_isstring( p_state, t+3 ) )
-                    {
-                        const char  *psz_path     = NULL;
-                        const char  *psz_name     = NULL;
-                        char       **ppsz_options = NULL;
-                        int          i_options    = 0;
-                        mtime_t      i_duration   = -1;
-
-                        /* Read path and name */
-                        psz_path = lua_tostring( p_state, t+3 );
-                        msg_Dbg( p_demux, "Path: %s", psz_path );
-                        lua_getfield( p_state, t+2, "name" );
-                        if( lua_isstring( p_state, t+4 ) )
-                        {
-                            psz_name = lua_tostring( p_state, t+4 );
-                            msg_Dbg( p_demux, "Name: %s", psz_name );
-                        }
-                        else
-                        {
-                            psz_name = psz_path;
-                        }
-
-                        /* Read duration */
-                        lua_getfield( p_state, t+2, "duration" );
-                        if( lua_isnumber( p_state, t+5 ) )
-                        {
-                            i_duration = (mtime_t)lua_tointeger( p_state, t+5 );
-                            i_duration *= 1000000;
-                        }
-                        lua_pop( p_state, 1 ); /* pop "duration" */
-
-                        /* Read options */
-                        read_options( p_demux, p_state, t+2, t+5,
-                                      &i_options, &ppsz_options );
-
-                        /* Create input item */
-                        p_input = input_ItemNewExt( p_playlist, psz_path,
-                                                    psz_name, i_options,
-                                                    (const char **)ppsz_options,
-                                                    i_duration );
-                        lua_pop( p_state, 1 ); /* pop "name" */
-
-                        /* Read meta data */
-                        vlclua_read_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4, p_input );
-
-                        /* Read custom meta data */
-                        vlclua_read_custom_meta_data( VLC_OBJECT(p_demux), p_state, t+2, t+4,
-                                               p_input );
-
-                        /* Append item to playlist */
-                        input_ItemAddSubItem( p_current_input, p_input );
-
-                        while( i_options > 0 )
-                            free( ppsz_options[--i_options] );
-                        free( ppsz_options );
-                    }
-                    else
-                    {
-                        msg_Warn( p_demux,
-                                 "Playlist item's path should be a string" );
-                    }
-                    lua_pop( p_state, 1 ); /* pop "path" */
-                }
-                else
-                {
-                    msg_Warn( p_demux, "Playlist item should be a table" );
-                }
-                lua_pop( p_state, 1 ); /* pop the value, keep the key for
-                                        * the next lua_next() call */
-            }
-        }
-        else
-        {
-            msg_Warn( p_demux, "Script didn't return a table" );
-        }
-    }
-    else
-    {
-        msg_Err( p_demux, "Script went completely foobar" );
-    }
-
-    vlc_object_release( p_input_thread );
-    vlc_object_release( p_playlist );
-
-    return -1; /* Needed for correct operation of go back */
-}
-
-static int Control( demux_t *p_demux, int i_query, va_list args )
-{
-    return VLC_EGENERIC;
-}
diff --git a/modules/misc/lua/meta.c b/modules/misc/lua/meta.c
new file mode 100644 (file)
index 0000000..9566c73
--- /dev/null
@@ -0,0 +1,304 @@
+/*****************************************************************************
+ * meta.c: Get meta/artwork using lua scripts
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *          Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_input.h>
+#include <vlc_playlist.h>
+#include <vlc_meta.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_stream.h>
+#include <vlc_charset.h>
+
+#include "vlc.h"
+
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
+                       lua_State * L, void * user_data );
+static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
+                      lua_State * L, void * user_data );
+static lua_State *vlclua_meta_init( vlc_object_t *p_this,
+                                    input_item_t * p_item );
+
+
+/*****************************************************************************
+ * Lua function bridge
+ *****************************************************************************/
+/* Functions to register */
+static luaL_Reg p_reg[] =
+{
+    /* TODO: make an object out of the stream stuff, so we can do something
+     * like:
+     *
+     * s = vlc.stream_new( "http://www.videolan.org" )
+     * page = s:read( 2^16 )
+     * s:delete()
+     *
+     * instead of (which could be problematic since we don't check if
+     * s is really a stream object):
+     *
+     * s = vlc.stream_new( "http://www.videolan.org" )
+     * page = vlc.stream_read( s, 2^16 )
+     * vlc.stream_delete( s )
+     */
+    { "stream_new", vlclua_stream_new },
+    { "stream_read", vlclua_stream_read },
+    { "stream_readline", vlclua_stream_readline },
+    { "stream_delete", vlclua_stream_delete },
+    { "decode_uri", vlclua_decode_uri },
+    { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
+    { "msg_dbg", vlclua_msg_dbg },
+    { "msg_warn", vlclua_msg_warn },
+    { "msg_err", vlclua_msg_err },
+    { "msg_info", vlclua_msg_info },
+    { NULL, NULL }
+};
+
+/*****************************************************************************
+ * Init lua
+ *****************************************************************************/
+static lua_State * vlclua_meta_init( vlc_object_t *p_this, input_item_t * p_item )
+{
+    lua_State * L = luaL_newstate();
+    if( !L )
+    {
+        msg_Err( p_this, "Could not create new Lua State" );
+        return NULL;
+    }
+    char *psz_meta;
+
+    /* Load Lua libraries */
+    luaL_openlibs( L ); /* XXX: Don't open all the libs? */
+
+    luaL_register( L, "vlc", p_reg );
+
+    lua_pushlightuserdata( L, p_this );
+    lua_setfield( L, -2, "private" );
+
+    psz_meta = input_item_GetName( p_item );
+    lua_pushstring( L, psz_meta );
+    lua_setfield( L, -2, "name" );
+    free( psz_meta );
+
+    psz_meta = input_item_GetArtist( p_item );
+    lua_pushstring( L, psz_meta );
+    lua_setfield( L, -2, "artist" );
+    free( psz_meta );
+
+    psz_meta = input_item_GetTitle( p_item ) ;
+    lua_pushstring( L, psz_meta );
+    lua_setfield( L, -2, "title" );
+    free( psz_meta );
+
+    psz_meta = input_item_GetAlbum( p_item );
+    lua_pushstring( L, psz_meta );
+    lua_setfield( L, -2, "album" );
+    free( psz_meta );
+
+    psz_meta = input_item_GetArtURL( p_item );
+    lua_pushstring( L, psz_meta );
+    lua_setfield( L, -2, "arturl" );
+    free( psz_meta );
+    /* XXX: all should be passed ( could use macro ) */
+
+    return L;
+}
+
+/*****************************************************************************
+ * Called through lua_scripts_batch_execute to call 'fetch_art' on the script
+ * pointed by psz_filename.
+ *****************************************************************************/
+static int fetch_art( vlc_object_t *p_this, const char * psz_filename,
+                      lua_State * L, void * user_data )
+{
+    int i_ret = VLC_EGENERIC;
+    input_item_t * p_input = user_data;
+    int s;
+
+    /* Ugly hack to delete previous versions of the fetchart()
+    * functions. */
+    lua_pushnil( L );
+    lua_setglobal( L, "fetch_art" );
+
+    /* Load and run the script(s) */
+    if( luaL_dofile( L, psz_filename ) )
+    {
+        msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    lua_getglobal( L, "fetch_art" );
+
+    if( !lua_isfunction( L, lua_gettop( L ) ) )
+    {
+        msg_Warn( p_this, "Error while runing script %s, "
+                  "function fetch_art() not found", psz_filename );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_pcall( L, 0, 1, 0 ) )
+    {
+        msg_Warn( p_this, "Error while runing script %s, "
+                  "function fetch_art(): %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    if((s = lua_gettop( L )))
+    {
+        const char * psz_value;
+
+        if( lua_isstring( L, s ) )
+        {
+            psz_value = lua_tostring( L, s );
+            if( psz_value && *psz_value != 0 )
+            {
+                lua_Dbg( p_this, "setting arturl: %s", psz_value );
+                input_item_SetArtURL ( p_input, psz_value );
+                i_ret = VLC_SUCCESS;
+            }
+        }
+        else if( !lua_isnil( L, s ) )
+        {
+            msg_Err( p_this, "Lua art fetcher script %s: "
+                 "didn't return a string", psz_filename );
+        }
+    }
+    else
+    {
+        msg_Err( p_this, "Script went completely foobar" );
+    }
+
+    return i_ret;
+}
+
+/*****************************************************************************
+ * Called through lua_scripts_batch_execute to call 'fetch_meta' on the script
+ * pointed by psz_filename.
+ *****************************************************************************/
+static int fetch_meta( vlc_object_t *p_this, const char * psz_filename,
+                       lua_State * L, void * user_data )
+{
+    input_item_t * p_input = user_data;
+
+    /* In lua, setting a variable to nil is equivalent to deleting it */
+    lua_pushnil( L );
+    lua_setglobal( L, "fetch_meta" );
+
+    /* Load and run the script(s) */
+    if( luaL_dofile( L, psz_filename ) )
+    {
+        msg_Warn( p_this, "Error loading script %s: %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    lua_getglobal( L, "fetch_meta" );
+
+    if( !lua_isfunction( L, lua_gettop( L ) ) )
+    {
+        msg_Warn( p_this, "Error while runing script %s, "
+                  "function fetch_meta() not found", psz_filename );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_pcall( L, 0, 1, 0 ) )
+    {
+        msg_Warn( p_this, "Error while runing script %s, "
+                  "function fetch_meta(): %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+
+    if( lua_gettop( L ) )
+    {
+        if( lua_istable( L, -1 ) )
+        {
+            /* ... item */
+            vlclua_read_meta_data( p_this, L, p_input );
+            vlclua_read_custom_meta_data( p_this, L, p_input );
+        }
+        else
+        {
+            msg_Err( p_this, "Lua playlist script %s: "
+                     "didn't return a table", psz_filename );
+        }
+    }
+    else
+    {
+        msg_Err( p_this, "Script went completely foobar" );
+    }
+
+    /* We tell the batch thing to continue, hence all script
+     * will get the change to add its meta. This behaviour could
+     * be changed. */
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Module entry point for meta.
+ *****************************************************************************/
+int E_(FindMeta)( vlc_object_t *p_this )
+{
+    meta_engine_t *p_me = (meta_engine_t *)p_this;
+    input_item_t *p_item = p_me->p_item;
+    lua_State *L = vlclua_meta_init( p_this, p_item );
+
+    int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_meta, L, p_item );
+    lua_close( L );
+    return i_ret;
+}
+
+/*****************************************************************************
+ * Module entry point for art.
+ *****************************************************************************/
+int E_(FindArt)( vlc_object_t *p_this )
+{
+    playlist_t *p_playlist = (playlist_t *)p_this;
+    input_item_t *p_item = (input_item_t *)(p_playlist->p_private);
+    lua_State *L = vlclua_meta_init( p_this, p_item );
+
+    int i_ret = vlclua_scripts_batch_execute( p_this, "luameta", &fetch_art, L, p_item );
+    lua_close( L );
+    return i_ret;
+}
+
diff --git a/modules/misc/lua/net.c b/modules/misc/lua/net.c
new file mode 100644 (file)
index 0000000..345d0f1
--- /dev/null
@@ -0,0 +1,207 @@
+/*****************************************************************************
+ * net.c: Network related functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_network.h>
+#include <vlc_url.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+int vlclua_url_parse( lua_State *L )
+{
+    const char *psz_url = luaL_checkstring( L, 1 );
+    const char *psz_option = luaL_optstring( L, 2, NULL );
+    vlc_url_t url;
+
+    vlc_UrlParse( &url, psz_url, psz_option?*psz_option:0 );
+
+    lua_newtable( L );
+    lua_pushstring( L, url.psz_protocol );
+    lua_setfield( L, -2, "protocol" );
+    lua_pushstring( L, url.psz_username );
+    lua_setfield( L, -2, "username" );
+    lua_pushstring( L, url.psz_password );
+    lua_setfield( L, -2, "password" );
+    lua_pushstring( L, url.psz_host );
+    lua_setfield( L, -2, "host" );
+    lua_pushinteger( L, url.i_port );
+    lua_setfield( L, -2, "port" );
+    lua_pushstring( L, url.psz_path );
+    lua_setfield( L, -2, "path" );
+    lua_pushstring( L, url.psz_option );
+    lua_setfield( L, -2, "option" );
+
+    vlc_UrlClean( &url );
+
+    return 1;
+}
+
+int vlclua_net_listen_tcp( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    const char *psz_host = luaL_checkstring( L, 1 );
+    int i_port = luaL_checkint( L, 2 );
+    int *pi_fd = net_ListenTCP( p_this, psz_host, i_port );
+    if( pi_fd == NULL )
+        return luaL_error( L, "Cannot listen on %s:%d", psz_host, i_port );
+    lua_pushlightuserdata( L, pi_fd );
+    return 1;
+}
+
+int vlclua_net_listen_close( lua_State *L )
+{
+    int *pi_fd = (int*)luaL_checklightuserdata( L, 1 );
+    net_ListenClose( pi_fd );
+    return 0;
+}
+
+int vlclua_net_accept( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    int *pi_fd = (int*)luaL_checklightuserdata( L, 1 );
+    mtime_t i_wait = luaL_optint( L, 2, -1 ); /* default to block */
+    int i_fd = net_Accept( p_this, pi_fd, i_wait );
+    lua_pushinteger( L, i_fd );
+    return 1;
+}
+
+int vlclua_net_close( lua_State *L )
+{
+    int i_fd = luaL_checkint( L, 1 );
+    net_Close( i_fd );
+    return 0;
+}
+
+int vlclua_net_send( lua_State *L )
+{
+    int i_fd = luaL_checkint( L, 1 );
+    size_t i_len;
+    const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
+    i_len = luaL_optint( L, 3, i_len );
+    i_len = send( i_fd, psz_buffer, i_len, 0 );
+    lua_pushinteger( L, i_len );
+    return 1;
+}
+
+int vlclua_net_recv( lua_State *L )
+{
+    int i_fd = luaL_checkint( L, 1 );
+    size_t i_len = luaL_optint( L, 2, 1 );
+    char psz_buffer[i_len];
+    i_len = recv( i_fd, psz_buffer, i_len, 0 );
+    lua_pushlstring( L, psz_buffer, i_len );
+    return 1;
+}
+
+int vlclua_net_select( lua_State *L )
+{
+    int i_ret;
+    int i_nfds = luaL_checkint( L, 1 );
+    fd_set *fds_read = (fd_set*)luaL_checkuserdata( L, 2, sizeof( fd_set ) );
+    fd_set *fds_write = (fd_set*)luaL_checkuserdata( L, 3, sizeof( fd_set ) );
+    double f_timeout = luaL_checknumber( L, 4 );
+    struct timeval timeout;
+    timeout.tv_sec = (int)f_timeout;
+    timeout.tv_usec = (int)(1e6*(f_timeout-(double)((int)f_timeout)));
+    i_ret = select( i_nfds, fds_read, fds_write, 0, &timeout );
+    lua_pushinteger( L, i_ret );
+    lua_pushinteger( L, (double)timeout.tv_sec+((double)timeout.tv_usec)/1e-6 );
+    return 2;
+}
+
+int vlclua_fd_set_new( lua_State *L )
+{
+    fd_set *fds = (fd_set*)lua_newuserdata( L, sizeof( fd_set ) );
+    FD_ZERO( fds );
+    return 1;
+}
+
+int vlclua_fd_clr( lua_State *L )
+{
+    fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+    int i_fd = luaL_checkint( L, 2 );
+    FD_CLR( i_fd, fds );
+    return 0;
+}
+int vlclua_fd_isset( lua_State *L )
+{
+    fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+    int i_fd = luaL_checkint( L, 2 );
+    lua_pushboolean( L, FD_ISSET( i_fd, fds ) );
+    return 1;
+}
+int vlclua_fd_set( lua_State *L )
+{
+    fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+    int i_fd = luaL_checkint( L, 2 );
+    FD_SET( i_fd, fds );
+    return 0;
+}
+int vlclua_fd_zero( lua_State *L )
+{
+    fd_set *fds = (fd_set*)luaL_checkuserdata( L, 1, sizeof( fd_set ) );
+    FD_ZERO( fds );
+    return 0;
+}
+
+/*
+int vlclua_fd_open( lua_State *L )
+{
+}
+*/
+
+int vlclua_fd_write( lua_State *L )
+{
+    int i_fd = luaL_checkint( L, 1 );
+    size_t i_len;
+    ssize_t i_ret;
+    const char *psz_buffer = luaL_checklstring( L, 2, &i_len );
+    i_len = luaL_optint( L, 3, i_len );
+    i_ret = write( i_fd, psz_buffer, i_len );
+    lua_pushinteger( L, i_ret );
+    return 1;
+}
+
+int vlclua_fd_read( lua_State *L )
+{
+    int i_fd = luaL_checkint( L, 1 );
+    size_t i_len = luaL_optint( L, 2, 1 );
+    char psz_buffer[i_len];
+    i_len = read( i_fd, psz_buffer, i_len );
+    lua_pushlstring( L, psz_buffer, i_len );
+    return 1;
+}
diff --git a/modules/misc/lua/objects.c b/modules/misc/lua/objects.c
new file mode 100644 (file)
index 0000000..2e4dc40
--- /dev/null
@@ -0,0 +1,218 @@
+/*****************************************************************************
+ * objects.c: Generic lua<->vlc object wrapper
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+typedef struct
+{
+    vlc_object_t *p_obj;
+} vlclua_object_t;
+
+/*****************************************************************************
+ * Generic vlc_object_t wrapper creation
+ *****************************************************************************/
+int __vlclua_push_vlc_object( lua_State *L, vlc_object_t *p_obj,
+                              lua_CFunction pf_gc )
+{
+    vlclua_object_t *p_ud = (vlclua_object_t *)
+        lua_newuserdata( L, sizeof( vlclua_object_t ) );
+    p_ud->p_obj = p_obj;
+    lua_newtable( L );
+    /* Hide the metatable */
+    lua_pushstring( L, "__metatable" );
+    lua_pushstring( L, "none of your business" );
+    lua_settable( L, -3 );
+    if( pf_gc )
+    {
+        /* Set the garbage collector if needed */
+        lua_pushstring( L, "__gc" );
+        lua_pushcfunction( L, pf_gc );
+        lua_settable( L, -3 );
+    }
+    lua_setmetatable( L, -2 );
+    return 1;
+}
+
+int vlclua_gc_release( lua_State *L )
+{
+    vlclua_object_t *p_ud = (vlclua_object_t *)lua_touserdata( L, -1 );
+    lua_pop( L, 1 );
+    vlc_object_release( p_ud->p_obj );
+    return 0;
+}
+
+vlc_object_t *vlclua_checkobject( lua_State *L, int narg, int i_type )
+{
+    vlclua_object_t *p_obj = (vlclua_object_t *)luaL_checkuserdata( L, narg, sizeof( vlclua_object_t ) );
+    /* TODO: add some metatable based method to check that this isn't
+     * any userdata, but really some vlc object */
+    if( i_type )
+    {
+        if( p_obj->p_obj->i_object_type == i_type )
+            return p_obj->p_obj;
+        else
+        {
+            luaL_error( L, "VLC object type doesn't match requirements." );
+            return NULL; /* luaL_error alread longjmp-ed out of here.
+                          * This is to make gcc happy */
+        }
+    }
+    else
+    {
+        return p_obj->p_obj;
+    }
+}
+
+static int vlc_object_type_from_string( const char *psz_name )
+{
+    static const struct
+    {
+        int i_type;
+        const char *psz_name;
+    } pp_objects[] =
+        { { VLC_OBJECT_GLOBAL, "global" },
+          { VLC_OBJECT_LIBVLC, "libvlc" },
+          { VLC_OBJECT_MODULE, "module" },
+          { VLC_OBJECT_INTF, "intf" },
+          { VLC_OBJECT_PLAYLIST, "playlist" },
+          { VLC_OBJECT_ITEM, "item" },
+          { VLC_OBJECT_INPUT, "input" },
+          { VLC_OBJECT_DECODER, "decoder" },
+          { VLC_OBJECT_VOUT, "vout" },
+          { VLC_OBJECT_AOUT, "aout" },
+          { VLC_OBJECT_SOUT, "sout" },
+          { VLC_OBJECT_HTTPD, "httpd" },
+          { VLC_OBJECT_PACKETIZER, "packetizer" },
+          { VLC_OBJECT_ENCODER, "encoder" },
+          { VLC_OBJECT_DIALOGS, "dialogs" },
+          { VLC_OBJECT_VLM, "vlm" },
+          { VLC_OBJECT_ANNOUNCE, "announce" },
+          { VLC_OBJECT_DEMUX, "demux" },
+          { VLC_OBJECT_ACCESS, "access" },
+          { VLC_OBJECT_STREAM, "stream" },
+          { VLC_OBJECT_OPENGL, "opengl" },
+          { VLC_OBJECT_FILTER, "filter" },
+          { VLC_OBJECT_VOD, "vod" },
+          { VLC_OBJECT_SPU, "spu" },
+          { VLC_OBJECT_SD, "sd" },
+          { VLC_OBJECT_XML, "xml" },
+          { VLC_OBJECT_OSDMENU, "osdmenu" },
+          { VLC_OBJECT_HTTPD_HOST, "httpd_host" },
+          { VLC_OBJECT_META_ENGINE, "meta_engine" },
+          { VLC_OBJECT_GENERIC, "generic" },
+          { 0, "" } };
+    int i;
+    for( i = 0; pp_objects[i].i_type; i++ )
+    {
+        if( !strcmp( psz_name, pp_objects[i].psz_name ) )
+            return pp_objects[i].i_type;
+    }
+    return 0;
+}
+
+static int vlc_object_search_mode_from_string( const char *psz_name )
+{
+    static const struct
+    {
+        int i_mode;
+        const char *psz_name;
+    } pp_modes[] =
+        { { FIND_PARENT, "parent" },
+          { FIND_CHILD, "child" },
+          { FIND_ANYWHERE, "anywhere" },
+          { 0, "" } };
+    int i;
+    for( i = 0; pp_modes[i].i_mode; i++ )
+    {
+        if( !strcmp( psz_name, pp_modes[i].psz_name ) )
+            return pp_modes[i].i_mode;
+    }
+    return 0;
+}
+
+int vlclua_object_find( lua_State *L )
+{
+    const char *psz_type = luaL_checkstring( L, 2 );
+    const char *psz_mode = luaL_checkstring( L, 3 );
+
+    vlc_object_t *p_this;
+    int i_type = vlc_object_type_from_string( psz_type );
+    int i_mode = vlc_object_search_mode_from_string( psz_mode );
+    vlc_object_t *p_result;
+
+    if( !i_type )
+        return luaL_error( L, "\"%s\" is not a valid object type.", psz_type );
+    if( !i_mode )
+        return luaL_error( L, "\"%s\" is not a valid search mode.", psz_mode );
+
+    if( lua_type( L, 1 ) == LUA_TNIL )
+        p_this = vlclua_get_this( L );
+    else
+        p_this = vlclua_checkobject( L, 1, 0 );
+
+    p_result = vlc_object_find( p_this, i_type, i_mode );
+    if( !p_result )
+        lua_pushnil( L );
+    else
+        vlclua_push_vlc_object( L, p_result, vlclua_gc_release );
+    return 1;
+}
+
+int vlclua_object_find_name( lua_State *L )
+{
+    const char *psz_name = luaL_checkstring( L, 2 );
+    const char *psz_mode = luaL_checkstring( L, 3 );
+
+    vlc_object_t *p_this;
+    int i_mode = vlc_object_search_mode_from_string( psz_mode );
+    vlc_object_t *p_result;
+
+    if( !i_mode )
+        return luaL_error( L, "\"%s\" is not a valid search mode.",
+                           psz_mode );
+
+    if( lua_type( L, 1 ) == LUA_TNIL )
+        p_this = vlclua_get_this( L );
+    else
+        p_this = vlclua_checkobject( L, 1, 0 );
+
+    p_result = vlc_object_find_name( p_this, psz_name, i_mode );
+    if( !p_result )
+        lua_pushnil( L );
+    else
+        vlclua_push_vlc_object( L, p_result, vlclua_gc_release );
+    return 1;
+}
diff --git a/modules/misc/lua/playlist.c b/modules/misc/lua/playlist.c
new file mode 100644 (file)
index 0000000..2090f91
--- /dev/null
@@ -0,0 +1,294 @@
+/*****************************************************************************
+ * playlist.c :  Lua playlist demux module
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include <vlc_demux.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_charset.h>
+
+#include <errno.h>                                                 /* ENOMEM */
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+
+#include "vlc.h"
+
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Demux( demux_t *p_demux );
+static int Control( demux_t *p_demux, int i_query, va_list args );
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+struct demux_sys_t
+{
+    lua_State *L;
+    char *psz_filename;
+};
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+static int vlclua_demux_peek( lua_State *L )
+{
+    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+    int i = lua_gettop( L );
+    int n;
+    const uint8_t *p_peek;
+    int i_peek;
+    if( !i ) return 0;
+    n = lua_tonumber( L, 1 );
+    lua_pop( L, i );
+    i_peek = stream_Peek( p_demux->s, &p_peek, n );
+    lua_pushlstring( L, (const char *)p_peek, i_peek );
+    return 1;
+}
+
+static int vlclua_demux_read( lua_State *L )
+{
+    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+    int i = lua_gettop( L );
+    int n;
+    byte_t *p_read;
+    int i_read;
+    if( !i ) return 0;
+    n = lua_tonumber( L, 1 );
+    lua_pop( L, i );
+    i_read = stream_Read( p_demux->s, &p_read, n );
+    lua_pushlstring( L, (const char *)p_read, i_read );
+    return 1;
+}
+
+static int vlclua_demux_readline( lua_State *L )
+{
+    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
+    char *psz_line = stream_ReadLine( p_demux->s );
+    if( psz_line )
+    {
+        lua_pushstring( L, psz_line );
+        free( psz_line );
+    }
+    else
+    {
+        lua_pushnil( L );
+    }
+    return 1;
+}
+
+
+/* Functions to register */
+static luaL_Reg p_reg[] =
+{
+    { "peek", vlclua_demux_peek },
+    { "decode_uri", vlclua_decode_uri },
+    { "resolve_xml_special_chars", vlclua_resolve_xml_special_chars },
+    { "msg_dbg", vlclua_msg_dbg },
+    { "msg_warn", vlclua_msg_warn },
+    { "msg_err", vlclua_msg_err },
+    { "msg_info", vlclua_msg_info },
+    { NULL, NULL }
+};
+
+/* Functions to register for parse() function call only */
+static luaL_Reg p_reg_parse[] =
+{
+    { "read", vlclua_demux_read },
+    { "readline", vlclua_demux_readline },
+    { NULL, NULL }
+};
+
+/*****************************************************************************
+ * Called through lua_scripts_batch_execute to call 'probe' on
+ * the script pointed by psz_filename.
+ *****************************************************************************/
+static int probe_luascript( vlc_object_t *p_this, const char * psz_filename,
+                            lua_State * L, void * user_data )
+{
+    demux_t * p_demux = (demux_t *)p_this;
+
+    p_demux->p_sys->psz_filename = strdup(psz_filename);
+
+    /* In lua, setting a variable's value to nil is equivalent to deleting it */
+    lua_pushnil( L );
+    lua_pushnil( L );
+    lua_setglobal( L, "probe" );
+    lua_setglobal( L, "parse" );
+
+    /* Load and run the script(s) */
+    if( luaL_dofile( L, psz_filename ) )
+    {
+        msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    lua_getglobal( L, "probe" );
+
+    if( !lua_isfunction( L, lua_gettop( L ) ) )
+    {
+        msg_Warn( p_demux, "Error while runing script %s, "
+                  "function probe() not found", psz_filename );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_pcall( L, 0, 1, 0 ) )
+    {
+        msg_Warn( p_demux, "Error while runing script %s, "
+                  "function probe(): %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        lua_pop( L, 1 );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_gettop( L ) )
+    {
+        int i_ret = VLC_EGENERIC;
+        if( lua_toboolean( L, 1 ) )
+        {
+            msg_Dbg( p_demux, "Lua playlist script %s's "
+                     "probe() function was successful", psz_filename );
+            i_ret = VLC_SUCCESS;
+        }
+        lua_pop( L, 1 );
+
+        return i_ret;
+    }
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Import_LuaPlaylist: main import function
+ *****************************************************************************/
+int E_(Import_LuaPlaylist)( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t *)p_this;
+    lua_State *L;
+
+    p_demux->p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
+    if( !p_demux->p_sys )
+    {
+        return VLC_ENOMEM;
+    }
+
+    p_demux->p_sys->psz_filename = NULL;
+
+    p_demux->pf_control = Control;
+    p_demux->pf_demux = Demux;
+
+    /* Initialise Lua state structure */
+    L = luaL_newstate();
+    if( !L )
+    {
+        msg_Err( p_demux, "Could not create new Lua State" );
+        free( p_demux->p_sys );
+        return VLC_EGENERIC;
+    }
+    p_demux->p_sys->L = L;
+
+    /* Load Lua libraries */
+    luaL_openlibs( L ); /* FIXME: Don't open all the libs? */
+
+    luaL_register( L, "vlc", p_reg );
+    lua_pushlightuserdata( L, p_demux );
+    lua_setfield( L, -2, "private" );
+    lua_pushstring( L, p_demux->psz_path );
+    lua_setfield( L, -2, "path" );
+    lua_pushstring( L, p_demux->psz_access );
+    lua_setfield( L, -2, "access" );
+
+    lua_pop( L, 1 );
+
+    return vlclua_scripts_batch_execute( p_this, "luaplaylist", &probe_luascript,
+                                         L, NULL );
+}
+
+
+
+/*****************************************************************************
+ * Deactivate: frees unused data
+ *****************************************************************************/
+void E_(Close_LuaPlaylist)( vlc_object_t *p_this )
+{
+    demux_t *p_demux = (demux_t *)p_this;
+    lua_close( p_demux->p_sys->L );
+    free( p_demux->p_sys->psz_filename );
+    free( p_demux->p_sys );
+}
+
+static int Demux( demux_t *p_demux )
+{
+    lua_State *L = p_demux->p_sys->L;
+    char *psz_filename = p_demux->p_sys->psz_filename;
+
+    playlist_t *p_playlist = pl_Yield( p_demux );
+    input_thread_t *p_input_thread = (input_thread_t *)
+        vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
+    input_item_t *p_current_input = input_GetItem( p_input_thread );
+
+    luaL_register( L, "vlc", p_reg_parse );
+
+    lua_getglobal( L, "parse" );
+
+    if( !lua_isfunction( L, lua_gettop( L ) ) )
+    {
+        msg_Warn( p_demux, "Error while runing script %s, "
+                  "function parse() not found", psz_filename );
+        E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_pcall( L, 0, 1, 0 ) )
+    {
+        msg_Warn( p_demux, "Error while runing script %s, "
+                  "function parse(): %s", psz_filename,
+                  lua_tostring( L, lua_gettop( L ) ) );
+        E_(Close_LuaPlaylist)( VLC_OBJECT( p_demux ) );
+        return VLC_EGENERIC;
+    }
+
+    if( lua_gettop( L ) )
+        vlclua_playlist_add_internal( p_demux, L, p_playlist,
+                                      p_current_input, 0 );
+    else
+        msg_Err( p_demux, "Script went completely foobar" );
+
+    vlc_object_release( p_input_thread );
+    vlc_object_release( p_playlist );
+
+    return -1; /* Needed for correct operation of go back */
+}
+
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    return VLC_EGENERIC;
+}
diff --git a/modules/misc/lua/variables.c b/modules/misc/lua/variables.c
new file mode 100644 (file)
index 0000000..ed0f93c
--- /dev/null
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * variables.c: Generic lua<->vlc variables inteface
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Variables handling
+ *****************************************************************************/
+int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val )
+{
+    switch( i_type &= 0xf0 )
+    {
+        case VLC_VAR_VOID:
+            vlclua_error( L );
+            break;
+        case VLC_VAR_BOOL:
+            lua_pushboolean( L, val.b_bool );
+            break;
+        case VLC_VAR_INTEGER:
+            lua_pushinteger( L, val.i_int );
+            break;
+        case VLC_VAR_STRING:
+            lua_pushstring( L, val.psz_string );
+            break;
+        case VLC_VAR_FLOAT:
+            lua_pushnumber( L, val.f_float );
+            break;
+        case VLC_VAR_TIME:
+            /* FIXME? (we're losing some precision, but does it really matter?) */
+            lua_pushnumber( L, ((double)val.i_time)/1000000. );
+            break;
+        case VLC_VAR_ADDRESS:
+            vlclua_error( L );
+            break;
+        case VLC_VAR_MUTEX:
+            vlclua_error( L );
+            break;
+        case VLC_VAR_LIST:
+            {
+                int i_count = val.p_list->i_count;
+                int i;
+                lua_createtable( L, i_count, 0 );
+                for( i = 0; i < i_count; i++ )
+                {
+                    lua_pushinteger( L, i+1 );
+                    if( !vlclua_pushvalue( L, val.p_list->pi_types[i],
+                                           val.p_list->p_values[i] ) )
+                        lua_pushnil( L );
+                    lua_settable( L, -3 );
+                }
+            }
+            break;
+        default:
+            vlclua_error( L );
+    }
+    return 1;
+}
+
+static int vlclua_tovalue( lua_State *L, int i_type, vlc_value_t *val )
+{
+    switch( i_type & 0xf0 )
+    {
+        case VLC_VAR_VOID:
+            break;
+        case VLC_VAR_BOOL:
+            val->b_bool = luaL_checkboolean( L, -1 );
+            break;
+        case VLC_VAR_INTEGER:
+            val->i_int = luaL_checkint( L, -1 );
+            break;
+        case VLC_VAR_STRING:
+            val->psz_string = (char*)luaL_checkstring( L, -1 ); /* XXX: Beware, this only stays valid as long as (L,-1) stays in the stack */
+            break;
+        case VLC_VAR_FLOAT:
+            val->f_float = luaL_checknumber( L, -1 );
+            break;
+        case VLC_VAR_TIME:
+            {
+                double f = luaL_checknumber( L, -1 );
+                val->i_time = (vlc_int64_t)(f*1000000.);
+            }
+            break;
+        case VLC_VAR_ADDRESS:
+            vlclua_error( L );
+            break;
+        case VLC_VAR_MUTEX:
+            vlclua_error( L );
+            break;
+        case VLC_VAR_LIST:
+            vlclua_error( L );
+            break;
+        default:
+            vlclua_error( L );
+    }
+    return 1;
+}
+
+int vlclua_var_get( lua_State *L )
+{
+    int i_type;
+    vlc_value_t val;
+    vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+    const char *psz_var = luaL_checkstring( L, 2 );
+    i_type = var_Type( p_obj, psz_var );
+    var_Get( p_obj, psz_var, &val );
+    lua_pop( L, 2 );
+    return vlclua_pushvalue( L, i_type, val );
+}
+
+int vlclua_var_set( lua_State *L )
+{
+    int i_type;
+    vlc_value_t val;
+    vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+    const char *psz_var = luaL_checkstring( L, 2 );
+    int i_ret;
+    i_type = var_Type( p_obj, psz_var );
+    vlclua_tovalue( L, i_type, &val );
+    i_ret = var_Set( p_obj, psz_var, val );
+    lua_pop( L, 3 );
+    return vlclua_push_ret( L, i_ret );
+}
+
+int vlclua_var_get_list( lua_State *L )
+{
+    vlc_value_t val;
+    vlc_value_t text;
+    vlc_object_t *p_obj = vlclua_checkobject( L, 1, 0 );
+    const char *psz_var = luaL_checkstring( L, 2 );
+    int i_ret = var_Change( p_obj, psz_var, VLC_VAR_GETLIST, &val, &text );
+    if( i_ret < 0 ) return vlclua_push_ret( L, i_ret );
+    vlclua_pushvalue( L, VLC_VAR_LIST, val );
+    vlclua_pushvalue( L, VLC_VAR_LIST, text );
+    var_Change( p_obj, psz_var, VLC_VAR_FREELIST, &val, &text );
+    return 2;
+}
+
+int vlclua_module_command( lua_State *L )
+{
+    vlc_object_t * p_this = vlclua_get_this( L );
+    const char *psz_name;
+    const char *psz_cmd;
+    const char *psz_arg;
+    char *psz_msg;
+    psz_name = luaL_checkstring( L, 1 );
+    psz_cmd = luaL_checkstring( L, 2 );
+    psz_arg = luaL_checkstring( L, 3 );
+    lua_pop( L, 3 );
+    var_Command( p_this, psz_name, psz_cmd, psz_arg, &psz_msg );
+    if( psz_msg )
+    {
+        lua_pushstring( L, psz_msg );
+        free( psz_msg );
+    }
+    else
+    {
+        lua_pushstring( L, "" );
+    }
+    return 1;
+}
+
+int vlclua_libvlc_command( lua_State *L )
+{
+    vlc_object_t * p_this = vlclua_get_this( L );
+    const char *psz_cmd;
+    vlc_value_t val_arg;
+    psz_cmd = luaL_checkstring( L, 1 );
+    val_arg.psz_string = strdup( luaL_optstring( L, 2, "" ) );
+    lua_pop( L, 2 );
+    if( !var_Type( p_this->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
+    {
+        free( val_arg.psz_string );
+        return luaL_error( L, "libvlc's \"%s\" is not a command",
+                           psz_cmd );
+    }
+
+    return vlclua_push_ret( L,
+                            var_Set( p_this->p_libvlc, psz_cmd, val_arg ) );
+}
diff --git a/modules/misc/lua/vlc.c b/modules/misc/lua/vlc.c
new file mode 100644 (file)
index 0000000..eb07f4f
--- /dev/null
@@ -0,0 +1,712 @@
+/*****************************************************************************
+ * vlc.c: Generic lua inteface functions
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *          Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_meta.h>
+#include <vlc_charset.h>
+#include <vlc_aout.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+#define INTF_TEXT N_("Lua interface")
+#define INTF_LONGTEXT N_("Lua interface module to load")
+
+#define CONFIG_TEXT N_("Lua inteface configuration")
+#define CONFIG_LONGTEXT N_("Lua interface configuration string. Format is: '[\"<interface module name>\"] = { <option> = <value>, ...}, ...'.")
+
+vlc_module_begin();
+    add_shortcut( "luameta" );
+    set_shortname( N_( "Lua Meta" ) );
+    set_description( _("Fetch metadata using lua scripts") );
+    set_capability( "meta fetcher", 10 );
+    set_callbacks( E_(FindMeta), NULL );
+    add_submodule();
+        set_shortname( N_( "Lua Art" ) );
+        set_description( _("Fetch artwork using lua scripts") );
+        set_capability( "art finder", 10 );
+        set_callbacks( E_(FindArt), NULL );
+    add_submodule();
+        add_shortcut( "luaplaylist" );
+        set_category( CAT_INPUT );
+        set_subcategory( SUBCAT_INPUT_DEMUX );
+        set_shortname( _("Lua Playlist") );
+        set_description( _("Lua Playlist Parser Interface") );
+        set_capability( "demux2", 9 );
+        set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
+    add_submodule();
+        add_shortcut( "luaintf" );
+        add_shortcut( "luarc" );
+        /* add_shortcut( "rc" ); */
+        add_shortcut( "luahotkeys" );
+        /* add_shortcut( "hotkeys" ); */
+        add_shortcut( "luatelnet" );
+        /* add_shortcut( "telnet" ); */
+        set_description( _("Lua Interface Module") );
+        set_capability( "interface", 0 );
+        add_string( "lua-intf", "dummy", NULL,
+                    INTF_TEXT, INTF_LONGTEXT, VLC_FALSE );
+        add_string( "lua-config", "", NULL,
+                    CONFIG_TEXT, CONFIG_LONGTEXT, VLC_FALSE );
+        set_callbacks( E_(Open_LuaIntf), E_(Close_LuaIntf) );
+vlc_module_end();
+
+/*****************************************************************************
+ * Internal lua<->vlc utils
+ *****************************************************************************/
+vlc_object_t * vlclua_get_this( lua_State *L )
+{
+    vlc_object_t * p_this;
+    lua_getglobal( L, "vlc" );
+    lua_getfield( L, -1, "private" );
+    p_this = (vlc_object_t*)lua_topointer( L, lua_gettop( L ) );
+    lua_pop( L, 2 );
+    return p_this;
+}
+
+/*****************************************************************************
+ * VLC error code translation
+ *****************************************************************************/
+int vlclua_push_ret( lua_State *L, int i_error )
+{
+    lua_pushnumber( L, i_error );
+    lua_pushstring( L, vlc_error( i_error ) );
+    return 2;
+}
+
+/*****************************************************************************
+ * Get the VLC version string
+ *****************************************************************************/
+int vlclua_version( lua_State *L )
+{
+    lua_pushstring( L, VLC_Version() );
+    return 1;
+}
+
+/*****************************************************************************
+ * Quit VLC
+ *****************************************************************************/
+int vlclua_quit( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    /* The rc.c code also stops the playlist ... not sure if this is needed
+     * though. */
+    vlc_object_kill( p_this->p_libvlc );
+    return 0;
+}
+
+/*****************************************************************************
+ * Volume related
+ *****************************************************************************/
+int vlclua_volume_set( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    int i_volume = luaL_checkint( L, -1 );
+    /* Do we need to check that i_volume is in the AOUT_VOLUME_MIN->MAX range?*/
+    return vlclua_push_ret( L, aout_VolumeSet( p_this, i_volume ) );
+}
+
+int vlclua_volume_get( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    audio_volume_t i_volume;
+    if( aout_VolumeGet( p_this, &i_volume ) == VLC_SUCCESS )
+        lua_pushnumber( L, i_volume );
+    else
+        lua_pushnil( L );
+    return 1;
+}
+
+int vlclua_volume_up( lua_State *L )
+{
+    audio_volume_t i_volume;
+    aout_VolumeUp( vlclua_get_this( L ),
+                   luaL_optint( L, 1, 1 ),
+                   &i_volume );
+    lua_pushnumber( L, i_volume );
+    return 1;
+}
+
+int vlclua_volume_down( lua_State *L )
+{
+    audio_volume_t i_volume;
+    aout_VolumeDown( vlclua_get_this( L ),
+                     luaL_optint( L, 1, 1 ),
+                     &i_volume );
+    lua_pushnumber( L, i_volume );
+    return 1;
+}
+
+/*****************************************************************************
+ * Stream handling
+ *****************************************************************************/
+int vlclua_stream_new( lua_State *L )
+{
+    vlc_object_t * p_this = vlclua_get_this( L );
+    stream_t * p_stream;
+    const char * psz_url;
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    psz_url = luaL_checkstring( L, -1 );
+    lua_pop( L, 1 );
+    p_stream = stream_UrlNew( p_this, psz_url );
+    if( !p_stream ) return vlclua_error( L );
+    lua_pushlightuserdata( L, p_stream );
+    return 1;
+}
+
+int vlclua_stream_read( lua_State *L )
+{
+    stream_t * p_stream;
+    int n;
+    byte_t *p_read;
+    int i_read;
+    if( lua_gettop( L ) != 2 ) return vlclua_error( L );
+    p_stream = (stream_t *)luaL_checklightuserdata( L, -2 );
+    n = luaL_checkint( L, -1 );
+    lua_pop( L, 2 );
+    p_read = malloc( n );
+    if( !p_read ) return vlclua_error( L );
+    i_read = stream_Read( p_stream, p_read, n );
+    lua_pushlstring( L, (const char *)p_read, i_read );
+    free( p_read );
+    return 1;
+}
+
+int vlclua_stream_readline( lua_State *L )
+{
+    stream_t * p_stream;
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
+    lua_pop( L, 1 );
+    char *psz_line = stream_ReadLine( p_stream );
+    if( psz_line )
+    {
+        lua_pushstring( L, psz_line );
+        free( psz_line );
+    }
+    else
+    {
+        lua_pushnil( L );
+    }
+    return 1;
+}
+
+int vlclua_stream_delete( lua_State *L )
+{
+    stream_t * p_stream;
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    p_stream = (stream_t *)luaL_checklightuserdata( L, -1 );
+    lua_pop( L, 1 );
+    stream_Delete( p_stream );
+    return 1;
+}
+
+/*****************************************************************************
+ * String transformations
+ *****************************************************************************/
+/* TODO: make it work for any number of arguments */
+int vlclua_decode_uri( lua_State *L )
+{
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    char *psz_string = strdup( psz_cstring );
+    lua_pop( L, 1 );
+    decode_URI( psz_string );
+    lua_pushstring( L, psz_string );
+    free( psz_string );
+    return 1;
+}
+
+/* TODO: make it work for any number of arguments */
+int vlclua_resolve_xml_special_chars( lua_State *L )
+{
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    char *psz_string = strdup( psz_cstring );
+    lua_pop( L, 1 );
+    resolve_xml_special_chars( psz_string );
+    lua_pushstring( L, psz_string );
+    free( psz_string );
+    return 1;
+}
+
+/*****************************************************************************
+ * Messaging facilities
+ *****************************************************************************/
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_dbg( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    msg_Dbg( p_this, "%s", psz_cstring );
+    return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_warn( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    msg_Warn( p_this, "%s", psz_cstring );
+    return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_err( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    msg_Err( p_this, "%s", psz_cstring );
+    return 0;
+}
+/* TODO: make it work for any number of arguments */
+int vlclua_msg_info( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    if( lua_gettop( L ) != 1 ) return vlclua_error( L );
+    const char *psz_cstring = luaL_checkstring( L, 1 );
+    if( !psz_cstring ) return vlclua_error( L );
+    msg_Info( p_this, "%s", psz_cstring );
+    return 0;
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+static int file_select( const char *file )
+{
+    int i = strlen( file );
+    return i > 4 && !strcmp( file+i-4, ".lua" );
+}
+
+static int file_compare( const char **a, const char **b )
+{
+    return strcmp( *a, *b );
+}
+
+int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname,
+                     char **ppsz_dir_list )
+{
+    if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
+                   p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
+        return VLC_ENOMEM;
+
+#   if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
+    {
+        const char *psz_vlcpath = config_GetDataDir();
+        if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s",
+                      psz_vlcpath, luadirname )  < 0 )
+            return VLC_ENOMEM;
+
+        if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s",
+                      psz_vlcpath, luadirname )  < 0 )
+            return VLC_ENOMEM;
+    }
+#   else
+    if( asprintf( &ppsz_dir_list[1],
+                  "share" DIR_SEP "%s", luadirname ) < 0 )
+        return VLC_ENOMEM;
+
+#   ifdef HAVE_SYS_STAT_H
+    {
+        struct stat stat_info;
+        if( ( utf8_stat( ppsz_dir_list[1], &stat_info ) == -1 )
+            || !S_ISDIR( stat_info.st_mode ) )
+        {
+            free(ppsz_dir_list[1]);
+            if( asprintf( &ppsz_dir_list[1],
+                          DATA_PATH DIR_SEP "%s", luadirname ) < 0 )
+                return VLC_ENOMEM;
+        }
+    }
+#   endif
+#   endif
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Will execute func on all scripts in luadirname, and stop if func returns
+ * success.
+ *****************************************************************************/
+int vlclua_scripts_batch_execute( vlc_object_t *p_this,
+                                  const char * luadirname,
+                                  int (*func)(vlc_object_t *, const char *, lua_State *, void *),
+                                  lua_State * L,
+                                  void * user_data)
+{
+    int i_ret = VLC_EGENERIC;
+
+    DIR   *dir           = NULL;
+    char **ppsz_filelist = NULL;
+    char **ppsz_fileend  = NULL;
+    char **ppsz_file;
+
+    char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
+    char **ppsz_dir;
+
+    i_ret = vlclua_dir_list( p_this, luadirname, ppsz_dir_list );
+    if( i_ret != VLC_SUCCESS )
+        return i_ret;
+    i_ret = VLC_EGENERIC;
+
+
+    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+    {
+        int i_files;
+
+        if( ppsz_filelist )
+        {
+            for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
+                 ppsz_file++ )
+                free( *ppsz_file );
+            free( ppsz_filelist );
+            ppsz_filelist = NULL;
+        }
+
+        if( dir )
+        {
+            closedir( dir );
+        }
+
+        msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
+        dir = utf8_opendir( *ppsz_dir );
+
+        if( !dir ) continue;
+        i_files = utf8_loaddir( dir, &ppsz_filelist, file_select,
+                                file_compare );
+        if( i_files < 1 ) continue;
+        ppsz_fileend = ppsz_filelist + i_files;
+
+        for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
+        {
+            char  *psz_filename;
+            if( asprintf( &psz_filename,
+                          "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
+                return VLC_ENOMEM;
+            msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
+
+            i_ret = func( p_this, psz_filename, L, user_data );
+
+            free( psz_filename );
+
+            if( i_ret == VLC_SUCCESS ) break;
+        }
+        if( i_ret == VLC_SUCCESS ) break;
+    }
+
+    if( ppsz_filelist )
+    {
+        for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
+             ppsz_file++ )
+            free( *ppsz_file );
+        free( ppsz_filelist );
+    }
+    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
+        free( *ppsz_dir );
+
+    if( dir ) closedir( dir );
+
+    return i_ret;
+}
+
+
+/*****************************************************************************
+ * Meta data setters utility.
+ * Playlist item table should be on top of the stack when these are called
+ *****************************************************************************/
+void __vlclua_read_meta_data( vlc_object_t *p_this, lua_State *L,
+                              input_item_t *p_input )
+{
+#define TRY_META( a, b )                                        \
+    lua_getfield( L, -1, a );                                   \
+    if( lua_isstring( L, -1 ) )                                 \
+    {                                                           \
+        char *psz_value = strdup( lua_tostring( L, -1 ) );      \
+        EnsureUTF8( psz_value );                                \
+        msg_Dbg( p_this, #b ": %s", psz_value );                \
+        input_item_Set ## b ( p_input, psz_value );             \
+        free( psz_value );                                      \
+    }                                                           \
+    lua_pop( L, 1 ); /* pop a */
+    TRY_META( "title", Title );
+    TRY_META( "artist", Artist );
+    TRY_META( "genre", Genre );
+    TRY_META( "copyright", Copyright );
+    TRY_META( "album", Album );
+    TRY_META( "tracknum", TrackNum );
+    TRY_META( "description", Description );
+    TRY_META( "rating", Rating );
+    TRY_META( "date", Date );
+    TRY_META( "setting", Setting );
+    TRY_META( "url", URL );
+    TRY_META( "language", Language );
+    TRY_META( "nowplaying", NowPlaying );
+    TRY_META( "publisher", Publisher );
+    TRY_META( "encodedby", EncodedBy );
+    TRY_META( "arturl", ArtURL );
+    TRY_META( "trackid", TrackID );
+}
+
+void __vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L,
+                                     input_item_t *p_input )
+{
+    /* ... item */
+    lua_getfield( L, -1, "meta" );
+    /* ... item meta */
+    if( lua_istable( L, -1 ) )
+    {
+        lua_pushnil( L );
+        /* ... item meta nil */
+        while( lua_next( L, -2 ) )
+        {
+            /* ... item meta key value */
+            if( !lua_isstring( L, -2 ) )
+            {
+                msg_Warn( p_this, "Custom meta data category name must be "
+                                   "a string" );
+            }
+            else if( !lua_istable( L, -1 ) )
+            {
+                msg_Warn( p_this, "Custom meta data category contents "
+                                   "must be a table" );
+            }
+            else
+            {
+                const char *psz_meta_category = lua_tostring( L, -2 );
+                msg_Dbg( p_this, "Found custom meta data category: %s",
+                         psz_meta_category );
+                lua_pushnil( L );
+                /* ... item meta key value nil */
+                while( lua_next( L, -2 ) )
+                {
+                    /* ... item meta key value key2 value2 */
+                    if( !lua_isstring( L, -2 ) )
+                    {
+                        msg_Warn( p_this, "Custom meta category item name "
+                                           "must be a string." );
+                    }
+                    else if( !lua_isstring( L, -1 ) )
+                    {
+                        msg_Warn( p_this, "Custom meta category item value "
+                                           "must be a string." );
+                    }
+                    else
+                    {
+                        const char *psz_meta_name =
+                            lua_tostring( L, -2 );
+                        const char *psz_meta_value =
+                            lua_tostring( L, -1 );
+                        msg_Dbg( p_this, "Custom meta %s, %s: %s",
+                                 psz_meta_category, psz_meta_name,
+                                 psz_meta_value );
+                        input_ItemAddInfo( p_input, psz_meta_category,
+                                           psz_meta_name, psz_meta_value );
+                    }
+                    lua_pop( L, 1 ); /* pop item */
+                    /* ... item meta key value key2 */
+                }
+                /* ... item meta key value */
+            }
+            lua_pop( L, 1 ); /* pop category */
+            /* ... item meta key */
+        }
+        /* ... item meta */
+    }
+    lua_pop( L, 1 ); /* pop "meta" */
+    /* ... item -> back to original stack */
+}
+
+/*****************************************************************************
+ * Playlist utilities
+ ****************************************************************************/
+/**
+ * Playlist item table should be on top of the stack when this is called
+ */
+void __vlclua_read_options( vlc_object_t *p_this, lua_State *L,
+                            int *pi_options, char ***pppsz_options )
+{
+    lua_getfield( L, -1, "options" );
+    if( lua_istable( L, -1 ) )
+    {
+        lua_pushnil( L );
+        while( lua_next( L, -2 ) )
+        {
+            if( lua_isstring( L, -1 ) )
+            {
+                char *psz_option = strdup( lua_tostring( L, -1 ) );
+                msg_Dbg( p_this, "Option: %s", psz_option );
+                INSERT_ELEM( *pppsz_options, *pi_options, *pi_options,
+                             psz_option );
+            }
+            else
+            {
+                msg_Warn( p_this, "Option should be a string" );
+            }
+            lua_pop( L, 1 ); /* pop option */
+        }
+    }
+    lua_pop( L, 1 ); /* pop "options" */
+}
+
+int __vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L,
+                                    playlist_t *p_playlist,
+                                    input_item_t *p_parent, vlc_bool_t b_play )
+{
+    int i_count = 0;
+
+    /* playlist */
+    if( lua_istable( L, -1 ) )
+    {
+        lua_pushnil( L );
+        /* playlist nil */
+        while( lua_next( L, -2 ) )
+        {
+            /* playlist key item */
+            /* <Parse playlist item> */
+            if( lua_istable( L, -1 ) )
+            {
+                lua_getfield( L, -1, "path" );
+                /* playlist key item path */
+                if( lua_isstring( L, -1 ) )
+                {
+                    const char   *psz_path     = NULL;
+                    const char   *psz_name     = NULL;
+                    char        **ppsz_options = NULL;
+                    int           i_options    = 0;
+                    mtime_t       i_duration   = -1;
+                    input_item_t *p_input;
+
+                    /* Read path and name */
+                    psz_path = lua_tostring( L, -1 );
+                    msg_Dbg( p_this, "Path: %s", psz_path );
+                    lua_getfield( L, -2, "name" );
+                    /* playlist key item path name */
+                    if( lua_isstring( L, -1 ) )
+                    {
+                        psz_name = lua_tostring( L, -1 );
+                        msg_Dbg( p_this, "Name: %s", psz_name );
+                    }
+                    else
+                    {
+                        if( !lua_isnil( L, -1 ) )
+                            msg_Warn( p_this, "Playlist item name should be a string." );
+                        psz_name = psz_path;
+                    }
+
+                    /* Read duration */
+                    lua_getfield( L, -3, "duration" );
+                    /* playlist key item path name duration */
+                    if( lua_isnumber( L, -1 ) )
+                    {
+                        i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6);
+                    }
+                    else if( !lua_isnil( L, -1 ) )
+                    {
+                        msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." );
+                    }
+                    lua_pop( L, 1 ); /* pop "duration" */
+
+                    /* playlist key item path name */
+
+                    /* Read options: item must be on top of stack */
+                    lua_pushvalue( L, -3 );
+                    /* playlist key item path name item */
+                    vlclua_read_options( p_this, L, &i_options, &ppsz_options );
+
+                    /* Create input item */
+                    p_input = input_ItemNewExt( p_playlist, psz_path,
+                                                psz_name, i_options,
+                                                (const char **)ppsz_options,
+                                                i_duration );
+                    lua_pop( L, 3 ); /* pop "path name item" */
+                    /* playlist key item */
+
+                    /* Read meta data: item must be on top of stack */
+                    vlclua_read_meta_data( p_this, L, p_input );
+
+                    /* Read custom meta data: item must be on top of stack*/
+                    vlclua_read_custom_meta_data( p_this, L, p_input );
+
+                    /* Append item to playlist */
+                    if( p_parent ) /* Add to node */
+                        input_ItemAddSubItem( p_parent, p_input );
+                    else if( b_play ) /* Play */
+                        playlist_AddInput( p_playlist, p_input,
+                                           PLAYLIST_APPEND | PLAYLIST_GO,
+                                           PLAYLIST_END, VLC_TRUE, VLC_FALSE );
+                    else /* Enqueue */
+                        playlist_AddInput( p_playlist, p_input,
+                                           PLAYLIST_APPEND | PLAYLIST_PREPARSE,
+                                           PLAYLIST_END, VLC_TRUE, VLC_FALSE );
+                    i_count ++; /* increment counter */
+
+                    while( i_options > 0 )
+                        free( ppsz_options[--i_options] );
+                    free( ppsz_options );
+                }
+                else
+                {
+                    lua_pop( L, 1 ); /* pop "path" */
+                    msg_Warn( p_this,
+                             "Playlist item's path should be a string" );
+                }
+                /* playlist key item */
+            }
+            else
+            {
+                msg_Warn( p_this, "Playlist item should be a table" );
+            }
+            /* <Parse playlist item> */
+            lua_pop( L, 1 ); /* pop the value, keep the key for
+                              * the next lua_next() call */
+            /* playlist key */
+        }
+        /* playlist */
+    }
+    else
+    {
+        msg_Warn( p_this, "Playlist should be a table." );
+    }
+    return i_count;
+}
diff --git a/modules/misc/lua/vlc.h b/modules/misc/lua/vlc.h
new file mode 100644 (file)
index 0000000..0c52cc8
--- /dev/null
@@ -0,0 +1,202 @@
+/*****************************************************************************
+ * luameta.c: Get meta/artwork using lua scripts
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *          Pierre d'Herbemont <pdherbemont # videolan.org>
+ *
+ * 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 VLCLUA_H
+#define VLC_LUA_H
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_input.h>
+#include <vlc_playlist.h>
+#include <vlc_meta.h>
+#include <vlc_url.h>
+#include <vlc_strings.h>
+#include <vlc_stream.h>
+#include <vlc_charset.h>
+
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+/*****************************************************************************
+ * Module entry points
+ *****************************************************************************/
+int E_(FindArt)( vlc_object_t * );
+int E_(FindMeta)( vlc_object_t * );
+
+int E_(Import_LuaPlaylist)( vlc_object_t * );
+void E_(Close_LuaPlaylist)( vlc_object_t * );
+
+int E_(Open_LuaIntf)( vlc_object_t * );
+void E_(Close_LuaIntf)( vlc_object_t * );
+
+
+/*****************************************************************************
+ * Lua debug
+ *****************************************************************************/
+static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... )
+{
+    if( p_this->p_libvlc->i_verbose < 3 )
+        return;
+
+    va_list ap;
+    va_start( ap, ppz_fmt );
+    __msg_GenericVa( ( vlc_object_t *)p_this, MSG_QUEUE_NORMAL,
+                      VLC_MSG_DBG, MODULE_STRING,
+                      ppz_fmt, ap );
+    va_end( ap );
+}
+
+/*****************************************************************************
+ * Functions that should be in lua ... but aren't for some obscure reason
+ *****************************************************************************/
+static inline int luaL_checkboolean( lua_State *L, int narg )
+{
+    luaL_checktype( L, narg, LUA_TBOOLEAN ); /* can raise an error */
+    return lua_toboolean( L, narg );
+}
+
+static inline const void *luaL_checklightuserdata( lua_State *L, int narg )
+{
+    luaL_checktype( L, narg, LUA_TLIGHTUSERDATA ); /* can raise an error */
+    return lua_topointer( L, narg );
+}
+
+static inline const void *luaL_checkuserdata( lua_State *L, int narg, size_t size )
+{
+    luaL_checktype( L, narg, LUA_TUSERDATA ); /* can raise an error */
+    if( size && size != lua_objlen( L, narg ) ) /* this isn't worth much ... but it might still prevent a few errors */
+        luaL_error( L, "user data size doesn't match" );
+    return lua_topointer( L, narg );
+}
+
+/*****************************************************************************
+ * Lua vlc_object_t wrapper
+ *****************************************************************************/
+int __vlclua_push_vlc_object( lua_State *L, vlc_object_t *p_obj,
+                              lua_CFunction pf_gc );
+#define vlclua_push_vlc_object( a, b, c ) \
+        __vlclua_push_vlc_object( a, VLC_OBJECT( b ), c )
+vlc_object_t *vlclua_checkobject( lua_State *L, int narg, int i_type );
+int vlclua_gc_release( lua_State *L );
+int vlclua_object_find( lua_State *L );
+int vlclua_object_find_name( lua_State *L );
+
+int vlclua_add_callback( lua_State * );
+int vlclua_del_callback( lua_State * );
+
+int vlclua_url_parse( lua_State * );
+int vlclua_net_listen_tcp( lua_State * );
+int vlclua_net_listen_close( lua_State * );
+int vlclua_net_accept( lua_State * );
+int vlclua_net_close( lua_State * );
+int vlclua_net_send( lua_State * );
+int vlclua_net_recv( lua_State * );
+int vlclua_net_select( lua_State * );
+
+int vlclua_fd_set_new( lua_State * );
+int vlclua_fd_clr( lua_State * );
+int vlclua_fd_isset( lua_State * );
+int vlclua_fd_set( lua_State * );
+int vlclua_fd_zero( lua_State * );
+int vlclua_fd_read( lua_State * );
+int vlclua_fd_write( lua_State * );
+
+int vlclua_vlm_new( lua_State * );
+int vlclua_vlm_delete( lua_State * );
+int vlclua_vlm_execute_command( lua_State * );
+
+/*****************************************************************************
+ * Lua function bridge
+ *****************************************************************************/
+vlc_object_t * vlclua_get_this( lua_State * );
+#define vlclua_error( L ) luaL_error( L, "VLC lua error in file %s line %d (function %s)", __FILE__, __LINE__, __func__ )
+int vlclua_push_ret( lua_State *, int i_error );
+
+int vlclua_version( lua_State * );
+int vlclua_quit( lua_State * );
+
+int vlclua_pushvalue( lua_State *L, int i_type, vlc_value_t val ); /* internal use only */
+int vlclua_var_get( lua_State * );
+int vlclua_var_get_list( lua_State * );
+int vlclua_var_set( lua_State * );
+int vlclua_module_command( lua_State * );
+int vlclua_libvlc_command( lua_State * );
+
+int vlclua_config_get( lua_State * );
+int vlclua_config_set( lua_State * );
+
+int vlclua_volume_set( lua_State * );
+int vlclua_volume_get( lua_State * );
+int vlclua_volume_up( lua_State * );
+int vlclua_volume_down( lua_State * );
+
+int vlclua_stream_new( lua_State * );
+int vlclua_stream_read( lua_State * );
+int vlclua_stream_readline( lua_State * );
+int vlclua_stream_delete( lua_State * );
+
+int vlclua_decode_uri( lua_State * );
+int vlclua_resolve_xml_special_chars( lua_State * );
+
+int vlclua_msg_dbg( lua_State * );
+int vlclua_msg_warn( lua_State * );
+int vlclua_msg_err( lua_State * );
+int vlclua_msg_info( lua_State * );
+
+/*****************************************************************************
+ * Will execute func on all scripts in luadirname, and stop if func returns
+ * success.
+ *****************************************************************************/
+int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname,
+        int (*func)(vlc_object_t *, const char *, lua_State *, void *),
+        lua_State * L, void * user_data );
+int vlclua_dir_list( vlc_object_t *p_this, const char *luadirname, char **ppsz_dir_list );
+
+/*****************************************************************************
+ * Playlist and meta data internal utilities.
+ *****************************************************************************/
+void __vlclua_read_options( vlc_object_t *, lua_State *, int *, char *** );
+#define vlclua_read_options(a,b,c,d) __vlclua_read_options(VLC_OBJECT(a),b,c,d)
+void __vlclua_read_meta_data( vlc_object_t *, lua_State *, input_item_t * );
+#define vlclua_read_meta_data(a,b,c) __vlclua_read_meta_data(VLC_OBJECT(a),b,c)
+void __vlclua_read_custom_meta_data( vlc_object_t *, lua_State *,
+                                     input_item_t *);
+#define vlclua_read_custom_meta_data(a,b,c) __vlclua_read_custom_meta_data(VLC_OBJECT(a),b,c)
+int __vlclua_playlist_add_internal( vlc_object_t *, lua_State *, playlist_t *,
+                                    input_item_t *, vlc_bool_t );
+#define vlclua_playlist_add_internal(a,b,c,d,e) __vlclua_playlist_add_internal(VLC_OBJECT(a),b,c,d,e)
+
+
+#endif /* VLC_LUA_H */
+
diff --git a/modules/misc/lua/vlclua.c b/modules/misc/lua/vlclua.c
deleted file mode 100644 (file)
index c7480e3..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*****************************************************************************
- * vlclua.c: Generic lua inteface functions
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- *          Pierre d'Herbemont <pdherbemont # videolan.org>
- *
- * 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
- *****************************************************************************/
-#ifndef  _GNU_SOURCE
-#   define  _GNU_SOURCE
-#endif
-
-#include <vlc/vlc.h>
-#include <vlc_meta.h>
-#include <vlc_charset.h>
-
-#include <lua.h>        /* Low level lua C API */
-#include <lauxlib.h>    /* Higher level C API */
-#include <lualib.h>     /* Lua libs */
-
-#include "vlclua.h"
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-
-vlc_module_begin();
-    add_shortcut( "luameta" );
-    set_shortname( N_( "Lua Meta" ) );
-    set_description( _("Fetch metadata using lua scripts") );
-    set_capability( "meta fetcher", 10 );
-    set_callbacks( E_(FindMeta), NULL );
-    add_submodule();
-        set_shortname( N_( "Lua Art" ) );
-        set_description( _("Fetch artwork using lua scripts") );
-        set_capability( "art finder", 10 );
-        set_callbacks( E_(FindArt), NULL );
-    add_submodule();
-        add_shortcut( "luaplaylist" );
-        set_category( CAT_INPUT );
-        set_subcategory( SUBCAT_INPUT_DEMUX );
-        set_shortname( _("Lua Playlist") );
-        set_description( _("Lua Playlist Parser Interface") );
-        set_capability( "demux2", 9 );
-        set_callbacks( E_(Import_LuaPlaylist), E_(Close_LuaPlaylist) );
-vlc_module_end();
-
-/*****************************************************************************
- * Lua function bridge
- *****************************************************************************/
-vlc_object_t * vlclua_get_this( lua_State *p_state )
-{
-    vlc_object_t * p_this;
-    lua_getglobal( p_state, "vlc" );
-    lua_getfield( p_state, lua_gettop( p_state ), "private" );
-    p_this = (vlc_object_t*)lua_topointer( p_state, lua_gettop( p_state ) );
-    lua_pop( p_state, 2 );
-    return p_this;
-}
-
-int vlclua_stream_new( lua_State *p_state )
-{
-    vlc_object_t * p_this = vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    stream_t * p_stream;
-    const char * psz_url;
-    if( !i ) return 0;
-    psz_url = lua_tostring( p_state, 1 );
-    lua_pop( p_state, i );
-    p_stream = stream_UrlNew( p_this, psz_url );
-    if( !p_stream ) return 0;
-    lua_pushlightuserdata( p_state, p_stream );
-    return 1;
-}
-
-int vlclua_stream_read( lua_State *p_state )
-{
-    int i = lua_gettop( p_state );
-    stream_t * p_stream;
-    int n;
-    byte_t *p_read;
-    int i_read;
-    if( !i ) return 0;
-    p_stream = (stream_t *)lua_topointer( p_state, 1 );
-    n = lua_tonumber( p_state, 2 );
-    lua_pop( p_state, i );
-    p_read = malloc( n );
-    if( !p_read ) return 0;
-    i_read = stream_Read( p_stream, p_read, n );
-    lua_pushlstring( p_state, (const char *)p_read, i_read );
-    free( p_read );
-    return 1;
-}
-
-int vlclua_stream_readline( lua_State *p_state )
-{
-    int i = lua_gettop( p_state );
-    stream_t * p_stream;
-    if( !i ) return 0;
-    p_stream = (stream_t *)lua_topointer( p_state, 1 );
-    lua_pop( p_state, i );
-    char *psz_line = stream_ReadLine( p_stream );
-    if( psz_line )
-    {
-        lua_pushstring( p_state, psz_line );
-        free( psz_line );
-    }
-    else
-    {
-        lua_pushnil( p_state );
-    }
-    return 1;
-}
-
-int vlclua_stream_delete( lua_State *p_state )
-{
-    int i = lua_gettop( p_state );
-    stream_t * p_stream;
-    if( !i ) return 0;
-    p_stream = (stream_t *)lua_topointer( p_state, 1 );
-    lua_pop( p_state, i );
-    stream_Delete( p_stream );
-    return 1;
-}
-
-int vlclua_decode_uri( lua_State *p_state )
-{
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    char *psz_string = strdup( psz_cstring );
-    lua_pop( p_state, i );
-    decode_URI( psz_string );
-    lua_pushstring( p_state, psz_string );
-    free( psz_string );
-    return 1;
-}
-
-int vlclua_resolve_xml_special_chars( lua_State *p_state )
-{
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    char *psz_string = strdup( psz_cstring );
-    lua_pop( p_state, i );
-    resolve_xml_special_chars( psz_string );
-    lua_pushstring( p_state, psz_string );
-    free( psz_string );
-    return 1;
-}
-
-int vlclua_msg_dbg( lua_State *p_state )
-{
-    vlc_object_t *p_this = vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    msg_Dbg( p_this, "%s", psz_cstring );
-    return 0;
-}
-int vlclua_msg_warn( lua_State *p_state )
-{
-    vlc_object_t *p_this = vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    msg_Warn( p_this, "%s", psz_cstring );
-    return 0;
-}
-int vlclua_msg_err( lua_State *p_state )
-{
-    vlc_object_t *p_this = vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    msg_Err( p_this, "%s", psz_cstring );
-    return 0;
-}
-int vlclua_msg_info( lua_State *p_state )
-{
-    vlc_object_t *p_this = vlclua_get_this( p_state );
-    int i = lua_gettop( p_state );
-    if( !i ) return 0;
-    const char *psz_cstring = lua_tostring( p_state, 1 );
-    if( !psz_cstring ) return 0;
-    msg_Info( p_this, "%s", psz_cstring );
-    return 0;
-}
-
-/*****************************************************************************
- *
- *****************************************************************************/
-static int file_select( const char *file )
-{
-    int i = strlen( file );
-    return i > 4 && !strcmp( file+i-4, ".lua" );
-}
-
-static int file_compare( const char **a, const char **b )
-{
-    return strcmp( *a, *b );
-}
-
-
-/*****************************************************************************
- * Will execute func on all scripts in luadirname, and stop if func returns
- * success.
- *****************************************************************************/
-int vlclua_scripts_batch_execute( vlc_object_t *p_this,
-                                  const char * luadirname,
-                                  int (*func)(vlc_object_t *, const char *, lua_State *, void *),
-                                  lua_State * p_state,
-                                  void * user_data)
-{
-    int i_ret = VLC_EGENERIC;
-
-    DIR   *dir           = NULL;
-    char **ppsz_filelist = NULL;
-    char **ppsz_fileend  = NULL;
-    char **ppsz_file;
-
-    char  *ppsz_dir_list[] = { NULL, NULL, NULL, NULL };
-    char **ppsz_dir;
-
-    if( asprintf( &ppsz_dir_list[0], "%s" DIR_SEP "%s",
-                   p_this->p_libvlc->psz_datadir, luadirname ) < 0 )
-        return VLC_ENOMEM;
-
-#   if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
-    {
-        const char *psz_vlcpath = config_GetDataDir();
-        if( asprintf( &ppsz_dir_list[1], "%s" DIR_SEP "%s", psz_vlcpath, luadirname )  < 0 )
-            return VLC_ENOMEM;
-
-        if( asprintf( &ppsz_dir_list[2], "%s" DIR_SEP "share" DIR_SEP "%s", psz_vlcpath, luadirname )  < 0 )
-            return VLC_ENOMEM;
-    }
-#   else
-    if( asprintf( &ppsz_dir_list[1],
-                  "share" DIR_SEP "%s", luadirname ) < 0 )
-        return VLC_ENOMEM;
-
-#   ifdef HAVE_SYS_STAT_H
-    {
-        struct stat stat_info;
-        if( ( utf8_stat( ppsz_dir_list[1], &stat_info ) == -1 )
-            || !S_ISDIR( stat_info.st_mode ) )
-        {
-            free(ppsz_dir_list[1]);
-            if( asprintf( &ppsz_dir_list[1],
-                          DATA_PATH DIR_SEP "%s", luadirname ) < 0 )
-                return VLC_ENOMEM;
-        }
-    }
-#   endif
-#   endif
-
-    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
-    {
-        int i_files;
-
-        if( ppsz_filelist )
-        {
-            for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
-                 ppsz_file++ )
-                free( *ppsz_file );
-            free( ppsz_filelist );
-            ppsz_filelist = NULL;
-        }
-
-        if( dir )
-        {
-            closedir( dir );
-        }
-
-        msg_Dbg( p_this, "Trying Lua scripts in %s", *ppsz_dir );
-        dir = utf8_opendir( *ppsz_dir );
-
-        if( !dir ) continue;
-        i_files = utf8_loaddir( dir, &ppsz_filelist, file_select, file_compare );
-        if( i_files < 1 ) continue;
-        ppsz_fileend = ppsz_filelist + i_files;
-
-        for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend; ppsz_file++ )
-        {
-            char  *psz_filename;
-            if( asprintf( &psz_filename,
-                          "%s" DIR_SEP "%s", *ppsz_dir, *ppsz_file ) < 0)
-                return VLC_ENOMEM;
-            msg_Dbg( p_this, "Trying Lua playlist script %s", psz_filename );
-            i_ret = func( p_this, psz_filename, p_state, user_data );
-            free( psz_filename );
-
-            if( i_ret == VLC_SUCCESS ) break;
-        }
-        if( i_ret == VLC_SUCCESS ) break;
-    }
-
-    if( ppsz_filelist )
-    {
-        for( ppsz_file = ppsz_filelist; ppsz_file < ppsz_fileend;
-             ppsz_file++ )
-            free( *ppsz_file );
-        free( ppsz_filelist );
-    }
-    for( ppsz_dir = ppsz_dir_list; *ppsz_dir; ppsz_dir++ )
-        free( *ppsz_dir );
-
-    if( dir ) closedir( dir );
-
-    return i_ret;
-}
-
-
-/*****************************************************************************
- * Meta data setters utility.
- *****************************************************************************/
-void vlclua_read_meta_data( vlc_object_t *p_this, lua_State *p_state,
-                            int o, int t, input_item_t *p_input )
-{
-    const char *psz_value;
-#define TRY_META( a, b )                                    \
-    lua_getfield( p_state, o, a );                          \
-    if( lua_isstring( p_state, t ) )                        \
-    {                                                       \
-        psz_value = lua_tostring( p_state, t );             \
-        EnsureUTF8( psz_value );                            \
-        msg_Dbg( p_this, #b ": %s", psz_value );            \
-        input_item_Set ## b ( p_input, psz_value );         \
-    }                                                       \
-    lua_pop( p_state, 1 ); /* pop a */
-    TRY_META( "title", Title );
-    TRY_META( "artist", Artist );
-    TRY_META( "genre", Genre );
-    TRY_META( "copyright", Copyright );
-    TRY_META( "album", Album );
-    TRY_META( "tracknum", TrackNum );
-    TRY_META( "description", Description );
-    TRY_META( "rating", Rating );
-    TRY_META( "date", Date );
-    TRY_META( "setting", Setting );
-    TRY_META( "url", URL );
-    TRY_META( "language", Language );
-    TRY_META( "nowplaying", NowPlaying );
-    TRY_META( "publisher", Publisher );
-    TRY_META( "encodedby", EncodedBy );
-    TRY_META( "arturl", ArtURL );
-    TRY_META( "trackid", TrackID );
-}
-
-void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *p_state,
-                                   int o, int t, input_item_t *p_input )
-{
-    lua_getfield( p_state, o, "meta" );
-    if( lua_istable( p_state, t ) )
-    {
-        lua_pushnil( p_state );
-        while( lua_next( p_state, t ) )
-        {
-            if( !lua_isstring( p_state, t+1 ) )
-            {
-                msg_Warn( p_this, "Custom meta data category name must be "
-                                   "a string" );
-            }
-            else if( !lua_istable( p_state, t+2 ) )
-            {
-                msg_Warn( p_this, "Custom meta data category contents "
-                                   "must be a table" );
-            }
-            else
-            {
-                const char *psz_meta_category = lua_tostring( p_state, t+1 );
-                msg_Dbg( p_this, "Found custom meta data category: %s",
-                         psz_meta_category );
-                lua_pushnil( p_state );
-                while( lua_next( p_state, t+2 ) )
-                {
-                    if( !lua_isstring( p_state, t+3 ) )
-                    {
-                        msg_Warn( p_this, "Custom meta category item name "
-                                           "must be a string." );
-                    }
-                    else if( !lua_isstring( p_state, t+4 ) )
-                    {
-                        msg_Warn( p_this, "Custom meta category item value "
-                                           "must be a string." );
-                    }
-                    else
-                    {
-                        const char *psz_meta_name =
-                            lua_tostring( p_state, t+3 );
-                        const char *psz_meta_value =
-                            lua_tostring( p_state, t+4 );
-                        msg_Dbg( p_this, "Custom meta %s, %s: %s",
-                                 psz_meta_category, psz_meta_name,
-                                 psz_meta_value );
-                        input_ItemAddInfo( p_input, psz_meta_category,
-                                           psz_meta_name, psz_meta_value );
-                    }
-                    lua_pop( p_state, 1 ); /* pop item */
-                }
-            }
-            lua_pop( p_state, 1 ); /* pop category */
-        }
-    }
-    lua_pop( p_state, 1 ); /* pop "meta" */
-}
-
diff --git a/modules/misc/lua/vlclua.h b/modules/misc/lua/vlclua.h
deleted file mode 100644 (file)
index 79fdaa4..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*****************************************************************************
- * luameta.c: Get meta/artwork using lua scripts
- *****************************************************************************
- * Copyright (C) 2007 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan tod org>
- *          Pierre d'Herbemont <pdherbemont # videolan.org>
- *
- * 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 VLCLUA_H
-#define VLC_LUA_H
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#ifndef  _GNU_SOURCE
-#   define  _GNU_SOURCE
-#endif
-
-#include <vlc/vlc.h>
-#include <vlc_input.h>
-#include <vlc_playlist.h>
-#include <vlc_meta.h>
-#include <vlc_url.h>
-#include <vlc_strings.h>
-#include <vlc_stream.h>
-#include <vlc_charset.h>
-
-#ifdef HAVE_SYS_STAT_H
-#   include <sys/stat.h>
-#endif
-
-#include <lua.h>        /* Low level lua C API */
-#include <lauxlib.h>    /* Higher level C API */
-#include <lualib.h>     /* Lua libs */
-
-/*****************************************************************************
- * Module entry points
- *****************************************************************************/
-int E_(FindArt)( vlc_object_t * );
-int E_(FindMeta)( vlc_object_t * );
-
-int E_(Import_LuaPlaylist)( vlc_object_t * );
-void E_(Close_LuaPlaylist)( vlc_object_t * );
-
-
-/*****************************************************************************
- * Lua debug
- *****************************************************************************/
-static inline void lua_Dbg( vlc_object_t * p_this, const char * ppz_fmt, ... )
-{
-    if( p_this->p_libvlc->i_verbose < 3 )
-        return;
-
-    va_list ap;
-    va_start( ap, ppz_fmt );
-    __msg_GenericVa( ( vlc_object_t *)p_this, MSG_QUEUE_NORMAL,
-                      VLC_MSG_DBG, MODULE_STRING,
-                      ppz_fmt, ap );
-    va_end( ap );
-}
-
-/*****************************************************************************
- * Lua function bridge
- *****************************************************************************/
-vlc_object_t * vlclua_get_this( lua_State *p_state );
-int vlclua_stream_new( lua_State *p_state );
-int vlclua_stream_read( lua_State *p_state );
-int vlclua_stream_readline( lua_State *p_state );
-int vlclua_stream_delete( lua_State *p_state );
-int vlclua_decode_uri( lua_State *p_state );
-int vlclua_resolve_xml_special_chars( lua_State *p_state );
-int vlclua_msg_dbg( lua_State *p_state );
-int vlclua_msg_warn( lua_State *p_state );
-int vlclua_msg_err( lua_State *p_state );
-int vlclua_msg_info( lua_State *p_state );
-
-
-/*****************************************************************************
- * Will execute func on all scripts in luadirname, and stop if func returns
- * success.
- *****************************************************************************/
-int vlclua_scripts_batch_execute( vlc_object_t *p_this, const char * luadirname,
-        int (*func)(vlc_object_t *, const char *, lua_State *, void *),
-        lua_State * p_state, void * user_data);
-
-
-
-/*****************************************************************************
- * Meta data setters utility.
- *****************************************************************************/
-void vlclua_read_meta_data( vlc_object_t *p_this,
-        lua_State *p_state, int o, int t, input_item_t *p_input );
-
-
-void vlclua_read_custom_meta_data( vlc_object_t *p_this,
-        lua_State *p_state, int o, int t, input_item_t *p_input );
-
-#endif /* VLC_LUA_H */
-
diff --git a/modules/misc/lua/vlm.c b/modules/misc/lua/vlm.c
new file mode 100644 (file)
index 0000000..cbdf5ba
--- /dev/null
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * objects.c: Generic lua VLM wrapper
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan tod org>
+ *
+ * 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
+ *****************************************************************************/
+#ifndef  _GNU_SOURCE
+#   define  _GNU_SOURCE
+#endif
+
+#include <vlc/vlc.h>
+#include <vlc_vlm.h>
+
+#include <lua.h>        /* Low level lua C API */
+#include <lauxlib.h>    /* Higher level C API */
+#include <lualib.h>     /* Lua libs */
+
+#include "vlc.h"
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+int vlclua_vlm_new( lua_State *L )
+{
+    vlc_object_t *p_this = vlclua_get_this( L );
+    vlm_t *p_vlm = vlm_New( p_this );
+    if( !p_vlm )
+        return luaL_error( L, "Cannot start VLM." );
+    __vlclua_push_vlc_object( L, (vlc_object_t*)p_vlm, NULL );
+    return 1;
+}
+
+int vlclua_vlm_delete( lua_State *L )
+{
+    vlm_t *p_vlm = (vlm_t*)vlclua_checkobject( L, 1, VLC_OBJECT_VLM );
+    vlm_Delete( p_vlm );
+    return 0;
+}
+
+void push_message( lua_State *L, vlm_message_t *message );
+void push_message( lua_State *L, vlm_message_t *message )
+{
+    lua_createtable( L, 0, 2 );
+    lua_pushstring( L, message->psz_name );
+    lua_setfield( L, -2, "name" );
+    if( message->i_child > 0 )
+    {
+        int i;
+        lua_createtable( L, message->i_child, 0 );
+        for( i = 0; i < message->i_child; i++ )
+        {
+            lua_pushinteger( L, i+1 );
+            push_message( L, message->child[i] );
+            lua_settable( L, -3 );
+        }
+        lua_setfield( L, -2, "children" );
+    }
+    else
+    {
+        lua_pushstring( L, message->psz_value );
+        lua_setfield( L, -2, "value" );
+    }
+}
+
+int vlclua_vlm_execute_command( lua_State *L )
+{
+    vlm_t *p_vlm = (vlm_t*)vlclua_checkobject( L, 1, VLC_OBJECT_VLM );
+    const char *psz_command = luaL_checkstring( L, 2 );
+    vlm_message_t *message;
+    vlm_ExecuteCommand( p_vlm, psz_command, &message );
+    lua_settop( L, 0 );
+    push_message( L, message );
+    vlm_MessageDelete( message );
+    return 1;
+}
index 85b39b3..a604fe0 100644 (file)
@@ -256,4 +256,10 @@ DIST_lua= \
        luaplaylist/youtube.lua \
        luaplaylist/youtube_homepage.lua \
        luaplaylist/metacafe.lua \
-       luaplaylist/googlevideo.lua
+       luaplaylist/googlevideo.lua \
+       luaintf/rc.lua \
+       luaintf/hotkeys.lua \
+       luaintf/modules/common.lua \
+       luaintf/modules/host.lua \
+       luaintf/telnet.lua \
+       luaintf/dummy.lua
diff --git a/share/luaintf/dummy.lua b/share/luaintf/dummy.lua
new file mode 100644 (file)
index 0000000..7193043
--- /dev/null
@@ -0,0 +1,12 @@
+--[[ This code is public domain (since it really isn't very interesting) ]]--
+
+msg = [[
+This is the `dummy' VLC Lua interface module.
+Please specify a VLC Lua interface to load with the --lua-intf option.
+VLC Lua interface modules include: `rc', `telnet' and `hotkeys'.
+For example: vlc -I lua --lua-intf rc]]
+--You can also use the alternate syntax: vlc -I "lua{intf=rc}"]]
+
+for line in string.gmatch(msg,"([^\n]+)\n*") do
+    vlc.msg.err(line)
+end
diff --git a/share/luaintf/hotkeys.lua b/share/luaintf/hotkeys.lua
new file mode 100644 (file)
index 0000000..8740b59
--- /dev/null
@@ -0,0 +1,119 @@
+--[==========================================================================[
+ hotkeys.lua: hotkey handling for VLC
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ 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.
+--]==========================================================================]
+
+--[==========================================================================[
+    This is meant to replace modules/control/hotkeys.c
+    (which will require some changes in the VLC core hotkeys stuff)
+--]==========================================================================]
+
+require("common")
+--common.table_print(vlc,"vlc.\t")
+
+bindings = {
+    ["Ctrl-q"] = "quit",
+    ["Space"] = "play-pause",
+    [113] --[[q]] = "quit",
+    [119] --[[w]] = "demo",
+    [120] --[[x]] = "demo2",
+    }
+
+function quit()
+    print("Bye-bye!")
+    vlc.quit()
+end
+
+function demo()
+    vlc.osd.icon("speaker")
+end
+
+function demo2()
+    if not channel1 then
+        channel1 = vlc.osd.channel_register()
+        channel2 = vlc.osd.channel_register()
+    end
+    vlc.osd.message("Hey!",channel1)
+    vlc.osd.slider( 10, "horizontal", channel2 )
+end
+
+function action(func,delta)
+    return { func = func, delta = delta or 0, last = 0, times = 0 }
+end
+
+actions = {
+    ["quit"] = action(quit),
+    ["play-pause"] = action(play_pause),
+    ["demo"] = action(demo),
+    ["demo2"] = action(demo2),
+    }
+
+action = nil
+
+queue = {}
+
+function action_trigger( action )
+    print("action_trigger:",tostring(action))
+    local a = actions[action]
+    if a then
+        local date = vlc.mdate()
+        if a.delta and date > a.last + a.delta then
+            a.times = 0
+        else
+            a.times = a.times + 1
+        end
+        a.last = date
+        table.insert(queue,action)
+        vlc.signal()
+    else
+        vlc.msg_err("Key `"..key.."' points to unknown action `"..bindings[key].."'.")
+    end
+end
+
+function key_press( var, old, new, data )
+    local key = new
+    print("key_press:",tostring(key))
+    if bindings[key] then
+        action_trigger(bindings[key])
+    else
+        vlc.msg_err("Key `"..key.."' isn't bound to any action.")
+    end
+end
+
+vlc.var.add_callback( vlc.object.libvlc(), "key-pressed", key_press )
+--vlc.var.add_callback( vlc.object.libvlc(), "action-triggered", action_trigger )
+
+while not die do
+    if #queue ~= 0 then
+        local action = actions[queue[1]]
+        local ok, msg = pcall( action.func )
+        if not ok then
+            vlc.msg.err("Error while executing action `"..queue[1].."': "..msg)
+        end
+        table.remove(queue,1)
+    else
+        die = vlc.lock_and_wait()
+    end
+end
+
+-- Clean up
+vlc.var.del_callback( vlc.object.libvlc(), "key-pressed", key_press )
+--vlc.var.del_callback( vlc.object.libvlc(), "action-triggered", action_trigger )
diff --git a/share/luaintf/modules/common.lua b/share/luaintf/modules/common.lua
new file mode 100644 (file)
index 0000000..a709180
--- /dev/null
@@ -0,0 +1,72 @@
+--[[ This code is public domain (since it really isn't very interesting) ]]--
+
+module("common",package.seeall)
+
+-- Iterate over a table in the keys' alphabetical order
+function pairs_sorted(t)
+    local s = {}
+    for k,_ in pairs(t) do table.insert(s,k) end
+    table.sort(s)
+    local i = 0
+    return function () i = i + 1; return s[i], t[s[i]] end
+end
+
+-- Return a function such as skip(foo)(a,b,c) = foo(b,c)
+function skip(foo)
+    return function(discard,...) return foo(...) end
+end
+
+-- Return a function such as setarg(foo,a)(b,c) = foo(a,b,c)
+function setarg(foo,a)
+    return function(...) return foo(a,...) end
+end
+
+-- Trigger a hotkey
+function hotkey(arg)
+    vlc.var.set( vlc.object.libvlc(), "key-pressed", vlc.config.get( arg ) )
+end
+
+-- Take a video snapshot
+function snapshot()
+    local vout = vlc.object.find(nil,"vout","anywhere")
+    if not vout then return end
+    vlc.var.set(vout,"video-snapshot",nil)
+end
+
+-- Naive (non recursive) table copy
+function table_copy(t)
+    c = {}
+    for i,v in pairs(t) do c[i]=v end
+    return c
+end
+
+-- strip leading and trailing spaces
+function strip(str)
+    return string.gsub(str, "^%s*(.-)%s*$", "%1")
+end
+
+-- print a table (recursively)
+function table_print(t,prefix)
+    local prefix = prefix or ""
+    for a,b in pairs_sorted(t) do
+        print(prefix..tostring(a),b)
+        if type(b)==type({}) then
+            table_print(b,prefix.."\t")
+        end
+    end
+end
+
+-- print the list of callbacks registered in lua
+-- usefull for debug purposes
+function print_callbacks()
+    print "callbacks:"
+    table_print(vlc.callbacks)
+end 
+
+-- convert a duration (in seconds) to a string
+function durationtostring(duration)
+    return string.format("%02d:%02d:%02d",
+                         math.floor(duration/3600),
+                         math.floor(duration/60)%60,
+                         math.floor(duration%60))
+end
diff --git a/share/luaintf/modules/host.lua b/share/luaintf/modules/host.lua
new file mode 100644 (file)
index 0000000..d6b7017
--- /dev/null
@@ -0,0 +1,350 @@
+--[==========================================================================[
+ host.lua: VLC Lua interface command line host module
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ 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.
+--]==========================================================================]
+
+--[==========================================================================[
+Example use:
+
+    require "host"
+    h = host.host()
+
+    -- Bypass any authentification
+    function on_password( client )
+        client:switch_status( host.status.read )
+    end
+    h.status_callbacks[host.status.password] = on_password
+
+    h:listen( "localhost:4212" )
+    h:listen( "*console" )
+    --or h:listen( { "localhost:4212", "*console" } )
+
+    -- The main loop
+    while not vlc.should_die() do
+        -- accept new connections
+        h:accept()
+
+        -- select active clients
+        local write, read = h:select( 0.1 ) -- 0.1 is a timeout in seconds
+
+        -- handle clients in write mode
+        for _, client in pairs(write) do
+            client:send()
+            client.buffer = ""
+            client:switch_status( host.status.read )
+        end
+
+        -- handle clients in read mode
+        for _, client in pairs(read) do
+            local str = client:recv(1000)
+            str = string.gsub(str,"\r?\n$","")
+            client.buffer = "Got `"..str.."'.\r\n"
+            client:switch_status( host.status.write )
+        end
+    end
+
+For complete examples see existing VLC Lua interface modules (ie telnet.lua)
+--]==========================================================================]
+
+module("host",package.seeall)
+
+status = { init = 0, read = 1, write = 2, password = 3 }
+client_type = { net = 1, stdio = 2, fifo = 3 }
+
+function host()
+    -- private data
+    local clients = {}
+    local listeners = {}
+    local status_callbacks = {}
+
+    -- private methods
+    local function new_fd_set()
+        function foo_fds(foo)
+            return function(fd,...) return foo(fd.fds,...) end
+        end
+        return { -- data
+                 fds = vlc.fd.new_fd_set(),
+                 -- methods
+                 zero = foo_fds( vlc.fd.fd_zero ),
+                 set = foo_fds( vlc.fd.fd_set ),
+                 isset = foo_fds( vlc.fd.fd_isset ),
+                 clr = foo_fds( vlc.fd.fd_clr ),
+               }
+    end
+
+    -- private data
+    local fds_read = new_fd_set()
+    local fds_write = new_fd_set()
+
+    -- private methods
+    local function client_accept( clients, listen )
+        local wait
+        if #clients == 0 then
+            wait = -1
+        else
+            wait = 0
+        end
+        return vlc.net.accept( listen, wait )
+    end
+
+    local function fd_client( client )
+        if client.status == status.read then
+            return client.rfd
+        else -- status.write
+            return client.wfd
+        end
+    end
+
+    local function send( client, data, len )
+        if len then
+            return vlc.net.send( client.wfd, data, len )
+        else
+            return vlc.net.send( client.wfd, data or client.buffer )
+        end
+    end
+
+    local function recv( client, len )
+        if len then
+            return vlc.net.recv( client.rfd, len )
+        else
+            return vlc.net.recv( client.rfd )
+        end
+    end
+
+    local function write( client, data )
+        return vlc.fd.write( client.wfd, data or client.buffer )
+    end
+
+    local function read( client, len )
+        if len then
+            return vlc.fd.read( client.rfd, len )
+        else
+            return vlc.fd.read( client.rfd )
+        end
+    end
+
+    local function del_client( client )
+        if client.type == client_type.stdio then
+            client:send( "Cannot delete stdin/stdout client.\n" )
+            return
+        end
+        for i, c in pairs(clients) do
+            if c == client then
+                if client.type == client_type.net then
+                    if client.wfd ~= client.rfd then
+                        vlc.net.close( client.rfd )
+                    end
+                    vlc.net.close( client.wfd )
+                end
+                clients[i] = nil
+                return
+            end
+        end
+        vlc.msg.err("couldn't find client to remove.")
+    end
+    
+    local function switch_status( client, s )
+        if client.status == s then return end
+        client.status = s
+        if status_callbacks[s] then
+            status_callbacks[s]( client )
+        end
+    end
+
+    -- append a line to a client's (output) buffer
+    local function append( client, string )
+        client.buffer = client.buffer .. string .. "\r\n"
+    end
+
+    local function new_client( h, fd, wfd, t )
+        if fd < 0 then return end
+        local w, r
+        if t == client_type.net then
+            w = send
+            r = recv
+        else if t == client_type.stdio or t == client_type.fifo then
+            w = write
+            r = read
+        else
+            error("Unknown client type", t )
+        end end
+        local client = { -- data
+                         rfd = fd,
+                         wfd = wfd or fd,
+                         status = status.init,
+                         buffer = "",
+                         type = t,
+                         -- methods
+                         fd = fd_client,
+                         send = w,
+                         recv = r,
+                         del = del_client,
+                         switch_status = switch_status,
+                         append = append,
+                       }
+        client:send( "VLC media player "..vlc.version().."\n" )
+        table.insert(clients, client)
+        client:switch_status(status.password)
+    end
+
+    function filter_client( fd, status, status2 )
+        local l = 0
+        fd:zero()
+        for _, client in pairs(clients) do
+            if client.status == status or client.status == status2 then
+                fd:set( client:fd() )
+                l = math.max( l, client:fd() )
+            end
+        end
+        return l
+    end
+
+    -- public methods
+    local function _listen_tcp( h, host, port )
+        if listeners.tcp and listeners.tcp[host]
+                         and listeners.tcp[host][port] then
+            error("Already listening on tcp host `"..host..":"..tostring(port).."'")
+        end
+        if not listeners.tcp then
+            listeners.tcp = {}
+        end
+        if not listeners.tcp[host] then
+            listeners.tcp[host] = {}
+        end
+        local listener = vlc.net.listen_tcp( host, port )
+        listeners.tcp[host][port] = listener
+        if not listeners.tcp.list then
+            -- FIXME: if host == "list" we'll have a problem
+            listeners.tcp.list = {}
+            local m = { __mode = "v" } -- week values
+            setmetatable( listeners.tcp.list, m )
+        end
+        table.insert( listeners.tcp.list, listener )
+    end
+
+    local function _listen_stdio( h )
+        
+        if listeners.stdio then
+            error("Already listening on stdio")
+        end
+        new_client( h, 0, 1, client_type.stdio )
+        listeners.stdio = true
+    end
+
+    local function _listen( h, url )
+        if type(url)==type({}) then
+            for _,u in pairs(url) do
+                h:listen( u )
+            end
+        else
+            vlc.msg.info( "Listening on host \""..url.."\"." )
+            if url == "*console" then
+                h:listen_stdio()
+            else
+                u = vlc.net.url_parse( url )
+                h:listen_tcp( u.host, u.port )
+            end
+        end
+    end
+
+    local function _accept( h )
+        if listeners.tcp then
+            local wait
+            if #clients == 0 and not listeners.stdio and #listeners.tcp.list == 1 then
+                wait = -1 -- blocking
+            else
+                wait = 0
+            end
+            for _, listener in pairs(listeners.tcp.list) do
+                local fd = vlc.net.accept( listener, wait )
+                new_client( h, fd, fd, client_type.net )
+            end
+        end
+    end
+
+    local function _select( h, timeout )
+        local nfds = math.max( filter_client( fds_read, status.read, status.password ),
+                               filter_client( fds_write, status.write ) ) + 1
+        local ret = vlc.net.select( nfds, fds_read.fds, fds_write.fds,
+                                    timeout or 0.5 )
+        local wclients = {}
+        local rclients = {}
+        if ret > 0 then
+            for _, client in pairs(clients) do
+                if fds_write:isset( client:fd() ) then
+                    table.insert(wclients,client)
+                end
+                if fds_read:isset( client:fd() ) then
+                    table.insert(rclients,client)
+                end
+            end
+        end
+        return wclients, rclients
+    end
+
+    local function destructor( h )
+        print "destructor"
+        for _,client in pairs(clients) do
+            client:send("Shutting down.")
+            if client.type == client_type.tcp then
+                if client.wfd ~= client.rfd then
+                    vlc.net.close(client.rfd)
+                end
+                vlc.net.close(client.wfd)
+            end
+        end
+
+        if listeners.tcp then
+            for _, listener in pairs(listeners.tcp.list) do
+                vlc.net.listen_close( listener )
+            end
+        end
+    end
+
+    local function _broadcast( h, msg )
+        for _,client in pairs(clients) do
+            client:send( msg )
+        end
+    end
+
+    -- the instance
+    local h = { -- data
+                status_callbacks = status_callbacks,
+                -- methods
+                listen = _listen,
+                listen_tcp = _listen_tcp,
+                listen_stdio = _listen_stdio,
+                accept = _accept,
+                select = _select,
+                broadcast = _broadcast,
+              }
+
+    -- the metatable
+    local m = { -- data
+                __metatable = "Nothing to see here. Move along.",
+                -- methods
+                __gc = destructor,
+              }
+
+    setmetatable( h, m )
+
+    return h
+end
diff --git a/share/luaintf/rc.lua b/share/luaintf/rc.lua
new file mode 100644 (file)
index 0000000..19a6268
--- /dev/null
@@ -0,0 +1,521 @@
+--[==========================================================================[
+ rc.lua: remote control module for VLC
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ 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.
+--]==========================================================================]
+
+--[==========================================================================[
+ This is a modules/control/rc.c look alike (with a bunch of new features)
+ Use on local term:
+    vlc -I luarc
+ Use on tcp connection:
+    vlc -I luarc --lua-config "rc={host='localhost:4212'}"
+ Use on multiple hosts (term + 2 tcp ports):
+    vlc -I luarc --lua-config "rc={hosts={'*console','localhost:4212','localhost:5678'}}"
+ Note:
+    -I luarc is an alias for -I lua --lua-intf rc
+--]==========================================================================]
+
+require("common")
+skip = common.skip
+skip2 = function(foo) return skip(skip(foo)) end
+setarg = common.setarg
+strip = common.strip
+
+--[[ Setup default environement ]]
+env = { prompt = "> ";
+        width = 70;
+        autocompletion = 1;
+        autoalias = 1;
+        welcome = "Remote control interface initialized. Type `help' for help."
+      }
+
+--[[ Command functions ]]
+function set_env(name,client,value)
+    if value then
+        local var,val = split_input(value)
+        if val then
+            s = string.gsub(val,"\"(.*)\"","%1")
+            if type(client.env[var])==type(1) then
+                client.env[var] = tonumber(s)
+            else
+                client.env[var] = s
+            end
+        else
+            client:append( tostring(client.env[var]) )
+        end
+    else
+        for e,v in common.pairs_sorted(client.env) do
+            client:append(e.."="..v)
+        end
+    end
+end
+
+function save_env(name,client,value)
+    env = common.table_copy(client.env)
+end
+
+function alias(client,value)
+    if value then
+        local var,val = split_input(value)
+        if commands[var] and type(commands[var]) ~= type("") then
+            client:append("Error: cannot use a primary command as an alias name")
+        else
+            if commands[val] then
+                commands[var]=val
+            else
+                client:append("Error: unknown primary command `"..val.."'.")
+            end
+        end
+    else
+        for c,v in common.pairs_sorted(commands) do
+            if type(v)==type("") then
+                client:append(c.."="..v)
+            end
+        end
+    end
+end
+
+function fixme(name,client)
+    client:append( "FIXME: unimplemented command `"..name.."'." )
+end
+
+function logout(name,client)
+    if client.type == host.client_type.net then
+        client:send("Bye-bye!")
+        client:del()
+    else
+        client:append("Error: Can't logout of stdin/stdout. Use quit or shutdown to close VLC.")
+    end
+end
+
+function shutdown(name,client)
+    client:append("Bye-bye!")
+    h:broadcast("Shutting down.")
+    vlc.msg.err("Requested shutdown.")
+    vlc.quit()
+end
+
+function quit(name,client)
+    if client.type == host.client_type.net then
+        logout(name,client)
+    else
+        shutdown(name,client)
+    end
+end
+
+function add(name,client,arg)
+    -- TODO: parse (and use) options
+    local f
+    if name == "enqueue" then
+        f = vlc.playlist.enqueue
+    else
+        f = vlc.playlist.add
+    end
+    f({{path=arg}})
+end
+
+function playlist(name,client,arg)
+    -- TODO: add possibility to filter playlist items using a mask
+    local playlist
+    if arg == "ml" then
+        playlist = vlc.playlist.get(true)
+        client:append("+----[ Playlist - Media Library ]")
+    else
+        playlist = vlc.playlist.get()
+        client:append("+----[ Playlist ]")
+    end
+    for i, item in pairs(playlist) do
+        local str = "| "..tostring(i).." - "..item.name
+        if item.duration > 0 then
+            str = str.." ("..common.durationtostring(item.duration)..")"
+        end
+        if item.nb_played > 0 then
+            str = str.." played "..tostring(item.nb_played).." time"
+            if item.nb_played > 1 then
+                str = str .. "s"
+            end
+        end
+        client:append(str)
+    end
+    client:append("+----[ End of playlist ]")
+end
+
+function help(name,client,arg)
+    local width = client.env.width
+    local long = (name == "longhelp")
+    local extra = ""
+    if arg then extra = "matching `" .. arg .. "' " end
+    client:append("+----[ Remote control commands "..extra.."]")
+    for i, cmd in ipairs(commands_ordered) do
+        if (cmd == "" or not commands[cmd].adv or long)
+        and (not arg or string.match(cmd,arg)) then
+            local str = "| " .. cmd
+            if cmd ~= "" then
+                local val = commands[cmd]
+                if val.aliases then
+                    for _,a in ipairs(val.aliases) do
+                        str = str .. ", " .. a
+                    end
+                end
+                if val.args then str = str .. " " .. val.args end
+                if #str%2 == 1 then str = str .. " " end
+                str = str .. string.rep(" .",(width-(#str+#val.help)-1)/2)
+                str = str .. string.rep(" ",width-#str-#val.help) .. val.help
+            end
+            client:append(str)
+        end
+    end
+    client:append("+----[ end of help ]")
+end
+
+function input_info(name,client)
+    local categories = vlc.input_info()
+    for cat, infos in pairs(categories) do
+        client:append("+----[ "..cat.." ]")
+        client:append("|")
+        for name, value in pairs(infos) do
+            client:append("| "..name..": "..value)
+        end
+        client:append("|")
+    end
+    client:append("+----[ end of stream info ]")
+end
+
+function playlist_status(name,client)
+    local a,b,c = vlc.playlist.status()
+    client:append( "( new input: " .. tostring(a) .. " )" )
+    client:append( "( audio volume: " .. tostring(b) .. " )")
+    client:append( "( state " .. tostring(c) .. " )")
+end
+
+function is_playing(name,client)
+    if vlc.is_playing() then client:append "1" else client:append "0" end
+end
+
+function ret_print(foo,start,stop)
+    local start = start or ""
+    local stop = stop or ""
+    return function(discard,client,...) client:append(start..tostring(foo(...))..stop) end
+end
+
+function get_time(var,client)
+    return function()
+        local input = vlc.object.input()
+        client:append(math.floor(vlc.var.get( input, var )))
+    end
+end
+
+function titlechap(name,client,value)
+    local input = vlc.object.input()
+    local var = string.gsub( name, "_.*$", "" )
+    if value then
+        vlc.var.set( input, var, value )
+    else
+        local item = vlc.var.get( input, var )
+        -- Todo: add item name conversion
+        client:apped(item)
+    end
+end
+function titlechap_offset(client,offset)
+    return function(name,value)
+        local input = vlc.object.input()
+        local var = string.gsub( name, "_.*$", "" )
+        vlc.var.set( input, var, vlc.var.get( input, var )+offset )
+    end
+end
+
+function seek(name,client,value)
+    local input = vlc.object.input()
+    if string.sub(value,#value)=="%" then
+        vlc.var.set(input,"position",tonumber(string.sub(value,1,#value-1))/100.)
+    else
+        vlc.var.set(input,"time",tonumber(value))
+    end
+end
+
+function volume(name,client,value)
+    if value then
+        vlc.volume.set(value)
+    else
+        client:append(tostring(vlc.volume.get()))
+    end
+end
+
+function rate(name,client)
+    local input = vlc.object.input()
+    if name == "normal" then
+        vlc.var.set(input,"rate",1000) -- FIXME: INPUT_RATE_DEFAULT
+    else
+        vlc.var.set(input,"rate-"..name,nil)
+    end
+end
+
+function listvalue(obj,var)
+    return function(client,value)
+        local o = vlc.object.find(nil,obj,"anywhere")
+        if not o then return end
+        if value then
+            vlc.var.set( o, var, value )
+        else
+            local c = vlc.var.get( o, var )
+            local v, l = vlc.var.get_list( o, var )
+            client:append("+----[ "..var.." ]")
+            for i,val in ipairs(v) do
+                local mark = (val==c)and " *" or ""
+                client:append("| "..tostring(val).." - "..tostring(l[i])..mark)
+            end
+            client:append("+----[ end of "..var.." ]")
+        end
+    end
+end
+
+function eval(client,val)
+    client:append(loadstring("return "..val)())
+end
+
+--[[ Declare commands, register their callback functions and provide
+     help strings here.
+     Syntax is:
+     "<command name>"; { func = <function>; [ args = "<str>"; ] help = "<str>"; [ adv = <bool>; ] [ aliases = { ["<str>";]* }; ] }
+     ]]
+commands_ordered = {
+    { "add"; { func = add; args = "XYZ"; help = "add XYZ to playlist" } };
+    { "enqueue"; { func = add; args = "XYZ"; help = "queue XYZ to playlist" } };
+    { "playlist"; { func = playlist; help = "show items currently in playlist" } };
+    { "play"; { func = skip2(vlc.playlist.play); help = "play stream" } };
+    { "stop"; { func = skip2(vlc.playlist.stop); help = "stop stream" } };
+    { "next"; { func = skip2(vlc.playlist.next); help = "next playlist item" } };
+    { "prev"; { func = skip2(vlc.playlist.prev); help = "previous playlist item" } };
+    { "goto"; { func = skip2(vlc.playlist.goto); help = "goto item at index" } };
+    { "repeat"; { func = skip2(vlc.playlist.repeat_); args = "[on|off]"; help = "toggle playlist repeat" } };
+    { "loop"; { func = skip2(vlc.playlist.loop); args = "[on|off]"; help = "toggle playlist loop" } };
+    { "random"; { func = skip2(vlc.playlist.random); args = "[on|off]"; help = "toggle playlist random" } };
+    { "clear"; { func = skip2(vlc.playlist.clear); help = "clear the playlist" } };
+    { "status"; { func = playlist_status; help = "current playlist status" } };
+    { "title"; { func = titlechap; args = "[X]"; help = "set/get title in current item" } };
+    { "title_n"; { func = titlechap_offset(1); help = "next title in current item" } };
+    { "title_p"; { func = titlechap_offset(-1); help = "previous title in current item" } };
+    { "chapter"; { func = titlechap; args = "[X]"; help = "set/get chapter in current item" } };
+    { "chapter_n"; { func = titlechap_offset(1); help = "next chapter in current item" } };
+    { "chapter_p"; { func = titlechap_offset(-1); help = "previous chapter in current item" } };
+    { "" };
+    { "seek"; { func = seek; args = "X"; help = "seek in seconds, for instance `seek 12'" } };
+    { "pause"; { func = setarg(common.hotkey,"key-play-pause"); help = "toggle pause" } };
+    { "fastforward"; { func = setarg(common.hotkey,"key-jump+extrashort"); help = "set to maximum rate" } };
+    { "rewind"; { func = setarg(common.hotkey,"key-jump-extrashort"); help = "set to minimum rate" } };
+    { "faster"; { func = rate; help = "faster playing of stream" } };
+    { "slower"; { func = rate; help = "slower playing of stream" } };
+    { "normal"; { func = rate; help = "normal playing of stream" } };
+    { "fullscreen"; { func = skip2(vlc.fullscreen); args = "[on|off]"; help = "toggle fullscreen"; aliases = { "f", "F" } } };
+    { "info"; { func = input_info; help = "information about the current stream" } };
+    { "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
+    { "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
+    { "get_title"; { func = ret_print(vlc.get_title); help = "the title of the current stream" } };
+    { "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
+    { "" };
+    { "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
+    { "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
+    { "voldown"; { func = ret_print(vlc.volume.down,"( audio volume: "," )"); args = "[X]"; help = "lower audio volume X steps" } };
+    { "adev"; { func = skip(listvalue("aout","audio-device")); args = "[X]"; help = "set/get audio device" } };
+    { "achan"; { func = skip(listvalue("aout","audio-channels")); args = "[X]"; help = "set/get audio channels" } };
+    { "atrack"; { func = skip(listvalue("input","audio-es")); args = "[X]"; help = "set/get audio track" } };
+    { "vtrack"; { func = skip(listvalue("input","video-es")); args = "[X]"; help = "set/get video track" } };
+    { "vratio"; { func = skip(listvalue("vout","aspect-ratio")); args = "[X]"; help = "set/get video aspect ratio" } };
+    { "vcrop"; { func = skip(listvalue("vout","crop")); args = "[X]"; help = "set/get video crop"; aliases = { "crop" } } };
+    { "vzoom"; { func = skip(listvalue("vout","zoom")); args = "[X]"; help = "set/get video zoom"; aliases = { "zoom" } } };
+    { "snapshot"; { func = common.snapshot; help = "take video snapshot" } };
+    { "strack"; { func = skip(listvalue("input","spu-es")); args = "[X]"; help = "set/get subtitles track" } };
+    { "hotkey"; { func = skip(common.hotkey); args = "[hotkey name]"; help = "simulate hotkey press"; adv = true; aliases = { "key" } } };
+    { "menu"; { func = fixme; args = "[on|off|up|down|left|right|select]"; help = "use menu"; adv = true } };
+    { "" };
+    { "set"; { func = set_env; args = "[var [value]]"; help = "set/get env var"; adv = true } };
+    { "save_env"; { func = save_env; help = "save env vars (for future clients)"; adv = true } };
+    { "alias"; { func = skip(alias); args = "[cmd]"; help = "set/get command aliases"; adv = true } };
+    { "eval"; { func = skip(eval); help = "eval some lua (*debug*)"; adv =true } }; -- FIXME: comment out if you're not debugging
+    { "help"; { func = help; args = "[pattern]"; help = "a help message"; aliases = { "?" } } };
+    { "longhelp"; { func = help; args = "[pattern]"; help = "a longer help message" } };
+    { "logout"; { func = logout; help = "exit (if in a socket connection)" } };
+    { "quit"; { func = quit; help = "quit VLC (or logout if in a socket connection)" } };
+    { "shutdown"; { func = shutdown; help = "shutdown VLC" } };
+    }
+commands = {}
+for i, cmd in ipairs( commands_ordered ) do
+    if #cmd == 2 then
+        commands[cmd[1]]=cmd[2]
+        if cmd[2].aliases then
+            for _,a in ipairs(cmd[2].aliases) do
+                commands[a]=cmd[1]
+            end
+        end
+    end
+    commands_ordered[i]=cmd[1]
+end
+--[[ From now on commands_ordered is a list of the different command names
+     and commands is a associative array indexed by the command name. ]]
+
+-- Compute the column width used when printing a the autocompletion list
+env.colwidth = 0
+for c,_ in pairs(commands) do
+    if #c > env.colwidth then env.colwidth = #c end
+end
+env.coldwidth = env.colwidth + 1
+
+-- Count unimplemented functions
+do
+    local count = 0
+    local list = "("
+    for c,v in pairs(commands) do
+        if v.func == fixme then
+            count = count + 1
+            if count ~= 1 then
+                list = list..","
+            end
+            list = list..c
+        end
+    end
+    list = list..")"
+    if count ~= 0 then
+        env.welcome = env.welcome .. "\r\nWarning: "..count.." functions are still unimplemented "..list.."."
+    end
+end
+
+--[[ Utils ]]
+function split_input(input)
+    local input = strip(input)
+    local s = string.find(input," ")
+    if s then
+        return string.sub(input,0,s-1), strip(string.sub(input,s))
+    else
+        return input
+    end
+end
+
+function call_command(cmd,client,arg)
+    if type(commands[cmd]) == type("") then
+        cmd = commands[cmd]
+    end
+    local ok, msg
+    if arg ~= nil then
+        ok, msg = pcall( commands[cmd].func, cmd, client, arg )
+    else
+        ok, msg = pcall( commands[cmd].func, cmd, client )
+    end
+    if not ok then
+        local a = arg or ""
+        if a ~= "" then a = " " .. a end
+        client:append("Error in `"..cmd..a.."' ".. msg)
+    end
+end
+
+function call_libvlc_command(cmd,client,arg)
+    local ok, vlcerr, vlcmsg = pcall( vlc.libvlc_command, cmd, arg )
+    if not ok then
+        local a = arg or ""
+        if a ~= "" then a = " " .. a end
+        client:append("Error in `"..cmd..a.."' ".. vlcerr) -- when pcall fails, the 2nd arg is the error message.
+    end
+    return vlcerr
+end
+
+--[[ Setup host ]]
+require("host")
+h = host.host()
+-- No auth
+h.status_callbacks[host.status.password] = function(client)
+    client.env = common.table_copy( env )
+    client:send( client.env.welcome .. "\r\n")
+    client:switch_status(host.status.read)
+end
+-- Print prompt when switching a client's status to `read'
+h.status_callbacks[host.status.read] = function(client) client:send( client.env.prompt ) end
+
+h:listen( config.hosts or config.host or "*console" )
+
+--[[ The main loop ]]
+while not vlc.should_die() do
+    h:accept()
+    local write, read = h:select(0.1)
+
+    for _, client in pairs(write) do
+        local len = client:send()
+        client.buffer = string.sub(client.buffer,len+1)
+        if client.buffer == "" then client:switch_status(host.status.read) end
+    end
+
+    for _, client in pairs(read) do
+        local input = client:recv(1000)
+        if string.match(input,"\n$") then
+            client.buffer = string.gsub(client.buffer..input,"\r?\n$","")
+            done = true
+        elseif client.buffer == ""
+           and ((client.type == host.client_type.stdio and input == "")
+           or  (client.type == host.client_type.net and input == "\004")) then
+            -- Caught a ^D
+            client.buffer = "quit"
+            done = true
+        else
+            client.buffer = client.buffer .. input
+        end
+        if done then
+            local cmd,arg = split_input(client.buffer)
+            client.buffer = ""
+            client:switch_status(host.status.write)
+            if commands[cmd] then
+                call_command(cmd,client,arg)
+            else
+                if client.type == host.client_type.stdio 
+                and call_libvlc_command(cmd,client,arg) == 0 then
+                else
+                    local choices = {}
+                    if client.env.autocompletion ~= 0 then
+                        for v,_ in common.pairs_sorted(commands) do
+                            if string.sub(v,0,#cmd)==cmd then
+                                table.insert(choices, v)
+                            end
+                        end
+                    end
+                    if #choices == 1 and client.env.autoalias ~= 0 then
+                        -- client:append("Aliasing to \""..choices[1].."\".")
+                        cmd = choices[1]
+                        call_command(cmd,client,arg)
+                    else
+                        client:append("Unknown command `"..cmd.."'. Type `help' for help.")
+                        if #choices ~= 0 then
+                            client:append("Possible choices are:")
+                            local cols = math.floor(client.env.width/(client.env.colwidth+1))
+                            local fmt = "%-"..client.env.colwidth.."s"
+                            for i = 1, #choices do
+                                choices[i] = string.format(fmt,choices[i])
+                            end
+                            for i = 1, #choices, cols do
+                                local j = i + cols - 1
+                                if j > #choices then j = #choices end
+                                client:append("  "..table.concat(choices," ",i,j))
+                            end
+                        end
+                    end
+                end
+            end
+        end
+    end
+end
diff --git a/share/luaintf/telnet.lua b/share/luaintf/telnet.lua
new file mode 100644 (file)
index 0000000..3c4efcb
--- /dev/null
@@ -0,0 +1,204 @@
+--[==========================================================================[
+ telnet.lua: VLM interface plugin
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id: $
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+
+ 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.
+--]==========================================================================]
+
+--[==========================================================================[
+ Copy (features wise) of the original VLC modules/control/telnet.c module.
+
+ Differences are:
+    * it's in Lua
+    * 'lock' command to lock the telnet promt
+    * possibility to listen on different hosts including stdin
+      for example:
+        listen on stdin: vlc -I lua --lua-intf telnet --lua-config "telnet={host='*console'}"
+        listen on stdin + 2 ports on localhost: vlc -I lua --lua-intf telnet --lua-config "telnet={hosts={'localhost:4212','localhost:5678','*console'}}"
+--]==========================================================================]
+
+require "host"
+
+--[[ Some telnet command special characters ]]
+WILL = "\251" -- Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
+WONT = "\252" -- Indicates the refusal to perform, or continue performing, the indicated option.
+DO   = "\253" -- Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
+DONT = "\254" -- Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option.
+IAC  = "\255" -- Interpret as command
+
+ECHO = "\001"
+
+--[[ Client status change callbacks ]]
+function on_password( client )
+    if client.type == host.client_type.net then
+        client:send( "Password: " ..IAC..WILL..ECHO )
+    else
+        -- no authentification needed on stdin
+        client:switch_status( host.status.read )
+    end
+end
+function on_read( client )
+    client:send( "> " )
+end
+function on_write( client )
+end
+
+--[[ Misc functions ]]
+function telnet_commands( client )
+    -- remove telnet command replies from the client's data
+    client.buffer = string.gsub( client.buffer, IAC.."["..DO..DONT..WILL..WONT.."].", "" )
+end
+
+function vlm_message_to_string(client,message,prefix)
+    local prefix = prefix or ""
+    if message.value then
+        client:append(prefix .. message.name .. " : " .. message.value)
+        return
+    else
+        client:append(prefix .. message.name)
+        if message.children then
+            for i,c in ipairs(message.children) do
+                vlm_message_to_string(client,c,prefix.."    ")
+            end
+        end
+        return
+    end
+end
+
+--[[ Configure the host ]]
+h = host.host()
+
+h.status_callbacks[host.status.password] = on_password
+h.status_callbacks[host.status.read] = on_read
+h.status_callbacks[host.status.write] = on_write
+
+h:listen( config.hosts or config.host or "localhost:4212" )
+password = config.password or "admin"
+
+--[[ Launch vlm ]]
+vlm = vlc.vlm.new()
+
+--[[ Commands ]]
+function shutdown(client)
+    h:broadcast("Shutting down.\r\n")
+    vlc.msg.err("shutdown requested")
+    vlc.quit()
+    return true
+end
+function logout(client)
+    client:del()
+    return true
+end
+function quit(client)
+    if client.type == host.client_type.net then
+        return logout(client)
+    else
+        return shutdown(client)
+    end
+end
+function lock(client)
+    client:send("\r\n")
+    client:switch_status( host.status.password )
+    client.buffer = ""
+    return false
+end
+function help(client)
+    client:append("    Telnet Specific Commands:")
+    for c,t in pairs(commands) do
+        client:append("        "..c.." : "..t.help)
+    end
+    return true
+end
+commands = {
+    ["shutdown"] = { func = shutdown, help = "shutdown VLC" },
+    ["quit"]     = { func = quit, help = "logout from telnet/shutdown VLC from local shell" },
+    ["logout"]   = { func = logout, help = "logout" },
+    ["lock"]     = { func = lock, help = "lock the telnet prompt" },
+    ["help"]     = { func = help, help = "show this help", dovlm = true },
+    }
+
+function client_command( client )
+    local cmd = client.buffer
+    client.buffer = ""
+    if not commands[cmd] or not commands[cmd].func or commands[cmd].dovlm then
+        -- if it's not an interface specific command, it has to be a VLM command
+        message = vlc.vlm.execute_command( vlm, cmd )
+        vlm_message_to_string( client, message )
+        if not commands[cmd] or not commands[cmd].func and not commands[cmd].dovlm then
+            return true
+        end
+    end
+    ok, msg = pcall( commands[cmd].func, client )
+    if not ok then
+        client:append( "Error in `"..cmd.."' "..msg )
+        return true
+    end
+    return msg
+end
+
+--[[ The main loop ]]
+while not vlc.should_die() do
+    h:accept()
+    local w, r = h:select( 0.1 )
+
+    -- Handle writes
+    for _, client in pairs(w) do
+        local len = client:send()
+        client.buffer = string.sub(client.buffer,len+1)
+        if client.buffer == "" then client:switch_status( host.status.read ) end
+    end
+
+    -- Handle reads
+    for _, client in pairs(r) do
+        local str = client:recv(1000)
+        local done = false
+        if string.match(str,"\n$") then
+            client.buffer = string.gsub(client.buffer..str,"\r?\n$","")
+            done = true
+        elseif client.buffer == ""
+           and ((client.type == host.client_type.stdio and str == "")
+           or  (client.type == host.client_type.net and str == "\004")) then
+            -- Caught a ^D
+            client.buffer = "quit"
+            done = true
+        else
+            client.buffer = client.buffer .. str
+        end
+        if client.type == host.client_type.net then
+            telnet_commands( client )
+        end
+        if done then
+            if client.status == host.status.password then
+                if client.buffer == password then
+                    client:send( IAC..WONT..ECHO.."\r\nWelcome, Master\r\n" )
+                    client.buffer = ""
+                    client:switch_status( host.status.write )
+                else
+                    client:send( "\r\nWrong password\r\nPassword: " )
+                    client.buffer = ""
+                end
+            elseif client_command( client ) then
+                client:switch_status( host.status.write )
+            end
+        end
+    end
+end
+
+--[[ Clean up ]]
+vlc.vlm.delete( vlm )