view src/if_lua.c @ 16150:b23048205589 v8.1.1080

patch 8.1.1080: when a screendump test fails, moving the file is a hassle commit https://github.com/vim/vim/commit/ef7f0e367eeaf6fb31b1caa0e3de1a4b07e86af3 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Mar 30 15:59:51 2019 +0100 patch 8.1.1080: when a screendump test fails, moving the file is a hassle Problem: When a screendump test fails, moving the file is a hassle. Solution: Instead of appending ".failed" to the file name, keep the same file name but put the screendump in the "failed" directory. Then the file name only needs to be typed once when moving a screendump.
author Bram Moolenaar <Bram@vim.org>
date Sat, 30 Mar 2019 16:00:06 +0100
parents 643fe07aa0e2
children 1d30eb64a7a2
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4 noet:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Lua interface by Luis Carvalho
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

#include "vim.h"

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

/* Only do the following when the feature is enabled.  Needed for "make
 * depend". */
#if defined(FEAT_LUA) || defined(PROTO)

#define LUAVIM_CHUNKNAME "vim chunk"
#define LUAVIM_NAME "vim"
#define LUAVIM_EVALNAME "luaeval"
#define LUAVIM_EVALHEADER "local _A=select(1,...) return "

typedef buf_T *luaV_Buffer;
typedef win_T *luaV_Window;
typedef dict_T *luaV_Dict;
typedef list_T *luaV_List;
typedef blob_T *luaV_Blob;
typedef struct {
    char_u	*name;	// funcref
    dict_T	*self;	// selfdict
} luaV_Funcref;
typedef void (*msgfunc_T)(char_u *);

static const char LUAVIM_DICT[] = "dict";
static const char LUAVIM_LIST[] = "list";
static const char LUAVIM_BLOB[] = "blob";
static const char LUAVIM_FUNCREF[] = "funcref";
static const char LUAVIM_BUFFER[] = "buffer";
static const char LUAVIM_WINDOW[] = "window";
static const char LUAVIM_FREE[] = "luaV_free";
static const char LUAVIM_LUAEVAL[] = "luaV_luaeval";
static const char LUAVIM_SETREF[] = "luaV_setref";

/* most functions are closures with a cache table as first upvalue;
 * get/setudata manage references to vim userdata in cache table through
 * object pointers (light userdata) */
#define luaV_getudata(L, v) \
    lua_pushlightuserdata((L), (void *) (v)); \
    lua_rawget((L), lua_upvalueindex(1))
#define luaV_setudata(L, v) \
    lua_pushlightuserdata((L), (void *) (v)); \
    lua_pushvalue((L), -2); \
    lua_rawset((L), lua_upvalueindex(1))
#define luaV_getfield(L, s) \
    lua_pushlightuserdata((L), (void *)(s)); \
    lua_rawget((L), LUA_REGISTRYINDEX)
#define luaV_checksandbox(L) \
    if (sandbox) luaL_error((L), "not allowed in sandbox")
#define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg)
#define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
#define luaV_checktypval(L, a, v, msg) \
    do { \
        if (luaV_totypval(L, a, v) == FAIL) \
	    luaL_error(L, msg ": cannot convert value"); \
    } while (0)

static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
static luaV_Blob *luaV_pushblob(lua_State *L, blob_T *blo);
static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);

#if LUA_VERSION_NUM <= 501
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
#define luaL_typeerror luaL_typerror
#else
#define luaV_openlib luaL_setfuncs
#endif

#ifdef DYNAMIC_LUA

#ifndef MSWIN
# include <dlfcn.h>
# define HANDLE void*
# define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL)
# define symbol_from_dll dlsym
# define close_dll dlclose
#else
# define load_dll vimLoadLib
# define symbol_from_dll GetProcAddress
# define close_dll FreeLibrary
#endif

/* lauxlib */
#if LUA_VERSION_NUM <= 501
#define luaL_register dll_luaL_register
#define luaL_prepbuffer dll_luaL_prepbuffer
#define luaL_openlib dll_luaL_openlib
#define luaL_typerror dll_luaL_typerror
#define luaL_loadfile dll_luaL_loadfile
#define luaL_loadbuffer dll_luaL_loadbuffer
#else
#define luaL_prepbuffsize dll_luaL_prepbuffsize
#define luaL_setfuncs dll_luaL_setfuncs
#define luaL_loadfilex dll_luaL_loadfilex
#define luaL_loadbufferx dll_luaL_loadbufferx
#define luaL_argerror dll_luaL_argerror
#endif
#define luaL_checkany dll_luaL_checkany
#define luaL_checklstring dll_luaL_checklstring
#define luaL_checkinteger dll_luaL_checkinteger
#define luaL_optinteger dll_luaL_optinteger
#define luaL_checktype dll_luaL_checktype
#define luaL_error dll_luaL_error
#define luaL_newstate dll_luaL_newstate
#define luaL_buffinit dll_luaL_buffinit
#define luaL_addlstring dll_luaL_addlstring
#define luaL_pushresult dll_luaL_pushresult
/* lua */
#if LUA_VERSION_NUM <= 501
#define lua_tonumber dll_lua_tonumber
#define lua_tointeger dll_lua_tointeger
#define lua_call dll_lua_call
#define lua_pcall dll_lua_pcall
#else
#define lua_tonumberx dll_lua_tonumberx
#define lua_tointegerx dll_lua_tointegerx
#define lua_callk dll_lua_callk
#define lua_pcallk dll_lua_pcallk
#define lua_getglobal dll_lua_getglobal
#define lua_setglobal dll_lua_setglobal
#endif
#if LUA_VERSION_NUM <= 502
#define lua_replace dll_lua_replace
#define lua_remove dll_lua_remove
#endif
#if LUA_VERSION_NUM >= 503
#define lua_rotate dll_lua_rotate
#define lua_copy dll_lua_copy
#endif
#define lua_typename dll_lua_typename
#define lua_close dll_lua_close
#define lua_gettop dll_lua_gettop
#define lua_settop dll_lua_settop
#define lua_pushvalue dll_lua_pushvalue
#define lua_isnumber dll_lua_isnumber
#define lua_isstring dll_lua_isstring
#define lua_type dll_lua_type
#define lua_rawequal dll_lua_rawequal
#define lua_toboolean dll_lua_toboolean
#define lua_tolstring dll_lua_tolstring
#define lua_touserdata dll_lua_touserdata
#define lua_pushnil dll_lua_pushnil
#define lua_pushnumber dll_lua_pushnumber
#define lua_pushinteger dll_lua_pushinteger
#define lua_pushlstring dll_lua_pushlstring
#define lua_pushstring dll_lua_pushstring
#define lua_pushfstring dll_lua_pushfstring
#define lua_pushcclosure dll_lua_pushcclosure
#define lua_pushboolean dll_lua_pushboolean
#define lua_pushlightuserdata dll_lua_pushlightuserdata
#define lua_getfield dll_lua_getfield
#define lua_rawget dll_lua_rawget
#define lua_rawgeti dll_lua_rawgeti
#define lua_createtable dll_lua_createtable
#if LUA_VERSION_NUM >= 504
 #define lua_newuserdatauv dll_lua_newuserdatauv
#else
 #define lua_newuserdata dll_lua_newuserdata
#endif
#define lua_getmetatable dll_lua_getmetatable
#define lua_setfield dll_lua_setfield
#define lua_rawset dll_lua_rawset
#define lua_rawseti dll_lua_rawseti
#define lua_setmetatable dll_lua_setmetatable
#define lua_next dll_lua_next
/* libs */
#define luaopen_base dll_luaopen_base
#define luaopen_table dll_luaopen_table
#define luaopen_string dll_luaopen_string
#define luaopen_math dll_luaopen_math
#define luaopen_io dll_luaopen_io
#define luaopen_os dll_luaopen_os
#define luaopen_package dll_luaopen_package
#define luaopen_debug dll_luaopen_debug
#define luaL_openlibs dll_luaL_openlibs

