/*
** $Id: luasql.c,v 1.28 2009/02/11 12:08:50 tomas Exp $
** See Copyright Notice in license.html
*/

#include <string.h>

#include "lua.h"
#include "lauxlib.h"


#include "luasql.h"

#if !defined(lua_pushliteral)
#define lua_pushliteral(L, s) \
		lua_pushstring(L, "" s, (sizeof(s) / sizeof(char)) - 1)
#endif


/*
** Typical database error situation
*/
LUASQL_API int luasql_faildirect(lua_State *L, const char *err)
{
	lua_pushnil(L);
	lua_pushliteral(L, LUASQL_PREFIX);
	lua_pushstring(L, err);
	lua_concat(L, 2);
	return 2;
}


/*
** Database error with LuaSQL message
** @param err LuaSQL error message.
** @param m Driver error message.
*/
LUASQL_API int luasql_failmsg(lua_State *L, const char *err, const char *m)
{
	lua_pushnil(L);
	lua_pushliteral(L, LUASQL_PREFIX);
	lua_pushstring(L, err);
	lua_pushstring(L, m);
	lua_concat(L, 3);
	return 2;
}


typedef struct { short closed; } pseudo_data;

/*
** Return the name of the object's metatable.
** This function is used by `tostring'.
*/
static int luasql_tostring(lua_State *L)
{
	char        buff[100];
	pseudo_data *obj = (pseudo_data *)lua_touserdata(L, 1);
	if (obj->closed)
	{
		strncpy(buff, "closed", sizeof(buff));
	}
	else
	{
		snprintf(buff, sizeof(buff), "%p", (void *)obj);
	}
	lua_pushfstring(L, "%s (%s)", lua_tostring(L, lua_upvalueindex(1)), buff);
	return 1;
}


#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM == 501
/*
** Adapted from Lua 5.2.0
*/
void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
{
	luaL_checkstack(L, nup + 1, "too many upvalues");
	for (; l->name != NULL; l++)    /* fill the table with given functions */
	{
		int i;
		lua_pushstring(L, l->name);
		for (i = 0; i < nup; i++)   /* copy upvalues to the top */
			lua_pushvalue(L, -(nup + 1));
		lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */
		lua_settable(L, -(nup + 3));
	}
	lua_pop(L, nup);    /* remove upvalues */
}
#endif

/*
** Create a metatable and leave it on top of the stack.
*/
LUASQL_API int luasql_createmeta(lua_State *L, const char *name, const luaL_Reg *methods)
{
	if (!luaL_newmetatable(L, name))
	{
		return 0;
	}

	/* define methods */
	luaL_setfuncs(L, methods, 0);

	/* define metamethods */
	lua_pushliteral(L, "__index");
	lua_pushvalue(L, -2);
	lua_settable(L, -3);

	lua_pushliteral(L, "__tostring");
	lua_pushstring(L, name);
	lua_pushcclosure(L, luasql_tostring, 1);
	lua_settable(L, -3);

	lua_pushliteral(L, "__metatable");
	lua_pushliteral(L, LUASQL_PREFIX "you're not allowed to get this metatable");
	lua_settable(L, -3);

	return 1;
}


/*
** Define the metatable for the object on top of the stack
*/
LUASQL_API void luasql_setmeta(lua_State *L, const char *name)
{
	luaL_getmetatable(L, name);
	lua_setmetatable(L, -2);
}


/*
** Assumes the table is on top of the stack.
*/
LUASQL_API void luasql_set_info(lua_State *L)
{
	lua_pushliteral(L, "_COPYRIGHT");
	lua_pushliteral(L, "Copyright (C) 2003-2020 Kepler Project");
	lua_settable(L, -3);
	lua_pushliteral(L, "_DESCRIPTION");
	lua_pushliteral(L, "LuaSQL is a simple interface from Lua to a DBMS");
	lua_settable(L, -3);
	lua_pushliteral(L, "_VERSION");
	lua_pushliteral(L, "LuaSQL 2.6.0 (for "LUA_VERSION ")");
	lua_settable(L, -3);
}