/* lauxlib */
#if LUA_VERSION_NUM <= 501
void (*dll_luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l);
char *(*dll_luaL_prepbuffer) (luaL_Buffer *B);
void (*dll_luaL_openlib) (lua_State *L, const char *libname, const luaL_Reg *l, int nup);
int (*dll_luaL_typerror) (lua_State *L, int narg, const char *tname);
int (*dll_luaL_loadfile) (lua_State *L, const char *filename);
int (*dll_luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, const char *name);
#else
char *(*dll_luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
void (*dll_luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
int (*dll_luaL_loadfilex) (lua_State *L, const char *filename, const char *mode);
int (*dll_luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
int (*dll_luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
#endif
void (*dll_luaL_checkany) (lua_State *L, int narg);
const char *(*dll_luaL_checklstring) (lua_State *L, int numArg, size_t *l);
lua_Integer (*dll_luaL_checkinteger) (lua_State *L, int numArg);
lua_Integer (*dll_luaL_optinteger) (lua_State *L, int nArg, lua_Integer def);
void (*dll_luaL_checktype) (lua_State *L, int narg, int t);
int (*dll_luaL_error) (lua_State *L, const char *fmt, ...);
lua_State *(*dll_luaL_newstate) (void);
void (*dll_luaL_buffinit) (lua_State *L, luaL_Buffer *B);
void (*dll_luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
void (*dll_luaL_pushresult) (luaL_Buffer *B);
/* lua */
#if LUA_VERSION_NUM <= 501
lua_Number (*dll_lua_tonumber) (lua_State *L, int idx);
lua_Integer (*dll_lua_tointeger) (lua_State *L, int idx);
void (*dll_lua_call) (lua_State *L, int nargs, int nresults);
int (*dll_lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
#else
lua_Number (*dll_lua_tonumberx) (lua_State *L, int idx, int *isnum);
lua_Integer (*dll_lua_tointegerx) (lua_State *L, int idx, int *isnum);
void (*dll_lua_callk) (lua_State *L, int nargs, int nresults, int ctx,
	lua_CFunction k);
int (*dll_lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
	int ctx, lua_CFunction k);
void (*dll_lua_getglobal) (lua_State *L, const char *var);
void (*dll_lua_setglobal) (lua_State *L, const char *var);
#endif
#if LUA_VERSION_NUM <= 502
void (*dll_lua_replace) (lua_State *L, int idx);
void (*dll_lua_remove) (lua_State *L, int idx);
#endif
#if LUA_VERSION_NUM >= 503
void  (*dll_lua_rotate) (lua_State *L, int idx, int n);
void (*dll_lua_copy) (lua_State *L, int fromidx, int toidx);
#endif
const char *(*dll_lua_typename) (lua_State *L, int tp);
void       (*dll_lua_close) (lua_State *L);
int (*dll_lua_gettop) (lua_State *L);
void (*dll_lua_settop) (lua_State *L, int idx);
void (*dll_lua_pushvalue) (lua_State *L, int idx);
int (*dll_lua_isnumber) (lua_State *L, int idx);
int (*dll_lua_isstring) (lua_State *L, int idx);
int (*dll_lua_type) (lua_State *L, int idx);
int (*dll_lua_rawequal) (lua_State *L, int idx1, int idx2);
int (*dll_lua_toboolean) (lua_State *L, int idx);
const char *(*dll_lua_tolstring) (lua_State *L, int idx, size_t *len);
void *(*dll_lua_touserdata) (lua_State *L, int idx);
void (*dll_lua_pushnil) (lua_State *L);
void (*dll_lua_pushnumber) (lua_State *L, lua_Number n);
void (*dll_lua_pushinteger) (lua_State *L, lua_Integer n);
void (*dll_lua_pushlstring) (lua_State *L, const char *s, size_t l);
void (*dll_lua_pushstring) (lua_State *L, const char *s);
const char *(*dll_lua_pushfstring) (lua_State *L, const char *fmt, ...);
void (*dll_lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
void (*dll_lua_pushboolean) (lua_State *L, int b);
void (*dll_lua_pushlightuserdata) (lua_State *L, void *p);
void (*dll_lua_getfield) (lua_State *L, int idx, const char *k);
#if LUA_VERSION_NUM <= 502
void (*dll_lua_rawget) (lua_State *L, int idx);
void (*dll_lua_rawgeti) (lua_State *L, int idx, int n);
#else
int (*dll_lua_rawget) (lua_State *L, int idx);
int (*dll_lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
#endif
void (*dll_lua_createtable) (lua_State *L, int narr, int nrec);
#if LUA_VERSION_NUM >= 504
void *(*dll_lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
#else
void *(*dll_lua_newuserdata) (lua_State *L, size_t sz);
#endif
int (*dll_lua_getmetatable) (lua_State *L, int objindex);
void (*dll_lua_setfield) (lua_State *L, int idx, const char *k);
void (*dll_lua_rawset) (lua_State *L, int idx);
#if LUA_VERSION_NUM <= 502
void (*dll_lua_rawseti) (lua_State *L, int idx, int n);
#else
void (*dll_lua_rawseti) (lua_State *L, int idx, lua_Integer n);
#endif
int (*dll_lua_setmetatable) (lua_State *L, int objindex);
int (*dll_lua_next) (lua_State *L, int idx);
/* libs */
int (*dll_luaopen_base) (lua_State *L);
int (*dll_luaopen_table) (lua_State *L);
int (*dll_luaopen_string) (lua_State *L);
int (*dll_luaopen_math) (lua_State *L);
int (*dll_luaopen_io) (lua_State *L);
int (*dll_luaopen_os) (lua_State *L);
int (*dll_luaopen_package) (lua_State *L);
int (*dll_luaopen_debug) (lua_State *L);
void (*dll_luaL_openlibs) (lua_State *L);

typedef void **luaV_function;
typedef struct {
    const char *name;
    luaV_function func;
} luaV_Reg;

static const luaV_Reg luaV_dll[] = {
    /* lauxlib */
#if LUA_VERSION_NUM <= 501
    {"luaL_register", (luaV_function) &dll_luaL_register},
    {"luaL_prepbuffer", (luaV_function) &dll_luaL_prepbuffer},
    {"luaL_openlib", (luaV_function) &dll_luaL_openlib},
    {"luaL_typerror", (luaV_function) &dll_luaL_typerror},
    {"luaL_loadfile", (luaV_function) &dll_luaL_loadfile},
    {"luaL_loadbuffer", (luaV_function) &dll_luaL_loadbuffer},
#else
    {"luaL_prepbuffsize", (luaV_function) &dll_luaL_prepbuffsize},
    {"luaL_setfuncs", (luaV_function) &dll_luaL_setfuncs},
    {"luaL_loadfilex", (luaV_function) &dll_luaL_loadfilex},
    {"luaL_loadbufferx", (luaV_function) &dll_luaL_loadbufferx},
    {"luaL_argerror", (luaV_function) &dll_luaL_argerror},
#endif
    {"luaL_checkany", (luaV_function) &dll_luaL_checkany},
    {"luaL_checklstring", (luaV_function) &dll_luaL_checklstring},
    {"luaL_checkinteger", (luaV_function) &dll_luaL_checkinteger},
    {"luaL_optinteger", (luaV_function) &dll_luaL_optinteger},
    {"luaL_checktype", (luaV_function) &dll_luaL_checktype},
    {"luaL_error", (luaV_function) &dll_luaL_error},
    {"luaL_newstate", (luaV_function) &dll_luaL_newstate},
    {"luaL_buffinit", (luaV_function) &dll_luaL_buffinit},
    {"luaL_addlstring", (luaV_function) &dll_luaL_addlstring},
    {"luaL_pushresult", (luaV_function) &dll_luaL_pushresult},
    /* lua */
#if LUA_VERSION_NUM <= 501
    {"lua_tonumber", (luaV_function) &dll_lua_tonumber},
    {"lua_tointeger", (luaV_function) &dll_lua_tointeger},
    {"lua_call", (luaV_function) &dll_lua_call},
    {"lua_pcall", (luaV_function) &dll_lua_pcall},
#else
    {"lua_tonumberx", (luaV_function) &dll_lua_tonumberx},
    {"lua_tointegerx", (luaV_function) &dll_lua_tointegerx},
    {"lua_callk", (luaV_function) &dll_lua_callk},
    {"lua_pcallk", (luaV_function) &dll_lua_pcallk},
    {"lua_getglobal", (luaV_function) &dll_lua_getglobal},
    {"lua_setglobal", (luaV_function) &dll_lua_setglobal},
#endif
#if LUA_VERSION_NUM <= 502
    {"lua_replace", (luaV_function) &dll_lua_replace},
    {"lua_remove", (luaV_function) &dll_lua_remove},
#endif
#if LUA_VERSION_NUM >= 503
    {"lua_rotate", (luaV_function) &dll_lua_rotate},
    {"lua_copy", (luaV_function) &dll_lua_copy},
#endif
    {"lua_typename", (luaV_function) &dll_lua_typename},
    {"lua_close", (luaV_function) &dll_lua_close},
    {"lua_gettop", (luaV_function) &dll_lua_gettop},
    {"lua_settop", (luaV_function) &dll_lua_settop},
    {"lua_pushvalue", (luaV_function) &dll_lua_pushvalue},
    {"lua_isnumber", (luaV_function) &dll_lua_isnumber},
    {"lua_isstring", (luaV_function) &dll_lua_isstring},
    {"lua_type", (luaV_function) &dll_lua_type},
    {"lua_rawequal", (luaV_function) &dll_lua_rawequal},
    {"lua_toboolean", (luaV_function) &dll_lua_toboolean},
    {"lua_tolstring", (luaV_function) &dll_lua_tolstring},
    {"lua_touserdata", (luaV_function) &dll_lua_touserdata},
    {"lua_pushnil", (luaV_function) &dll_lua_pushnil},
    {"lua_pushnumber", (luaV_function) &dll_lua_pushnumber},
    {"lua_pushinteger", (luaV_function) &dll_lua_pushinteger},
    {"lua_pushlstring", (luaV_function) &dll_lua_pushlstring},
    {"lua_pushstring", (luaV_function) &dll_lua_pushstring},
    {"lua_pushfstring", (luaV_function) &dll_lua_pushfstring},
    {"lua_pushcclosure", (luaV_function) &dll_lua_pushcclosure},
    {"lua_pushboolean", (luaV_function) &dll_lua_pushboolean},
    {"lua_pushlightuserdata", (luaV_function) &dll_lua_pushlightuserdata},
    {"lua_getfield", (luaV_function) &dll_lua_getfield},
    {"lua_rawget", (luaV_function) &dll_lua_rawget},
    {"lua_rawgeti", (luaV_function) &dll_lua_rawgeti},
    {"lua_createtable", (luaV_function) &dll_lua_createtable},
#if LUA_VERSION_NUM >= 504
    {"lua_newuserdatauv", (luaV_function) &dll_lua_newuserdatauv},
#else
    {"lua_newuserdata", (luaV_function) &dll_lua_newuserdata},
#endif
    {"lua_getmetatable", (luaV_function) &dll_lua_getmetatable},
    {"lua_setfield", (luaV_function) &dll_lua_setfield},
    {"lua_rawset", (luaV_function) &dll_lua_rawset},
    {"lua_rawseti", (luaV_function) &dll_lua_rawseti},
    {"lua_setmetatable", (luaV_function) &dll_lua_setmetatable},
    {"lua_next", (luaV_function) &dll_lua_next},
    /* libs */
    {"luaopen_base", (luaV_function) &dll_luaopen_base},
    {"luaopen_table", (luaV_function) &dll_luaopen_table},
    {"luaopen_string", (luaV_function) &dll_luaopen_string},
    {"luaopen_math", (luaV_function) &dll_luaopen_math},
    {"luaopen_io", (luaV_function) &dll_luaopen_io},
    {"luaopen_os", (luaV_function) &dll_luaopen_os},
    {"luaopen_package", (luaV_function) &dll_luaopen_package},
    {"luaopen_debug", (luaV_function) &dll_luaopen_debug},
    {"luaL_openlibs", (luaV_function) &dll_luaL_openlibs},
    {NULL, NULL}
};

static HANDLE hinstLua = NULL;

    static void
end_dynamic_lua(void)
{
    if (hinstLua)
    {
	close_dll(hinstLua);
	hinstLua = 0;
    }
}

    static int
lua_link_init(char *libname, int verbose)
{
    const luaV_Reg *reg;
    if (hinstLua) return OK;
    hinstLua = load_dll(libname);
    if (!hinstLua)
    {
	if (verbose)
	    semsg(_(e_loadlib), libname);
	return FAIL;
    }
    for (reg = luaV_dll; reg->func; reg++)
    {
	if ((*reg->func = symbol_from_dll(hinstLua, reg->name)) == NULL)
	{
	    close_dll(hinstLua);
	    hinstLua = 0;
	    if (verbose)
		semsg(_(e_loadfunc), reg->name);
	    return FAIL;
	}
    }
    return OK;
}
#endif /* DYNAMIC_LUA */

#if defined(DYNAMIC_LUA) || defined(PROTO)
    int
lua_enabled(int verbose)
{
    return lua_link_init((char *)p_luadll, verbose) == OK;
}
#endif

#if LUA_VERSION_NUM > 501
    static int
luaL_typeerror(lua_State *L, int narg, const char *tname)
{
    const char *msg = lua_pushfstring(L, "%s expected, got %s",
	    tname, luaL_typename(L, narg));
    return luaL_argerror(L, narg, msg);
}
#endif


/* =======   Internal   ======= */

    static void
luaV_newmetatable(lua_State *L, const char *tname)
{
    lua_newtable(L);
    lua_pushlightuserdata(L, (void *) tname);
    lua_pushvalue(L, -2);
    lua_rawset(L, LUA_REGISTRYINDEX);
}

    static void *
luaV_toudata(lua_State *L, int ud, const char *tname)
{
    void *p = lua_touserdata(L, ud);

    if (p != NULL) /* value is userdata? */
    {
	if (lua_getmetatable(L, ud)) /* does it have a metatable? */
	{
	    luaV_getfield(L, tname); /* get metatable */
	    if (lua_rawequal(L, -1, -2)) /* MTs match? */
	    {
		lua_pop(L, 2); /* MTs */
		return p;
	    }
	}
    }
    return NULL;
}

    static void *
luaV_checkcache(lua_State *L, void *p)
{
    luaV_getudata(L, p);
    if (lua_isnil(L, -1)) luaL_error(L, "invalid object");
    lua_pop(L, 1);
    return p;
}

#define luaV_unbox(L,luatyp,ud) (*((luatyp *) lua_touserdata((L),(ud))))

#define luaV_checkvalid(L,luatyp,ud) \
    luaV_checkcache((L), (void *) luaV_unbox((L),luatyp,(ud)))

    static void *
luaV_checkudata(lua_State *L, int ud, const char *tname)
{
    void *p = luaV_toudata(L, ud, tname);
    if (p == NULL) luaL_typeerror(L, ud, tname);
    return p;
}

    static void
luaV_pushtypval(lua_State *L, typval_T *tv)
{
    if (tv == NULL)
    {
	lua_pushnil(L);
	return;
    }
    switch (tv->v_type)
    {
	case VAR_STRING:
	    lua_pushstring(L, tv->vval.v_string == NULL
					    ? "" : (char *)tv->vval.v_string);
	    break;
	case VAR_NUMBER:
	    lua_pushinteger(L, (int) tv->vval.v_number);
	    break;
#ifdef FEAT_FLOAT
	case VAR_FLOAT:
	    lua_pushnumber(L, (lua_Number) tv->vval.v_float);
	    break;
#endif
	case VAR_LIST:
	    luaV_pushlist(L, tv->vval.v_list);
	    break;
	case VAR_DICT:
	    luaV_pushdict(L, tv->vval.v_dict);
	    break;
	case VAR_SPECIAL:
	    if (tv->vval.v_number <= VVAL_TRUE)
		lua_pushinteger(L, (int) tv->vval.v_number);
	    else
		lua_pushnil(L);
	    break;
	case VAR_FUNC:
	    luaV_pushfuncref(L, tv->vval.v_string);
	    break;
	case VAR_BLOB:
	    luaV_pushblob(L, tv->vval.v_blob);
	    break;
	default:
	    lua_pushnil(L);
    }
}

/*
 * Converts lua value at 'pos' to typval 'tv'.
 * Returns OK or FAIL.
 */
    static int
luaV_totypval(lua_State *L, int pos, typval_T *tv)
{
    int status = OK;

    switch (lua_type(L, pos))
    {
	case LUA_TBOOLEAN:
	    tv->v_type = VAR_SPECIAL;
	    tv->vval.v_number = (varnumber_T) lua_toboolean(L, pos);
	    break;
	case LUA_TNIL:
	    tv->v_type = VAR_SPECIAL;
	    tv->vval.v_number = VVAL_NULL;
	    break;
	case LUA_TSTRING:
	    tv->v_type = VAR_STRING;
	    tv->vval.v_string = vim_strsave((char_u *) lua_tostring(L, pos));
	    break;
	case LUA_TNUMBER:
#ifdef FEAT_FLOAT
	    tv->v_type = VAR_FLOAT;
	    tv->vval.v_float = (float_T) lua_tonumber(L, pos);
#else
	    tv->v_type = VAR_NUMBER;
	    tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
#endif
	    break;
	case LUA_TUSERDATA:
	{
	    void *p = lua_touserdata(L, pos);

	    if (lua_getmetatable(L, pos)) // has metatable?
	    {
		// check list
		luaV_getfield(L, LUAVIM_LIST);
		if (lua_rawequal(L, -1, -2))
		{
		    tv->v_type = VAR_LIST;
		    tv->vval.v_list = *((luaV_List *) p);
		    ++tv->vval.v_list->lv_refcount;
		    lua_pop(L, 2); // MTs
		    break;
		}
		// check dict
		luaV_getfield(L, LUAVIM_DICT);
		if (lua_rawequal(L, -1, -3))
		{
		    tv->v_type = VAR_DICT;
		    tv->vval.v_dict = *((luaV_Dict *) p);
		    ++tv->vval.v_dict->dv_refcount;
		    lua_pop(L, 3); // MTs
		    break;
		}
		// check blob
		luaV_getfield(L, LUAVIM_BLOB);
		if (lua_rawequal(L, -1, -4))
		{
		    tv->v_type = VAR_BLOB;
		    tv->vval.v_blob = *((luaV_Blob *) p);
		    ++tv->vval.v_blob->bv_refcount;
		    lua_pop(L, 4); // MTs
		    break;
		}
		// check funcref
		luaV_getfield(L, LUAVIM_FUNCREF);
		if (lua_rawequal(L, -1, -5))
		{
		    luaV_Funcref *f = (luaV_Funcref *) p;
		    func_ref(f->name);
		    tv->v_type = VAR_FUNC;
		    tv->vval.v_string = vim_strsave(f->name);
		    lua_pop(L, 5); // MTs
		    break;
		}
		lua_pop(L, 4); // MTs
	    }
	}
	// FALLTHROUGH
	default:
	    tv->v_type = VAR_NUMBER;
	    tv->vval.v_number = 0;
	    status = FAIL;
    }
    return status;
}

/* similar to luaL_addlstring, but replaces \0 with \n if toline and
 * \n with \0 otherwise */
    static void
luaV_addlstring(luaL_Buffer *b, const char *s, size_t l, int toline)
{
    while (l--)
    {
	if (*s == '\0' && toline)
	    luaL_addchar(b, '\n');
	else if (*s == '\n' && !toline)
	    luaL_addchar(b, '\0');
	else
	    luaL_addchar(b, *s);
	s++;
    }
}

    static void
luaV_pushline(lua_State *L, buf_T *buf, linenr_T n)
{
    const char *s = (const char *) ml_get_buf(buf, n, FALSE);
    luaL_Buffer b;
    luaL_buffinit(L, &b);
    luaV_addlstring(&b, s, strlen(s), 0);
    luaL_pushresult(&b);
}

    static char_u *
luaV_toline(lua_State *L, int pos)
{
    size_t l;
    const char *s = lua_tolstring(L, pos, &l);

    luaL_Buffer b;
    luaL_buffinit(L, &b);
    luaV_addlstring(&b, s, l, 1);
    luaL_pushresult(&b);
    return (char_u *) lua_tostring(L, -1);
}

/* pops a string s from the top of the stack and calls mf(t) for pieces t of
 * s separated by newlines */
    static void
luaV_msgfunc(lua_State *L, msgfunc_T mf)
{
    luaL_Buffer b;
    size_t l;
    const char *p, *s = lua_tolstring(L, -1, &l);
    luaL_buffinit(L, &b);
    luaV_addlstring(&b, s, l, 0);
    luaL_pushresult(&b);
    /* break string */
    p = s = lua_tolstring(L, -1, &l);
    while (l--)
    {
	if (*p++ == '\0') /* break? */
	{
	    mf((char_u *) s);
	    s = p;
	}
    }
    mf((char_u *) s);
    lua_pop(L, 2); /* original and modified strings */
}

#define luaV_newtype(typ,tname,luatyp,luatname) \
	static luatyp * \
    luaV_new##tname(lua_State *L, typ *obj) \
    { \
	luatyp *o = (luatyp *) lua_newuserdata(L, sizeof(luatyp)); \
	*o = obj; \
	luaV_setudata(L, obj); /* cache[obj] = udata */ \
	luaV_getfield(L, luatname); \
	lua_setmetatable(L, -2); \
	return o; \
    }

#define luaV_pushtype(typ,tname,luatyp) \
	static luatyp * \
    luaV_push##tname(lua_State *L, typ *obj) \
    { \
	luatyp *o = NULL; \
	if (obj == NULL) \
	    lua_pushnil(L); \
	else { \
	    luaV_getudata(L, obj); \
	    if (lua_isnil(L, -1)) /* not interned? */ \
	    { \
		lua_pop(L, 1); \
		o = luaV_new##tname(L, obj); \
	    } \
	    else \
		o = (luatyp *) lua_touserdata(L, -1); \
	} \
	return o; \
    }

#define luaV_type_tostring(tname,luatname) \
	static int \
    luaV_##tname##_tostring(lua_State *L) \
    { \
	lua_pushfstring(L, "%s: %p", luatname, lua_touserdata(L, 1)); \
	return 1; \
    }

/* =======   List type   ======= */

    static luaV_List *
luaV_newlist(lua_State *L, list_T *lis)
{
    luaV_List *l = (luaV_List *) lua_newuserdata(L, sizeof(luaV_List));
    *l = lis;
    lis->lv_refcount++; /* reference in Lua */
    luaV_setudata(L, lis); /* cache[lis] = udata */
    luaV_getfield(L, LUAVIM_LIST);
    lua_setmetatable(L, -2);
    return l;
}

luaV_pushtype(list_T, list, luaV_List)
luaV_type_tostring(list, LUAVIM_LIST)

    static int
luaV_list_len(lua_State *L)
{
    list_T *l = luaV_unbox(L, luaV_List, 1);
    lua_pushinteger(L, (int) list_len(l));
    return 1;
}

    static int
luaV_list_iter(lua_State *L)
{
    listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(2));
    if (li == NULL) return 0;
    luaV_pushtypval(L, &li->li_tv);
    lua_pushlightuserdata(L, (void *) li->li_next);
    lua_replace(L, lua_upvalueindex(2));
    return 1;
}

    static int
luaV_list_call(lua_State *L)
{
    list_T *l = luaV_unbox(L, luaV_List, 1);
    lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */
    lua_pushlightuserdata(L, (void *) l->lv_first);
    lua_pushcclosure(L, luaV_list_iter, 2);
    return 1;
}

    static int
luaV_list_index(lua_State *L)
{
    list_T *l = luaV_unbox(L, luaV_List, 1);
    if (lua_isnumber(L, 2)) /* list item? */
    {
	listitem_T *li = list_find(l, (long) luaL_checkinteger(L, 2));
	if (li == NULL)
	    lua_pushnil(L);
	else
	    luaV_pushtypval(L, &li->li_tv);
    }
    else if (lua_isstring(L, 2)) /* method? */
    {
	const char *s = lua_tostring(L, 2);
	if (strncmp(s, "add", 3) == 0
		|| strncmp(s, "insert", 6) == 0)
	{
	    lua_getmetatable(L, 1);
	    lua_getfield(L, -1, s);
	}
	else
	    lua_pushnil(L);
    }
    else
	lua_pushnil(L);
    return 1;
}

    static int
luaV_list_newindex(lua_State *L)
{
    list_T *l = luaV_unbox(L, luaV_List, 1);
    long n = (long) luaL_checkinteger(L, 2);
    listitem_T *li;
    if (l->lv_lock)
	luaL_error(L, "list is locked");
    li = list_find(l, n);
    if (li == NULL) return 0;
    if (lua_isnil(L, 3)) /* remove? */
    {
	vimlist_remove(l, li, li);
	clear_tv(&li->li_tv);
	vim_free(li);
    }
    else
    {
	typval_T v;
	luaV_checktypval(L, 3, &v, "setting list item");
	clear_tv(&li->li_tv);
	copy_tv(&v, &li->li_tv);
	clear_tv(&v);
    }
    return 0;
}

    static int
luaV_list_add(lua_State *L)
{
    luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST);
    list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis);
    typval_T v;
    if (l->lv_lock)
	luaL_error(L, "list is locked");
    lua_settop(L, 2);
    luaV_checktypval(L, 2, &v, "adding list item");
    if (list_append_tv(l, &v) == FAIL)
	luaL_error(L, "failed to add item to list");
    clear_tv(&v);
    lua_settop(L, 1);
    return 1;
}

    static int
luaV_list_insert(lua_State *L)
{
    luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST);
    list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis);
    long pos = (long) luaL_optinteger(L, 3, 0);
    listitem_T *li = NULL;
    typval_T v;
    if (l->lv_lock)
	luaL_error(L, "list is locked");
    if (pos < l->lv_len)
    {
	li = list_find(l, pos);
	if (li == NULL)
	    luaL_error(L, "invalid position");
    }
    lua_settop(L, 2);
    luaV_checktypval(L, 2, &v, "inserting list item");
    if (list_insert_tv(l, &v, li) == FAIL)
	luaL_error(L, "failed to add item to list");
    clear_tv(&v);
    lua_settop(L, 1);
    return 1;
}

static const luaL_Reg luaV_List_mt[] = {
    {"__tostring", luaV_list_tostring},
    {"__len", luaV_list_len},
    {"__call", luaV_list_call},
    {"__index", luaV_list_index},
    {"__newindex", luaV_list_newindex},
    {"add", luaV_list_add},
    {"insert", luaV_list_insert},
    {NULL, NULL}
};


/* =======   Dict type   ======= */

    static luaV_Dict *
luaV_newdict(lua_State *L, dict_T *dic)
{
    luaV_Dict *d = (luaV_Dict *) lua_newuserdata(L, sizeof(luaV_Dict));
    *d = dic;
    dic->dv_refcount++; /* reference in Lua */
    luaV_setudata(L, dic); /* cache[dic] = udata */
    luaV_getfield(L, LUAVIM_DICT);
    lua_setmetatable(L, -2);
    return d;
}

luaV_pushtype(dict_T, dict, luaV_Dict)
luaV_type_tostring(dict, LUAVIM_DICT)

    static int
luaV_dict_len(lua_State *L)
{
    dict_T *d = luaV_unbox(L, luaV_Dict, 1);
    lua_pushinteger(L, (int) dict_len(d));
    return 1;
}

    static int
luaV_dict_iter(lua_State *L UNUSED)
{
#ifdef FEAT_EVAL
    hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(2));
    int n = lua_tointeger(L, lua_upvalueindex(3));
    dictitem_T *di;
    if (n <= 0) return 0;
    while (HASHITEM_EMPTY(hi)) hi++;
    di = dict_lookup(hi);
    lua_pushstring(L, (char *) hi->hi_key);
    luaV_pushtypval(L, &di->di_tv);
    lua_pushlightuserdata(L, (void *) (hi + 1));
    lua_replace(L, lua_upvalueindex(2));
    lua_pushinteger(L, n - 1);
    lua_replace(L, lua_upvalueindex(3));
    return 2;
#else
    return 0;
#endif
}

    static int
luaV_dict_call(lua_State *L)
{
    dict_T *d = luaV_unbox(L, luaV_Dict, 1);
    hashtab_T *ht = &d->dv_hashtab;
    lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */
    lua_pushlightuserdata(L, (void *) ht->ht_array);
    lua_pushinteger(L, ht->ht_used); /* # remaining items */
    lua_pushcclosure(L, luaV_dict_iter, 3);
    return 1;
}

    static int
luaV_dict_index(lua_State *L)
{
    dict_T *d = luaV_unbox(L, luaV_Dict, 1);
    char_u *key = (char_u *) luaL_checkstring(L, 2);
    dictitem_T *di = dict_find(d, key, -1);

    if (di == NULL)
	lua_pushnil(L);
    else
    {
	luaV_pushtypval(L, &di->di_tv);
	if (di->di_tv.v_type == VAR_FUNC) /* funcref? */
	{
	    luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, -1);
	    f->self = d; /* keep "self" reference */
	    d->dv_refcount++;
	}
    }
    return 1;
}

    static int
luaV_dict_newindex(lua_State *L)
{
    dict_T *d = luaV_unbox(L, luaV_Dict, 1);
    char_u *key = (char_u *) luaL_checkstring(L, 2);
    dictitem_T *di;
    typval_T v;

    if (d->dv_lock)
	luaL_error(L, "dict is locked");
    if (key == NULL)
	return 0;
    if (*key == NUL)
	luaL_error(L, "empty key");
    if (!lua_isnil(L, 3)) /* read value? */
    {
	luaV_checktypval(L, 3, &v, "setting dict item");
	if (d->dv_scope == VAR_DEF_SCOPE && v.v_type == VAR_FUNC)
	    luaL_error(L, "cannot assign funcref to builtin scope");
    }
    di = dict_find(d, key, -1);
    if (di == NULL) /* non-existing key? */
    {
	if (lua_isnil(L, 3))
	    return 0;
	di = dictitem_alloc(key);
	if (di == NULL)
	    return 0;
	if (dict_add(d, di) == FAIL)
	{
	    vim_free(di);
	    return 0;
	}
    }
    else
	clear_tv(&di->di_tv);
    if (lua_isnil(L, 3)) /* remove? */
    {
	hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key);
	hash_remove(&d->dv_hashtab, hi);
	dictitem_free(di);
    }
    else
    {
	copy_tv(&v, &di->di_tv);
	clear_tv(&v);
    }
    return 0;
}

static const luaL_Reg luaV_Dict_mt[] = {
    {"__tostring", luaV_dict_tostring},
    {"__len", luaV_dict_len},
    {"__call", luaV_dict_call},
    {"__index", luaV_dict_index},
    {"__newindex", luaV_dict_newindex},
    {NULL, NULL}
};


/* =======   Blob type   ======= */

    static luaV_Blob *
luaV_newblob(lua_State *L, blob_T *blo)
{
    luaV_Blob *b = (luaV_Blob *) lua_newuserdata(L, sizeof(luaV_Blob));
    *b = blo;
    blo->bv_refcount++; /* reference in Lua */
    luaV_setudata(L, blo); /* cache[blo] = udata */
    luaV_getfield(L, LUAVIM_BLOB);
    lua_setmetatable(L, -2);
    return b;
}

luaV_pushtype(blob_T, blob, luaV_Blob)
luaV_type_tostring(blob, LUAVIM_BLOB)

    static int
luaV_blob_gc(lua_State *L)
{
    blob_T *b = luaV_unbox(L, luaV_Blob, 1);
    blob_unref(b);
    return 0;
}

    static int
luaV_blob_len(lua_State *L)
{
    blob_T *b = luaV_unbox(L, luaV_Blob, 1);
    lua_pushinteger(L, (int) blob_len(b));
    return 1;
}

    static int
luaV_blob_index(lua_State *L)
{
    blob_T *b = luaV_unbox(L, luaV_Blob, 1);
    if (lua_isnumber(L, 2))
    {
	int idx = luaL_checkinteger(L, 2);
	if (idx < blob_len(b))
	    lua_pushnumber(L, (lua_Number) blob_get(b, idx));
	else
	    lua_pushnil(L);
    }
    else if (lua_isstring(L, 2))
    {
	const char *s = lua_tostring(L, 2);
	if (strncmp(s, "add", 3) == 0)
	{
	    lua_getmetatable(L, 1);
	    lua_getfield(L, -1, s);
	}
	else
	    lua_pushnil(L);
    }
    else
	lua_pushnil(L);
    return 1;
}

    static int
luaV_blob_newindex(lua_State *L)
{
    blob_T *b = luaV_unbox(L, luaV_Blob, 1);
    if (b->bv_lock)
	luaL_error(L, "blob is locked");
    if (lua_isnumber(L, 2))
    {
	long len = blob_len(b);
	int idx = luaL_checkinteger(L, 2);
	int val = luaL_checkinteger(L, 3);
	if (idx < len || (idx == len && ga_grow(&b->bv_ga, 1) == OK))
	{
	    blob_set(b, idx, (char_u) val);
	    if (idx == len)
		++b->bv_ga.ga_len;
	}
	else
	    luaL_error(L, "index out of range");
    }
    return 0;
}

    static int
luaV_blob_add(lua_State *L)
{
    luaV_Blob *blo = luaV_checkudata(L, 1, LUAVIM_BLOB);
    blob_T *b = (blob_T *) luaV_checkcache(L, (void *) *blo);
    if (b->bv_lock)
	luaL_error(L, "blob is locked");
    lua_settop(L, 2);
    if (!lua_isstring(L, 2))
	luaL_error(L, "string expected, got %s", luaL_typename(L, 2));
    else
    {
	size_t i, l = 0;
	const char *s = lua_tolstring(L, 2, &l);

	if (ga_grow(&b->bv_ga, l) == OK)
	    for (i = 0; i < l; ++i)
		ga_append(&b->bv_ga, s[i]);
    }
    lua_settop(L, 1);
    return 1;
}

static const luaL_Reg luaV_Blob_mt[] = {
    {"__tostring", luaV_blob_tostring},
    {"__gc", luaV_blob_gc},
    {"__len", luaV_blob_len},
    {"__index", luaV_blob_index},
    {"__newindex", luaV_blob_newindex},
    {"add", luaV_blob_add},
    {NULL, NULL}
};


/* =======   Funcref type   ======= */

    static luaV_Funcref *
luaV_newfuncref(lua_State *L, char_u *name)
{
    luaV_Funcref *f = (luaV_Funcref *)lua_newuserdata(L, sizeof(luaV_Funcref));

    if (name != NULL)
    {
	func_ref(name);
	f->name = vim_strsave(name);
    }
    f->self = NULL;
    luaV_getfield(L, LUAVIM_FUNCREF);
    lua_setmetatable(L, -2);
    return f;
}

    static luaV_Funcref *
luaV_pushfuncref(lua_State *L, char_u *name)
{
    return luaV_newfuncref(L, name);
}


luaV_type_tostring(funcref, LUAVIM_FUNCREF)

    static int
luaV_funcref_gc(lua_State *L)
{
    luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);

    func_unref(f->name);
    vim_free(f->name);
    // NOTE: Don't call "dict_unref(f->self)", because the dict of "f->self"
    // will be (or has been already) freed by Vim's garbage collection.
    return 0;
}

/* equivalent to string(funcref) */
    static int
luaV_funcref_len(lua_State *L)
{
    luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);

    lua_pushstring(L, (const char *) f->name);
    return 1;
}

    static int
luaV_funcref_call(lua_State *L)
{
    luaV_Funcref *f = (luaV_Funcref *) lua_touserdata(L, 1);
    int i, n = lua_gettop(L) - 1; // #args
    int status = FAIL;
    typval_T args;
    typval_T rettv;

    args.v_type = VAR_LIST;
    args.vval.v_list = list_alloc();
    rettv.v_type = VAR_UNKNOWN; // as in clear_tv
    if (args.vval.v_list != NULL)
    {
	typval_T v;

	for (i = 0; i < n; i++)
	{
	    luaV_checktypval(L, i + 2, &v, "calling funcref");
	    list_append_tv(args.vval.v_list, &v);
	    clear_tv(&v);
	}
	status = func_call(f->name, &args, NULL, f->self, &rettv);
	if (status == OK)
	    luaV_pushtypval(L, &rettv);
	clear_tv(&args);
	clear_tv(&rettv);
    }
    if (status != OK)
	luaL_error(L, "cannot call funcref");
    return 1;
}

static const luaL_Reg luaV_Funcref_mt[] = {
    {"__tostring", luaV_funcref_tostring},
    {"__gc", luaV_funcref_gc},
    {"__len", luaV_funcref_len},
    {"__call", luaV_funcref_call},
    {NULL, NULL}
};


/* =======   Buffer type   ======= */

luaV_newtype(buf_T, buffer, luaV_Buffer, LUAVIM_BUFFER)
luaV_pushtype(buf_T, buffer, luaV_Buffer)
luaV_type_tostring(buffer, LUAVIM_BUFFER)

    static int
luaV_buffer_len(lua_State *L)
{
    buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1);
    lua_pushinteger(L, b->b_ml.ml_line_count);
    return 1;
}

    static int
luaV_buffer_call(lua_State *L)
{
    buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1);
    lua_settop(L, 1);
    set_curbuf(b, DOBUF_SPLIT);
    return 1;
}

    static int
luaV_buffer_index(lua_State *L)
{
    buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1);
    linenr_T n = (linenr_T) lua_tointeger(L, 2);
    if (n > 0 && n <= b->b_ml.ml_line_count)
	luaV_pushline(L, b, n);
    else if (lua_isstring(L, 2))
    {
	const char *s = lua_tostring(L, 2);
	if (strncmp(s, "name", 4) == 0)
	    lua_pushstring(L, (b->b_sfname == NULL)
					? "" : (char *) b->b_sfname);
	else if (strncmp(s, "fname", 5) == 0)
	    lua_pushstring(L, (b->b_ffname == NULL)
					? "" : (char *) b->b_ffname);
	else if (strncmp(s, "number", 6) == 0)
	    lua_pushinteger(L, b->b_fnum);
	/* methods */
	else if (strncmp(s,   "insert", 6) == 0
		|| strncmp(s, "next", 4) == 0
		|| strncmp(s, "previous", 8) == 0
		|| strncmp(s, "isvalid", 7) == 0)
	{
	    lua_getmetatable(L, 1);
	    lua_getfield(L, -1, s);
	}
	else
	    lua_pushnil(L);
    }
    else
	lua_pushnil(L);
    return 1;
}

    static int
luaV_buffer_newindex(lua_State *L)
{
    buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1);
    linenr_T n = (linenr_T) luaL_checkinteger(L, 2);
#ifdef HAVE_SANDBOX
    luaV_checksandbox(L);
#endif
    if (n < 1 || n > b->b_ml.ml_line_count)
	luaL_error(L, "invalid line number");
    if (lua_isnil(L, 3)) /* delete line */
    {
	buf_T *buf = curbuf;
	curbuf = b;
	if (u_savedel(n, 1L) == FAIL)
	{
	    curbuf = buf;
	    luaL_error(L, "cannot save undo information");
	}
	else if (ml_delete(n, FALSE) == FAIL)
	{
	    curbuf = buf;
	    luaL_error(L, "cannot delete line");
	}
	else
	{
	    deleted_lines_mark(n, 1L);
	    if (b == curwin->w_buffer) /* fix cursor in current window? */
	    {
		if (curwin->w_cursor.lnum >= n)
		{
		    if (curwin->w_cursor.lnum > n)
		    {
			curwin->w_cursor.lnum -= 1;
			check_cursor_col();
		    }
		    else check_cursor();
		    changed_cline_bef_curs();
		}
		invalidate_botline();
	    }
	}
	curbuf = buf;
    }
    else if (lua_isstring(L, 3)) /* update line */
    {
	buf_T *buf = curbuf;
	curbuf = b;
	if (u_savesub(n) == FAIL)
	{
	    curbuf = buf;
	    luaL_error(L, "cannot save undo information");
	}
	else if (ml_replace(n, luaV_toline(L, 3), TRUE) == FAIL)
	{
	    curbuf = buf;
	    luaL_error(L, "cannot replace line");
	}
	else changed_bytes(n, 0);
	curbuf = buf;
	if (b == curwin->w_buffer)
	    check_cursor_col();
    }
    else
	luaL_error(L, "wrong argument to change line");
    return 0;
}

    static int
luaV_buffer_insert(lua_State *L)
{
    luaV_Buffer *lb = luaV_checkudata(L, 1, LUAVIM_BUFFER);
    buf_T *b = (buf_T *) luaV_checkcache(L, (void *) *lb);
    linenr_T last = b->b_ml.ml_line_count;
    linenr_T n = (linenr_T) luaL_optinteger(L, 3, last);
    buf_T *buf;
    luaL_checktype(L, 2, LUA_TSTRING);
#ifdef HAVE_SANDBOX
    luaV_checksandbox(L);
#endif
    /* fix insertion line */
    if (n < 0) n = 0;
    if (n > last) n = last;
    /* insert */
    buf = curbuf;
    curbuf = b;
    if (u_save(n, n + 1) == FAIL)
    {
	curbuf = buf;
	luaL_error(L, "cannot save undo information");
    }
    else if (ml_append(n, luaV_toline(L, 2), 0, FALSE) == FAIL)
    {
	curbuf = buf;
	luaL_error(L, "cannot insert line");
    }
    else
	appended_lines_mark(n, 1L);
    curbuf = buf;
    update_screen(VALID);
    return 0;
}

    static int
luaV_buffer_next(lua_State *L)
{
    luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER);
    buf_T *buf = (buf_T *) luaV_checkcache(L, (void *) *b);
    luaV_pushbuffer(L, buf->b_next);
    return 1;
}

    static int
luaV_buffer_previous(lua_State *L)
{
    luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER);
    buf_T *buf = (buf_T *) luaV_checkcache(L, (void *) *b);
    luaV_pushbuffer(L, buf->b_prev);
    return 1;
}

    static int
luaV_buffer_isvalid(lua_State *L)
{
    luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER);
    luaV_getudata(L, *b);
    lua_pushboolean(L, !lua_isnil(L, -1));
    return 1;
}

static const luaL_Reg luaV_Buffer_mt[] = {
    {"__tostring", luaV_buffer_tostring},
    {"__len", luaV_buffer_len},
    {"__call", luaV_buffer_call},
    {"__index", luaV_buffer_index},
    {"__newindex", luaV_buffer_newindex},
    {"insert", luaV_buffer_insert},
    {"next", luaV_buffer_next},
    {"previous", luaV_buffer_previous},
    {"isvalid", luaV_buffer_isvalid},
    {NULL, NULL}
};


/* =======   Window type   ======= */

luaV_newtype(win_T, window, luaV_Window, LUAVIM_WINDOW)
luaV_pushtype(win_T, window, luaV_Window)
luaV_type_tostring(window, LUAVIM_WINDOW)

    static int
luaV_window_call(lua_State *L)
{
    win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1);
    lua_settop(L, 1);
    win_goto(w);
    return 1;
}

    static int
luaV_window_index(lua_State *L)
{
    win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1);
    const char *s = luaL_checkstring(L, 2);
    if (strncmp(s, "buffer", 6) == 0)
	luaV_pushbuffer(L, w->w_buffer);
    else if (strncmp(s, "line", 4) == 0)
	lua_pushinteger(L, w->w_cursor.lnum);
    else if (strncmp(s, "col", 3) == 0)
	lua_pushinteger(L, w->w_cursor.col + 1);
    else if (strncmp(s, "width", 5) == 0)
	lua_pushinteger(L, w->w_width);
    else if (strncmp(s, "height", 6) == 0)
	lua_pushinteger(L, w->w_height);
    /* methods */
    else if (strncmp(s,   "next", 4) == 0
	    || strncmp(s, "previous", 8) == 0
	    || strncmp(s, "isvalid", 7) == 0)
    {
	lua_getmetatable(L, 1);
	lua_getfield(L, -1, s);
    }
    else
	lua_pushnil(L);
    return 1;
}

    static int
luaV_window_newindex(lua_State *L)
{
    win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1);
    const char *s = luaL_checkstring(L, 2);
    int v = luaL_checkinteger(L, 3);
    if (strncmp(s, "line", 4) == 0)
    {
#ifdef HAVE_SANDBOX
	luaV_checksandbox(L);
#endif
	if (v < 1 || v > w->w_buffer->b_ml.ml_line_count)
	    luaL_error(L, "line out of range");
	w->w_cursor.lnum = v;
	update_screen(VALID);
    }
    else if (strncmp(s, "col", 3) == 0)
    {
#ifdef HAVE_SANDBOX
	luaV_checksandbox(L);
#endif
	w->w_cursor.col = v - 1;
	w->w_set_curswant = TRUE;
	update_screen(VALID);
    }
    else if (strncmp(s, "width", 5) == 0)
    {
	win_T *win = curwin;
#ifdef FEAT_GUI
	need_mouse_correct = TRUE;
#endif
	curwin = w;
	win_setwidth(v);
	curwin = win;
    }
    else if (strncmp(s, "height", 6) == 0)
    {
	win_T *win = curwin;
#ifdef FEAT_GUI
	need_mouse_correct = TRUE;
#endif
	curwin = w;
	win_setheight(v);
	curwin = win;
    }
    else
	luaL_error(L, "invalid window property: `%s'", s);
    return 0;
}

    static int
luaV_window_next(lua_State *L)
{
    luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW);
    win_T *win = (win_T *) luaV_checkcache(L, (void *) *w);
    luaV_pushwindow(L, win->w_next);
    return 1;
}

    static int
luaV_window_previous(lua_State *L)
{
    luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW);
    win_T *win = (win_T *) luaV_checkcache(L, (void *) *w);
    luaV_pushwindow(L, win->w_prev);
    return 1;
}

    static int
luaV_window_isvalid(lua_State *L)
{
    luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW);
    luaV_getudata(L, *w);
    lua_pushboolean(L, !lua_isnil(L, -1));
    return 1;
}

static const luaL_Reg luaV_Window_mt[] = {
    {"__tostring", luaV_window_tostring},
    {"__call", luaV_window_call},
    {"__index", luaV_window_index},
    {"__newindex", luaV_window_newindex},
    {"next", luaV_window_next},
    {"previous", luaV_window_previous},
    {"isvalid", luaV_window_isvalid},
    {NULL, NULL}
};


/* =======   Vim module   ======= */

    static int
luaV_print(lua_State *L)
{
    int i, n = lua_gettop(L); /* nargs */
    const char *s;
    size_t l;
    luaL_Buffer b;
    luaL_buffinit(L, &b);
    lua_getglobal(L, "tostring");
    for (i = 1; i <= n; i++)
    {
	lua_pushvalue(L, -1); /* tostring */
	lua_pushvalue(L, i); /* arg */
	lua_call(L, 1, 1);
	s = lua_tolstring(L, -1, &l);
	if (s == NULL)
	    return luaL_error(L, "cannot convert to string");
	if (i > 1) luaL_addchar(&b, ' '); /* use space instead of tab */
	luaV_addlstring(&b, s, l, 0);
	lua_pop(L, 1);
    }
    luaL_pushresult(&b);
    luaV_msg(L);
    return 0;
}

    static int
luaV_debug(lua_State *L)
{
    lua_settop(L, 0);
    lua_getglobal(L, "vim");
    lua_getfield(L, -1, "eval");
    lua_remove(L, -2); /* vim.eval at position 1 */
    for (;;)
    {
	const char *input;
	size_t l;
	lua_pushvalue(L, 1); /* vim.eval */
	lua_pushliteral(L, "input('lua_debug> ')");
	lua_call(L, 1, 1); /* return string */
	input = lua_tolstring(L, -1, &l);
	if (l == 0 || strcmp(input, "cont") == 0)
	    return 0;
	msg_putchar('\n'); /* avoid outputting on input line */
	if (luaL_loadbuffer(L, input, l, "=(debug command)")
		|| lua_pcall(L, 0, 0, 0))
	    luaV_emsg(L);
	lua_settop(L, 1); /* remove eventual returns, but keep vim.eval */
    }
}

    static int
luaV_command(lua_State *L)
{
    do_cmdline_cmd((char_u *) luaL_checkstring(L, 1));
    update_screen(VALID);
    return 0;
}

    static int
luaV_eval(lua_State *L)
{
    typval_T *tv = eval_expr((char_u *) luaL_checkstring(L, 1), NULL);
    if (tv == NULL) luaL_error(L, "invalid expression");
    luaV_pushtypval(L, tv);
    free_tv(tv);
    return 1;
}

    static int
luaV_beep(lua_State *L UNUSED)
{
    vim_beep(BO_LANG);
    return 0;
}

    static int
luaV_line(lua_State *L)
{
    luaV_pushline(L, curbuf, curwin->w_cursor.lnum);
    return 1;
}

    static int
luaV_list(lua_State *L)
{
    list_T *l;
    int initarg = !lua_isnoneornil(L, 1);

    if (initarg && lua_type(L, 1) != LUA_TTABLE)
	luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
    l = list_alloc();
    if (l == NULL)
	lua_pushnil(L);
    else
    {
	luaV_newlist(L, l);
	if (initarg) /* traverse table to init list */
	{
	    int notnil, i = 0;
	    typval_T v;
	    do
	    {
		lua_rawgeti(L, 1, ++i);
		notnil = !lua_isnil(L, -1);
		if (notnil)
		{
		    luaV_checktypval(L, -1, &v, "vim.list");
		    list_append_tv(l, &v);
		    clear_tv(&v);
		}
		lua_pop(L, 1); /* value */
	    } while (notnil);
	}
    }
    return 1;
}

    static int
luaV_dict(lua_State *L)
{
    dict_T *d;
    int initarg = !lua_isnoneornil(L, 1);

    if (initarg && lua_type(L, 1) != LUA_TTABLE)
	luaL_error(L, "table expected, got %s", luaL_typename(L, 1));
    d = dict_alloc();
    if (d == NULL)
	lua_pushnil(L);
    else
    {
	luaV_newdict(L, d);
	if (initarg) /* traverse table to init dict */
	{
	    lua_pushnil(L);
	    while (lua_next(L, 1))
	    {
		char_u *key;
		dictitem_T *di;
		typval_T v;

		lua_pushvalue(L, -2); /* dup key in case it's a number */
		key = (char_u *) lua_tostring(L, -1);
		if (key == NULL)
		{
		    lua_pushnil(L);
		    return 1;
		}
		if (*key == NUL)
		    luaL_error(L, "table has empty key");
		luaV_checktypval(L, -2, &v, "vim.dict"); /* value */
		di = dictitem_alloc(key);
		if (di == NULL || dict_add(d, di) == FAIL)
		{
		    vim_free(di);
		    lua_pushnil(L);
		    return 1;
		}
		copy_tv(&v, &di->di_tv);
		clear_tv(&v);
		lua_pop(L, 2); /* key copy and value */
	    }
	}
    }
    return 1;
}

    static int
luaV_blob(lua_State *L)
{
    blob_T *b;
    int initarg = !lua_isnoneornil(L, 1);

    if (initarg && !lua_isstring(L, 1))
	luaL_error(L, "string expected, got %s", luaL_typename(L, 1));
    b = blob_alloc();
    if (b == NULL)
	lua_pushnil(L);
    else
    {
	luaV_newblob(L, b);
	if (initarg)
	{
	    size_t i, l = 0;
	    const char *s = lua_tolstring(L, 1, &l);

	    if (ga_grow(&b->bv_ga, l) == OK)
		for (i = 0; i < l; ++i)
		    ga_append(&b->bv_ga, s[i]);
	}
    }
    return 1;
}

    static int
luaV_funcref(lua_State *L)
{
    const char *name = luaL_checkstring(L, 1);
    /* note: not checking if function exists (needs function_exists) */
    if (name == NULL || *name == NUL || VIM_ISDIGIT(*name))
	luaL_error(L, "invalid function name: %s", name);
    luaV_newfuncref(L, (char_u *) name);
    return 1;
}

    static int
luaV_buffer(lua_State *L)
{
    buf_T *buf;
    if (lua_isstring(L, 1)) /* get by number or name? */
    {
	if (lua_isnumber(L, 1)) /* by number? */
	{
	    int n = lua_tointeger(L, 1);
	    FOR_ALL_BUFFERS(buf)
		if (buf->b_fnum == n) break;
	}
	else // by name
	{
	    size_t l;
	    const char *s = lua_tolstring(L, 1, &l);
	    FOR_ALL_BUFFERS(buf)
	    {
		if (buf->b_ffname == NULL || buf->b_sfname == NULL)
		{
		    if (l == 0) break;
		}
		else if (strncmp(s, (char *)buf->b_ffname, l) == 0
			|| strncmp(s, (char *)buf->b_sfname, l) == 0)
		    break;
	    }
	}
    }
    else
	buf = (lua_toboolean(L, 1)) ? firstbuf : curbuf; /* first buffer? */
    luaV_pushbuffer(L, buf);
    return 1;
}

    static int
luaV_window(lua_State *L)
{
    win_T *win;
    if (lua_isnumber(L, 1)) /* get by number? */
    {
	int n = lua_tointeger(L, 1);
	for (win = firstwin; win != NULL; win = win->w_next, n--)
	    if (n == 1) break;
    }
    else
	win = (lua_toboolean(L, 1)) ? firstwin : curwin; /* first window? */
    luaV_pushwindow(L, win);
    return 1;
}

    static int
luaV_open(lua_State *L)
{
    char_u *s = NULL;
#ifdef HAVE_SANDBOX
    luaV_checksandbox(L);
#endif
    if (lua_isstring(L, 1)) s = (char_u *) lua_tostring(L, 1);
    luaV_pushbuffer(L, buflist_new(s, NULL, 1L, BLN_LISTED));
    return 1;
}

    static int
luaV_type(lua_State *L)
{
    luaL_checkany(L, 1);
    if (lua_type(L, 1) == LUA_TUSERDATA) /* check vim udata? */
    {
	lua_settop(L, 1);
	if (lua_getmetatable(L, 1))
	{
	    luaV_getfield(L, LUAVIM_LIST);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "list");
		return 1;
	    }
	    luaV_getfield(L, LUAVIM_DICT);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "dict");
		return 1;
	    }
	    luaV_getfield(L, LUAVIM_BLOB);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "blob");
		return 1;
	    }
	    luaV_getfield(L, LUAVIM_FUNCREF);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "funcref");
		return 1;
	    }
	    luaV_getfield(L, LUAVIM_BUFFER);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "buffer");
		return 1;
	    }
	    luaV_getfield(L, LUAVIM_WINDOW);
	    if (lua_rawequal(L, -1, 2))
	    {
		lua_pushstring(L, "window");
		return 1;
	    }
	}
    }
    lua_pushstring(L, luaL_typename(L, 1)); /* fallback */
    return 1;
}

static const luaL_Reg luaV_module[] = {
    {"command", luaV_command},
    {"eval", luaV_eval},
    {"beep", luaV_beep},
    {"line", luaV_line},
    {"list", luaV_list},
    {"dict", luaV_dict},
    {"blob", luaV_blob},
    {"funcref", luaV_funcref},
    {"buffer", luaV_buffer},
    {"window", luaV_window},
    {"open", luaV_open},
    {"type", luaV_type},
    {NULL, NULL}
};

/* for freeing list, dict, buffer and window objects; lightuserdata as arg */
    static int
luaV_free(lua_State *L)
{
    lua_pushnil(L);
    luaV_setudata(L, lua_touserdata(L, 1));
    return 0;
}

    static int
luaV_luaeval(lua_State *L)
{
    luaL_Buffer b;
    size_t l;
    const char *str = lua_tolstring(L, 1, &l);
    typval_T *arg = (typval_T *) lua_touserdata(L, 2);
    typval_T *rettv = (typval_T *) lua_touserdata(L, 3);
    luaL_buffinit(L, &b);
    luaL_addlstring(&b, LUAVIM_EVALHEADER, sizeof(LUAVIM_EVALHEADER) - 1);
    luaL_addlstring(&b, str, l);
    luaL_pushresult(&b);
    str = lua_tolstring(L, -1, &l);
    if (luaL_loadbuffer(L, str, l, LUAVIM_EVALNAME)) /* compile error? */
    {
	luaV_emsg(L);
	return 0;
    }
    luaV_pushtypval(L, arg);
    if (lua_pcall(L, 1, 1, 0)) /* running error? */
    {
	luaV_emsg(L);
	return 0;
    }
    if (luaV_totypval(L, -1, rettv) == FAIL)
	emsg("luaeval: cannot convert value");
    return 0;
}

    static int
luaV_setref(lua_State *L)
{
    int copyID = lua_tointeger(L, 1);
    int abort = FALSE;

    luaV_getfield(L, LUAVIM_LIST);
    luaV_getfield(L, LUAVIM_DICT);
    luaV_getfield(L, LUAVIM_FUNCREF);
    lua_pushnil(L);
    // traverse cache table
    while (!abort && lua_next(L, lua_upvalueindex(1)) != 0)
    {
	lua_getmetatable(L, -1);
	if (lua_rawequal(L, -1, 2)) // list?
	{
	    list_T *l = (list_T *)lua_touserdata(L, 5); // key

	    if (l->lv_copyID != copyID)
	    {
		l->lv_copyID = copyID;
		abort = set_ref_in_list(l, copyID, NULL);
	    }
	}
	else if (lua_rawequal(L, -1, 3)) // dict?
	{
	    dict_T *d = (dict_T *)lua_touserdata(L, 5); // key

	    if (d->dv_copyID != copyID)
	    {
		d->dv_copyID = copyID;
		abort = set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
	    }
	}
	else if (lua_rawequal(L, -1, 4)) // funcref?
	{
	    luaV_Funcref *f = (luaV_Funcref *)lua_touserdata(L, 5); // key

	    if (f->self != NULL && f->self->dv_copyID != copyID)
	    {
		f->self->dv_copyID = copyID;
		abort = set_ref_in_ht(&f->self->dv_hashtab, copyID, NULL);
	    }
	}
	lua_pop(L, 2); // metatable and value
    }
    lua_pushinteger(L, abort);
    return 1;
}

    static int
luaopen_vim(lua_State *L)
{
    /* set cache table */
    lua_newtable(L);
    lua_newtable(L);
    lua_pushstring(L, "v");
    lua_setfield(L, -2, "__mode");
    lua_setmetatable(L, -2); /* cache is weak-valued */
    /* print */
    lua_pushcfunction(L, luaV_print);
    lua_setglobal(L, "print");
    /* debug.debug */
    lua_getglobal(L, "debug");
    lua_pushcfunction(L, luaV_debug);
    lua_setfield(L, -2, "debug");
    lua_pop(L, 1);
    /* free */
    lua_pushlightuserdata(L, (void *) LUAVIM_FREE);
    lua_pushvalue(L, 1); /* cache table */
    lua_pushcclosure(L, luaV_free, 1);
    lua_rawset(L, LUA_REGISTRYINDEX);
    /* luaeval */
    lua_pushlightuserdata(L, (void *) LUAVIM_LUAEVAL);
    lua_pushvalue(L, 1); /* cache table */
    lua_pushcclosure(L, luaV_luaeval, 1);
    lua_rawset(L, LUA_REGISTRYINDEX);
    /* setref */
    lua_pushlightuserdata(L, (void *) LUAVIM_SETREF);
    lua_pushvalue(L, 1); /* cache table */
    lua_pushcclosure(L, luaV_setref, 1);
    lua_rawset(L, LUA_REGISTRYINDEX);
    /* register */
    luaV_newmetatable(L, LUAVIM_LIST);
    lua_pushvalue(L, 1);
    luaV_openlib(L, luaV_List_mt, 1);
    luaV_newmetatable(L, LUAVIM_DICT);
    lua_pushvalue(L, 1);
    luaV_openlib(L, luaV_Dict_mt, 1);
    luaV_newmetatable(L, LUAVIM_BLOB);
    lua_pushvalue(L, 1);
    luaV_openlib(L, luaV_Blob_mt, 1);
    luaV_newmetatable(L, LUAVIM_FUNCREF);
    lua_pushvalue(L, 1);
    luaV_openlib(L, luaV_Funcref_mt, 1);
    luaV_newmetatable(L, LUAVIM_BUFFER);
    lua_pushvalue(L, 1); /* cache table */
    luaV_openlib(L, luaV_Buffer_mt, 1);
    luaV_newmetatable(L, LUAVIM_WINDOW);
    lua_pushvalue(L, 1); /* cache table */
    luaV_openlib(L, luaV_Window_mt, 1);
    lua_newtable(L); /* vim table */
    lua_pushvalue(L, 1); /* cache table */
    luaV_openlib(L, luaV_module, 1);
    lua_setglobal(L, LUAVIM_NAME);
    return 0;
}

    static lua_State *
luaV_newstate(void)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L); /* core libs */
    lua_pushcfunction(L, luaopen_vim); /* vim */
    lua_call(L, 0, 0);
    return L;
}

    static void
luaV_setrange(lua_State *L, int line1, int line2)
{
    lua_getglobal(L, LUAVIM_NAME);
    lua_pushinteger(L, line1);
    lua_setfield(L, -2, "firstline");
    lua_pushinteger(L, line2);
    lua_setfield(L, -2, "lastline");
    lua_pop(L, 1); /* vim table */
}


/* =======   Interface   ======= */

static lua_State *L = NULL;

    static int
lua_isopen(void)
{
    return L != NULL;
}

    static int
lua_init(void)
{
    if (!lua_isopen())
    {
#ifdef DYNAMIC_LUA
	if (!lua_enabled(TRUE))
	{
	    emsg(_("Lua library cannot be loaded."));
	    return FAIL;
	}
#endif
	L = luaV_newstate();
    }
    return OK;
}

    void
lua_end(void)
{
    if (lua_isopen())
    {
	lua_close(L);
	L = NULL;
#ifdef DYNAMIC_LUA
	end_dynamic_lua();
#endif
    }
}

/* ex commands */
    void
ex_lua(exarg_T *eap)
{
    char *script;
    if (lua_init() == FAIL) return;
    script = (char *) script_get(eap, eap->arg);
    if (!eap->skip)
    {
	char *s = (script) ? script :  (char *) eap->arg;
	luaV_setrange(L, eap->line1, eap->line2);
	if (luaL_loadbuffer(L, s, strlen(s), LUAVIM_CHUNKNAME)
		|| lua_pcall(L, 0, 0, 0))
	    luaV_emsg(L);
    }
    if (script != NULL) vim_free(script);
}

    void
ex_luado(exarg_T *eap)
{
    linenr_T l;
    const char *s = (const char *) eap->arg;
    luaL_Buffer b;
    size_t len;
    buf_T *was_curbuf = curbuf;

    if (lua_init() == FAIL) return;
    if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
    {
	emsg(_("cannot save undo information"));
	return;
    }
    luaV_setrange(L, eap->line1, eap->line2);
    luaL_buffinit(L, &b);
    luaL_addlstring(&b, "return function(line, linenr) ", 30); /* header */
    luaL_addlstring(&b, s, strlen(s));
    luaL_addlstring(&b, " end", 4); /* footer */
    luaL_pushresult(&b);
    s = lua_tolstring(L, -1, &len);
    if (luaL_loadbuffer(L, s, len, LUAVIM_CHUNKNAME))
    {
	luaV_emsg(L);
	lua_pop(L, 1); /* function body */
	return;
    }
    lua_call(L, 0, 1);
    lua_replace(L, -2); /* function -> body */
    for (l = eap->line1; l <= eap->line2; l++)
    {
	/* Check the line number, the command my have deleted lines. */
	if (l > curbuf->b_ml.ml_line_count)
	    break;

	lua_pushvalue(L, -1); /* function */
	luaV_pushline(L, curbuf, l); /* current line as arg */
	lua_pushinteger(L, l); /* current line number as arg */
	if (lua_pcall(L, 2, 1, 0))
	{
	    luaV_emsg(L);
	    break;
	}
	/* Catch the command switching to another buffer. */
	if (curbuf != was_curbuf)
	    break;
	if (lua_isstring(L, -1)) /* update line? */
	{
#ifdef HAVE_SANDBOX
	    luaV_checksandbox(L);
#endif
	    ml_replace(l, luaV_toline(L, -1), TRUE);
	    changed_bytes(l, 0);
	    lua_pop(L, 1); /* result from luaV_toline */
	}
	lua_pop(L, 1); /* line */
    }
    lua_pop(L, 1); /* function */
    check_cursor();
    update_screen(NOT_VALID);
}

    void
ex_luafile(exarg_T *eap)
{
    if (lua_init() == FAIL)
	return;
    if (!eap->skip)
    {
	luaV_setrange(L, eap->line1, eap->line2);
	if (luaL_loadfile(L, (char *) eap->arg) || lua_pcall(L, 0, 0, 0))
	    luaV_emsg(L);
    }
}

#define luaV_freetype(typ,tname) \
	void \
    lua_##tname##_free(typ *o) \
    { \
	if (!lua_isopen()) return; \
	luaV_getfield(L, LUAVIM_FREE); \
	lua_pushlightuserdata(L, (void *) o); \
	lua_call(L, 1, 0); \
    }

luaV_freetype(buf_T, buffer)
luaV_freetype(win_T, window)

    void
do_luaeval(char_u *str, typval_T *arg, typval_T *rettv)
{
    lua_init();
    luaV_getfield(L, LUAVIM_LUAEVAL);
    lua_pushstring(L, (char *) str);
    lua_pushlightuserdata(L, (void *) arg);
    lua_pushlightuserdata(L, (void *) rettv);
    lua_call(L, 3, 0);
}

    int
set_ref_in_lua(int copyID)
{
    int aborted = 0;

    if (lua_isopen())
    {
	luaV_getfield(L, LUAVIM_SETREF);
	/* call the function with 1 arg, getting 1 result back */
	lua_pushinteger(L, copyID);
	lua_call(L, 1, 1);
	/* get the result */
	aborted = lua_tointeger(L, -1);
	/* pop result off the stack */
	lua_pop(L, 1);
    }
    return aborted;
}

#endif