Unverified Commit 67027c0d authored by Marcel Stör's avatar Marcel Stör Committed by GitHub
Browse files

Merge pull request #2340 from nodemcu/dev

2.2 master snap
parents 5073c199 18f33f5f
#ifndef APP_MODULES_COLOR_UTILS_H_
#define APP_MODULES_COLOR_UTILS_H_
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_math.h"
#include "c_string.h"
#include "user_interface.h"
#include "osapi.h"
/**
* Convert hsv to grb
* hue is 0-360, sat and val are 0-255
*/
uint32_t hsv2grb(uint16_t hue, uint8_t sat, uint8_t val);
/**
* Convert hsv to grbw
* hue is 0-360, sat and val are 0-255
*/
uint32_t hsv2grbw(uint16_t hue, uint8_t sat, uint8_t val);
/**
* Convert grb to hsv
* g, r, b are 0-255
*/
uint32_t grb2hsv(uint8_t g, uint8_t r, uint8_t b);
/**
* The color wheel function provides colors from r -> g -> b -> r.
* They are fully saturated and with full brightness.
* degree is from 0-360
*/
uint32_t color_wheel(uint16_t degree);
#endif /* APP_MODULES_COLOR_UTILS_H_ */
......@@ -104,7 +104,7 @@ static size_t lcron_findindex(lua_State *L, cronent_ud_t *ud) {
size_t i;
for (i = 0; i < cronent_count; i++) {
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
eud = lua_touserdata(L, 1);
eud = lua_touserdata(L, -1);
lua_pop(L, 1);
if (eud == ud) break;
}
......@@ -141,7 +141,7 @@ static int lcron_unschedule(lua_State *L) {
size_t i = lcron_findindex(L, ud);
if (i == -1) return 0;
luaL_unref(L, LUA_REGISTRYINDEX, cronent_list[i]);
memmove(cronent_list + i, cronent_list + i + 1, sizeof(int) * cronent_count - i - 1);
memmove(cronent_list + i, cronent_list + i + 1, sizeof(int) * (cronent_count - i - 1));
cronent_count--;
return 0;
}
......@@ -173,7 +173,7 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
desc.min = (uint64_t)1 << min;
for (size_t i = 0; i < cronent_count; i++) {
lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]);
cronent_ud_t *ent = lua_touserdata(L, 1);
cronent_ud_t *ent = lua_touserdata(L, -1);
lua_pop(L, 1);
if ((ent->desc.mon & desc.mon ) == 0) continue;
if ((ent->desc.dom & desc.dom ) == 0) continue;
......
......@@ -664,6 +664,8 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
struct station_config *cnf = luaM_malloc(lua_getstate(), sizeof(struct station_config));
c_memset(cnf, 0, sizeof(struct station_config));
cnf->threshold.rssi = -127;
cnf->threshold.authmode = AUTH_OPEN;
int err;
err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid));
......
......@@ -477,7 +477,7 @@ static int file_readline( lua_State* L )
{
GET_FILE_OBJ;
return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd);
return file_g_read(L, FILE_READ_CHUNK, '\n', fd);
}
// Lua: write("string")
......
......@@ -53,17 +53,31 @@ static void gpio_intr_callback_task (task_param_t param, uint8 priority)
// GPIO callbacks are run in L0 and include the level as a parameter
lua_State *L = lua_getstate();
NODE_DBG("Calling: %08x\n", gpio_cb_ref[pin]);
//
if (!INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
// Edge triggered -- re-enable the interrupt
platform_gpio_intr_init(pin, pin_int_type[pin]);
}
// Do the actual callback
lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
lua_pushinteger(L, level);
lua_pushinteger(L, then);
lua_call(L, 2, 0);
bool needs_callback = 1;
while (needs_callback) {
// Note that the interrupt level only modifies 'seen' and
// the base level only modifies 'reported'.
// Do the actual callback
lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
lua_pushinteger(L, level);
lua_pushinteger(L, then);
uint16_t seen = pin_counter[pin].seen;
lua_pushinteger(L, 0x7fff & (seen - pin_counter[pin].reported));
pin_counter[pin].reported = seen & 0x7fff; // This will cause the next interrupt to trigger a callback
uint16_t diff = (seen ^ pin_counter[pin].seen);
// Needs another callback if seen changed but not if the top bit is set
needs_callback = diff <= 0x7fff && diff > 0;
if (needs_callback) {
// Fake this for next time (this only happens if another interrupt happens since
// we loaded the 'seen' variable.
then = system_get_time() & 0x7fffffff;
}
lua_call(L, 3, 0);
}
if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
// Level triggered -- re-enable the callback
......@@ -108,6 +122,14 @@ static int lgpio_trig( lua_State* L )
// unreference any overwritten callback
if(old_pin_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, old_pin_ref);
uint16_t seen;
// Make sure that we clear out any queued interrupts
do {
seen = pin_counter[pin].seen;
pin_counter[pin].reported = seen & 0x7fff;
} while (seen != pin_counter[pin].seen);
NODE_DBG("Pin data: %d %d %08x, %d %d %d, %08x\n",
pin, type, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], gpio_cb_ref[pin]);
platform_gpio_intr_init(pin, type);
......@@ -132,7 +154,13 @@ static int lgpio_mode( lua_State* L )
NODE_DBG("pin,mode,pullup= %d %d %d\n",pin,mode,pullup);
NODE_DBG("Pin data at mode: %d %08x, %d %d %d, %08x\n",
pin, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], gpio_cb_ref[pin]);
pin, pin_mux[pin], pin_num[pin], pin_func[pin],
#ifdef GPIO_INTERRUPT_ENABLE
pin_int_type[pin], gpio_cb_ref[pin]
#else
0, 0
#endif
);
#ifdef GPIO_INTERRUPT_ENABLE
if (mode != INTERRUPT){ // disable interrupt
......@@ -200,8 +228,10 @@ static const os_param_t TIMER_OWNER = 0x6770696f; // "gpio"
static void seroutasync_done (task_param_t arg)
{
lua_State *L = lua_getstate();
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
serout.delay_table = NULL;
if (serout.delay_table) {
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
serout.delay_table = NULL;
}
if (serout.lua_done_ref != LUA_NOREF) {
lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
......@@ -282,17 +312,26 @@ static int lgpio_serout( lua_State* L )
}
} while (serout.repeats--);
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
serout.delay_table = NULL;
}
return 0;
}
#undef DELAY_TABLE_MAX_LEN
#ifdef LUA_USE_MODULES_GPIO_PULSE
extern const LUA_REG_TYPE gpio_pulse_map[];
extern int gpio_pulse_init(lua_State *);
#endif
// Module function map
static const LUA_REG_TYPE gpio_map[] = {
{ LSTRKEY( "mode" ), LFUNCVAL( lgpio_mode ) },
{ LSTRKEY( "read" ), LFUNCVAL( lgpio_read ) },
{ LSTRKEY( "write" ), LFUNCVAL( lgpio_write ) },
{ LSTRKEY( "serout" ), LFUNCVAL( lgpio_serout ) },
#ifdef LUA_USE_MODULES_GPIO_PULSE
{ LSTRKEY( "pulse" ), LROVAL( gpio_pulse_map ) }, //declared in gpio_pulse.c
#endif
#ifdef GPIO_INTERRUPT_ENABLE
{ LSTRKEY( "trig" ), LFUNCVAL( lgpio_trig ) },
{ LSTRKEY( "INT" ), LNUMVAL( INTERRUPT ) },
......@@ -308,6 +347,9 @@ static const LUA_REG_TYPE gpio_map[] = {
};
int luaopen_gpio( lua_State *L ) {
#ifdef LUA_USE_MODULES_GPIO_PULSE
gpio_pulse_init(L);
#endif
#ifdef GPIO_INTERRUPT_ENABLE
int i;
for(i=0;i<GPIO_PIN_NUM;i++){
......
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "user_interface.h"
#include "c_types.h"
#include "c_string.h"
#include "gpio.h"
#include "hw_timer.h"
#include "pin_map.h"
#include "driver/gpio16.h"
#define TIMER_OWNER 'P'
#define xstr(s) str(s)
#define str(s) #s
// Maximum delay in microseconds
#define DELAY_LIMIT 64000000
typedef struct {
uint32_t gpio_set;
uint32_t gpio_clr;
int32_t delay;
int32_t delay_min;
int32_t delay_max;
uint32_t count;
uint32_t count_left;
uint16_t loop;
} pulse_entry_t;
typedef struct {
uint32_t entry_count;
volatile uint32_t steps;
volatile uint16_t entry_pos;
volatile uint16_t active_pos;
volatile int16_t stop_pos; // -1 is stop nowhere, -2 is stop everywhere, otherwise is stop point
pulse_entry_t *entry;
volatile uint32_t expected_end_time;
volatile uint32_t desired_end_time;
volatile int32_t next_adjust;
volatile int32_t pending_delay;
volatile int cb_ref;
} pulse_t;
static int active_pulser_ref;
static pulse_t *active_pulser;
static task_handle_t tasknumber;
static int gpio_pulse_push_state(lua_State *L, pulse_t *pulser) {
uint32_t now;
uint32_t expected_end_time;
uint32_t active_pos;
uint32_t steps;
do {
now = 0x7FFFFFFF & system_get_time();
expected_end_time = pulser->expected_end_time;
active_pos = pulser->active_pos;
steps = pulser->steps;
} while (expected_end_time != pulser->expected_end_time ||
active_pos != pulser->active_pos ||
steps != pulser->steps);
if (active_pos >= pulser->entry_count) {
lua_pushnil(L);
} else {
lua_pushinteger(L, active_pos + 1); // Lua is 1 offset
}
lua_pushinteger(L, steps);
int32_t diff = (expected_end_time - now) & 0x7fffffff;
lua_pushinteger(L, (diff << 1) >> 1);
lua_pushinteger(L, now);
return 4;
}
static int gpio_pulse_getstate(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
return gpio_pulse_push_state(L, pulser);
}
static int gpio_pulse_stop(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
if (pulser != active_pulser) {
return 0;
}
int argno = 2;
int32_t stop_pos = -2;
if (lua_type(L, argno) == LUA_TNUMBER) {
stop_pos = luaL_checkinteger(L, 2);
if (stop_pos != -2) {
if (stop_pos < 1 || stop_pos > pulser->entry_count) {
return luaL_error( L, "bad stop position: %d (valid range 1 - %d)", stop_pos, pulser->entry_count );
}
stop_pos = stop_pos - 1;
}
argno++;
}
if (lua_type(L, argno) == LUA_TFUNCTION || lua_type(L, argno) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, argno);
} else {
return luaL_error( L, "missing callback" );
}
int new_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
int cb_ref = pulser->cb_ref;
pulser->cb_ref = LUA_NOREF;
pulser->stop_pos = -1;
pulser->cb_ref = new_cb_ref;
pulser->stop_pos = stop_pos;
luaL_unref(L, LUA_REGISTRYINDEX, cb_ref);
lua_pushboolean(L, 1);
return 1;
}
static int gpio_pulse_delete(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
if (pulser == active_pulser) {
return 0;
}
luaL_unref(L, LUA_REGISTRYINDEX, pulser->cb_ref);
return 0;
}
static void fill_entry_from_table(lua_State *L, pulse_entry_t *entry) {
if (lua_type(L, -1) != LUA_TTABLE) {
luaL_error(L, "All entries must be tables");
}
lua_pushnil(L); // key position
while (lua_next(L, -2)) {
// stack now contains: -1 => value; -2 => key; -3 => table
if (lua_type(L, -2) == LUA_TNUMBER) {
int pin = luaL_checkint(L, -2);
int value = luaL_checkint(L, -1);
if (pin < 0 || pin >= GPIO_PIN_NUM) {
luaL_error(L, "pin number %d must be in range 0 .. %d", pin, GPIO_PIN_NUM - 1);
}
if (value) {
entry->gpio_set |= BIT(pin_num[pin]);
} else {
entry->gpio_clr |= BIT(pin_num[pin]);
}
} else {
const char *str = luaL_checkstring(L, -2);
if (strcmp(str, "delay") == 0) {
entry->delay = luaL_checkint(L, -1);
if (entry->delay < 0 || entry->delay > DELAY_LIMIT) {
luaL_error(L, "delay of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay);
}
} else if (strcmp(str, "min") == 0) {
entry->delay_min = luaL_checkint(L, -1);
if (entry->delay_min < 0 || entry->delay_min > DELAY_LIMIT) {
luaL_error(L, "delay minimum of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay_min);
}
} else if (strcmp(str, "max") == 0) {
entry->delay_max = luaL_checkint(L, -1);
if (entry->delay_max < 0 || entry->delay_max > DELAY_LIMIT) {
luaL_error(L, "delay maximum of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay_max);
}
} else if (strcmp(str, "count") == 0) {
entry->count = luaL_checkint(L, -1);
} else if (strcmp(str, "loop") == 0) {
entry->loop = luaL_checkint(L, -1);
} else {
luaL_error(L, "Unrecognized key found: %s", str);
}
}
lua_pop(L, 1);
}
if (entry->delay_min != -1 || entry->delay_max != -1) {
if (entry->delay_min == -1) {
entry->delay_min = 0;
}
if (entry->delay_min > entry->delay ||
entry->delay_max < entry->delay) {
luaL_error(L, "Delay of %d must be between min and max", entry->delay);
}
}
lua_pop(L, 1);
}
static int gpio_pulse_build(lua_State *L) {
// Take a table argument
luaL_checktype(L, 1, LUA_TTABLE);
// First figure out how big we need the block to be
size_t size = luaL_getn(L, 1);
if (size > 100) {
return luaL_error(L, "table is too large: %d entries is more than 100", size);
}
size_t memsize = sizeof(pulse_t) + size * sizeof(pulse_entry_t);
pulse_t *pulser = (pulse_t *) lua_newuserdata(L, memsize);
memset(pulser, 0, memsize);
//
// Associate its metatable
luaL_getmetatable(L, "gpio.pulse");
lua_setmetatable(L, -2);
pulser->entry = (pulse_entry_t *) (pulser + 1);
pulser->entry_count = size;
size_t i;
for (i = 0; i < size; i++) {
pulse_entry_t *entry = pulser->entry + i;
entry->delay_min = -1;
entry->delay_max = -1;
lua_rawgeti(L, 1, i + 1);
fill_entry_from_table(L, entry);
}
return 1;
}
static int gpio_pulse_update(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
int entry_pos = luaL_checkinteger(L, 2);
if (entry_pos < 1 || entry_pos > pulser->entry_count) {
return luaL_error(L, "entry number must be in range 1 .. %d", pulser->entry_count);
}
pulse_entry_t *entry = pulser->entry + entry_pos - 1;
pulse_entry_t new_entry = *entry;
lua_pushvalue(L, 3);
fill_entry_from_table(L, &new_entry);
// Now do the update
ETS_FRC1_INTR_DISABLE();
*entry = new_entry;
ETS_FRC1_INTR_ENABLE();
return 0;
}
static int gpio_pulse_adjust(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
if (active_pulser != pulser) {
return 0;
}
int offset = luaL_checkinteger(L, 2);
// This will alter the next adjustable
pulser->next_adjust = offset;
int rc = gpio_pulse_push_state(L, active_pulser);
return rc;
}
static int gpio_pulse_cancel(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
if (active_pulser != pulser) {
return 0;
}
// Shut off the timer
platform_hw_timer_close(TIMER_OWNER);
int rc = gpio_pulse_push_state(L, active_pulser);
active_pulser = NULL;
int pulser_ref = active_pulser_ref;
active_pulser_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref);
return rc;
}
static void ICACHE_RAM_ATTR gpio_pulse_timeout(os_param_t p) {
(void) p;
uint32_t now = system_get_time();
int delay;
if (active_pulser) {
delay = active_pulser->pending_delay;
if (delay > 0) {
if (delay > 1200000) {
delay = 1000000;
}
active_pulser->pending_delay -= delay;
platform_hw_timer_arm_us(TIMER_OWNER, delay);
return;
}
}
do {
active_pulser->active_pos = active_pulser->entry_pos;
if (!active_pulser || active_pulser->entry_pos >= active_pulser->entry_count) {
if (active_pulser) {
active_pulser->steps++;
}
platform_hw_timer_close(TIMER_OWNER);
task_post_low(tasknumber, (task_param_t)0);
return;
}
active_pulser->steps++;
pulse_entry_t *entry = active_pulser->entry + active_pulser->entry_pos;
// Yes, this means that there is more skew on D0 than on other pins....
if (entry->gpio_set & 0x10000) {
gpio16_output_set(1);
}
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, entry->gpio_set);
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, entry->gpio_clr);
if (entry->gpio_clr & 0x10000) {
gpio16_output_set(0);
}
int16_t stop = active_pulser->stop_pos;
if (stop == -2 || stop == active_pulser->entry_pos) {
platform_hw_timer_close(TIMER_OWNER);
task_post_low(tasknumber, (task_param_t)0);
return;
}
if (entry->loop) {
if (entry->count_left == 0) {
entry->count_left = entry->count + 1;
}
if (--entry->count_left >= 1) {
active_pulser->entry_pos = entry->loop - 1; // zero offset
} else {
active_pulser->entry_pos++;
}
} else {
active_pulser->entry_pos++;
}
delay = entry->delay;
int delay_offset = 0;
if (entry->delay_min != -1) {
int offset = active_pulser->next_adjust;
active_pulser->next_adjust = 0;
delay_offset = ((0x7fffffff & (now - active_pulser->desired_end_time)) << 1) >> 1;
delay -= delay_offset;
delay += offset;
//dbg_printf("%d(et %d diff %d): Delay was %d us, offset = %d, delay_offset = %d, new delay = %d, range=%d..%d\n",
// now, active_pulser->desired_end_time, now - active_pulser->desired_end_time,
// entry->delay, offset, delay_offset, delay, entry->delay_min, entry->delay_max);
if (delay < entry->delay_min) {
// we can't delay as little as 'delay', so we need to adjust
// the next period as well.
active_pulser->next_adjust = (entry->delay - entry->delay_min) + offset;
delay = entry->delay_min;
} else if (delay > entry->delay_max) {
// we can't delay as much as 'delay', so we need to adjust
// the next period as well.
active_pulser->next_adjust = (entry->delay - entry->delay_max) + offset;
delay = entry->delay_max;
}
}
active_pulser->desired_end_time += delay + delay_offset;
active_pulser->expected_end_time = system_get_time() + delay;
} while (delay < 3);
if (delay > 1200000) {
active_pulser->pending_delay = delay - 1000000;
delay = 1000000;
}
platform_hw_timer_arm_us(TIMER_OWNER, delay);
}
static int gpio_pulse_start(lua_State *L) {
pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
if (active_pulser) {
return luaL_error(L, "pulse operation already in progress");
}
int argno = 2;
int initial_adjust;
if (lua_type(L, argno) == LUA_TNUMBER) {
initial_adjust = luaL_checkinteger(L, argno);
argno++;
}
if (lua_type(L, argno) == LUA_TFUNCTION || lua_type(L, argno) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, argno);
} else {
return luaL_error( L, "missing callback" );
}
luaL_unref(L, LUA_REGISTRYINDEX, pulser->cb_ref);
pulser->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
active_pulser = pulser;
lua_pushvalue(L, 1);
active_pulser_ref = luaL_ref(L, LUA_REGISTRYINDEX);
size_t i;
for (i = 0; i < pulser->entry_count; i++) {
pulser->entry[i].count_left = 0;
}
pulser->entry_pos = 0;
pulser->active_pos = 0;
pulser->steps = 0;
pulser->stop_pos = -1;
pulser->next_adjust = initial_adjust;
// Now start things up
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
// Failed to init the timer
luaL_error(L, "Unable to initialize timer");
}
active_pulser->expected_end_time = 0x7fffffff & system_get_time();
platform_hw_timer_set_func(TIMER_OWNER, gpio_pulse_timeout, 0);
gpio_pulse_timeout(0);
return 0;
}
static void gpio_pulse_task(os_param_t param, uint8_t prio)
{
(void) param;
(void) prio;
if (active_pulser) {
lua_State *L = lua_getstate();
// Invoke the callback
lua_rawgeti(L, LUA_REGISTRYINDEX, active_pulser->cb_ref);
int rc = gpio_pulse_push_state(L, active_pulser);
active_pulser = NULL;
int pulser_ref = active_pulser_ref;
active_pulser_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref);
lua_call(L, rc, 0);
}
}
static const LUA_REG_TYPE pulse_map[] = {
{ LSTRKEY( "getstate" ), LFUNCVAL( gpio_pulse_getstate ) },
{ LSTRKEY( "stop" ), LFUNCVAL( gpio_pulse_stop ) },
{ LSTRKEY( "cancel" ), LFUNCVAL( gpio_pulse_cancel ) },
{ LSTRKEY( "start" ), LFUNCVAL( gpio_pulse_start ) },
{ LSTRKEY( "adjust" ), LFUNCVAL( gpio_pulse_adjust ) },
{ LSTRKEY( "update" ), LFUNCVAL( gpio_pulse_update ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( gpio_pulse_delete ) },
{ LSTRKEY( "__index" ), LROVAL( pulse_map ) },
{ LNILKEY, LNILVAL }
};
const LUA_REG_TYPE gpio_pulse_map[] =
{
{ LSTRKEY( "build" ), LFUNCVAL( gpio_pulse_build ) },
{ LSTRKEY( "__index" ), LROVAL( gpio_pulse_map ) },
{ LNILKEY, LNILVAL }
};
int gpio_pulse_init(lua_State *L)
{
luaL_rometatable(L, "gpio.pulse", (void *)pulse_map);
tasknumber = task_get_id(gpio_pulse_task);
return 0;
}
//NODEMCU_MODULE(GPIOPULSE, "gpiopulse", gpio_pulse_map, gpio_pulse_init);
......@@ -33,33 +33,6 @@ static int hdc1080_setup(lua_State* L) {
return 0;
}
static int hdc1080_init(lua_State* L) {
uint32_t sda;
uint32_t scl;
platform_print_deprecation_note("hdc1080.init() is replaced by hdc1080.setup()", "in the next version");
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
return luaL_error(L, "wrong arg range");
}
sda = luaL_checkinteger(L, 1);
scl = luaL_checkinteger(L, 2);
if (scl == 0 || sda == 0) {
return luaL_error(L, "no i2c for D0");
}
platform_i2c_setup(hdc1080_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
// remove sda and scl parameters from stack
lua_remove(L, 1);
lua_remove(L, 1);
return hdc1080_setup(L);
}
static int hdc1080_read(lua_State* L) {
uint8_t data[2];
......@@ -129,7 +102,6 @@ static int hdc1080_read(lua_State* L) {
static const LUA_REG_TYPE hdc1080_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL( hdc1080_read )},
{ LSTRKEY( "setup" ), LFUNCVAL( hdc1080_setup )},
{ LSTRKEY( "init" ), LFUNCVAL( hdc1080_init )},
{ LNILKEY, LNILVAL}
};
......
......@@ -57,23 +57,6 @@ static int hmc5883_setup(lua_State* L) {
return 0;
}
static int hmc5883_init(lua_State* L) {
uint32_t sda;
uint32_t scl;
platform_print_deprecation_note("hmc5883l.init() is replaced by hmc5883l.setup()", "in the next version");
sda = luaL_checkinteger(L, 1);
scl = luaL_checkinteger(L, 2);
luaL_argcheck(L, sda > 0 && scl > 0, 1, "no i2c for D0");
platform_i2c_setup(hmc5883_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
return hmc5883_setup(L);
}
static int hmc5883_read(lua_State* L) {
uint8_t data[6];
......@@ -96,8 +79,8 @@ static int hmc5883_read(lua_State* L) {
platform_i2c_send_stop(hmc5883_i2c_id);
x = (int16_t) ((data[0] << 8) | data[1]);
y = (int16_t) ((data[2] << 8) | data[3]);
z = (int16_t) ((data[4] << 8) | data[5]);
z = (int16_t) ((data[2] << 8) | data[3]);
y = (int16_t) ((data[4] << 8) | data[5]);
lua_pushinteger(L, x);
lua_pushinteger(L, y);
......@@ -109,8 +92,6 @@ static int hmc5883_read(lua_State* L) {
static const LUA_REG_TYPE hmc5883_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL( hmc5883_read )},
{ LSTRKEY( "setup" ), LFUNCVAL( hmc5883_setup )},
// init() is deprecated
{ LSTRKEY( "init" ), LFUNCVAL( hmc5883_init )},
{ LNILKEY, LNILVAL}
};
......
......@@ -48,23 +48,6 @@ static int l3g4200d_setup(lua_State* L) {
return 0;
}
static int l3g4200d_init(lua_State* L) {
uint32_t sda;
uint32_t scl;
platform_print_deprecation_note("l3g4200d.init() is replaced by l3g4200d.setup()", "in the next version");
sda = luaL_checkinteger(L, 1);
scl = luaL_checkinteger(L, 2);
luaL_argcheck(L, sda > 0 && scl > 0, 1, "no i2c for D0");
platform_i2c_setup(i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
return l3g4200d_setup(L);
}
static int l3g4200d_read(lua_State* L) {
uint8_t data[6];
......@@ -99,8 +82,6 @@ static int l3g4200d_read(lua_State* L) {
static const LUA_REG_TYPE l3g4200d_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL( l3g4200d_read )},
{ LSTRKEY( "setup" ), LFUNCVAL( l3g4200d_setup )},
// init() is deprecated
{ LSTRKEY( "init" ), LFUNCVAL( l3g4200d_init )},
{ LNILKEY, LNILVAL}
};
......
......@@ -337,6 +337,9 @@ READPACKET:
} else {
mud->connState = MQTT_DATA;
NODE_DBG("MQTT: Connected\r\n");
mud->keepalive_sent = 0;
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref);
mud->cb_connect_fail_ref = LUA_NOREF;
if (mud->mqtt_state.auto_reconnect == RECONNECT_POSSIBLE) {
mud->mqtt_state.auto_reconnect = RECONNECT_ON;
}
......
......@@ -202,20 +202,29 @@ static void net_recv_cb(lnet_userdata *ud, struct pbuf *p, ip_addr_t *addr, u16_
pbuf_free(p);
return;
}
lua_State *L = lua_getstate();
int num_args = 2;
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushlstring(L, p->payload, p->len);
if (ud->type == TYPE_UDP_SOCKET) {
char iptmp[16] = { 0, };
if (ud->type == TYPE_UDP_SOCKET)
{
num_args += 2;
char iptmp[16];
bzero(iptmp, 16);
ets_sprintf(iptmp, IPSTR, IP2STR(&addr->addr));
lua_pushinteger(L, port);
lua_pushstring(L, iptmp);
}
lua_call(L, num_args, 0);
lua_State *L = lua_getstate();
struct pbuf *pp = p;
while (pp)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushlstring(L, pp->payload, pp->len);
if (ud->type == TYPE_UDP_SOCKET) {
lua_pushinteger(L, port);
lua_pushstring(L, iptmp);
}
lua_call(L, num_args, 0);
pp = pp->next;
}
pbuf_free(p);
}
......
......@@ -19,6 +19,9 @@
#define COND_REF(_cond) _cond ? luaL_ref( L, LUA_REGISTRYINDEX ) : LUA_NOREF
// task handles
task_handle_t pcm_data_vu_task, pcm_data_play_task, pcm_start_play_task;
static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
{
if (cb_ref != LUA_NOREF) {
......@@ -94,7 +97,7 @@ static int pcm_drv_pause( lua_State *L )
return 0;
}
static void pcm_start_play_task( task_param_t param, uint8 prio )
static void pcm_start_play( task_param_t param, uint8 prio )
{
lua_State *L = lua_getstate();
pud_t *pud = (pud_t *)param;
......@@ -126,8 +129,8 @@ static int pcm_drv_play( lua_State *L )
}
// schedule actions for play in separate task since drv:play() might have been called
// in the callback fn of pcm_data_play_task() which in turn gets called when starting play...
task_post_low( cfg->start_play_task, (os_param_t)pud );
// in the callback fn of pcm_data_play() which in turn gets called when starting play...
task_post_low( pcm_start_play_task, (os_param_t)pud );
return 0;
}
......@@ -203,10 +206,7 @@ static int pcm_new( lua_State *L )
cfg->bufs[0].rpos = cfg->bufs[1].rpos = 0;
cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
cfg->data_vu_task = task_get_id( pcm_data_vu_task );
cfg->vu_freq = 10;
cfg->data_play_task = task_get_id( pcm_data_play_task );
cfg->start_play_task = task_get_id( pcm_start_play_task );
if (driver == PCM_DRIVER_SD) {
cfg->pin = luaL_checkinteger( L, 2 );
......@@ -257,6 +257,10 @@ static const LUA_REG_TYPE pcm_map[] = {
int luaopen_pcm( lua_State *L ) {
luaL_rometatable( L, "pcm.driver", (void *)pcm_driver_map ); // create metatable
pcm_data_vu_task = task_get_id( pcm_data_vu );
pcm_data_play_task = task_get_id( pcm_data_play );
pcm_start_play_task = task_get_id( pcm_start_play );
return 0;
}
......
......@@ -167,6 +167,15 @@ static void do_sleep_opt (lua_State *L, int idx)
}
}
// rtctime.adjust_delta (usec)
static int rtctime_adjust_delta (lua_State *L)
{
uint32_t us = luaL_checknumber (L, 1);
lua_pushnumber(L, rtc_time_adjust_delta_by_rate(us));
return 1;
}
// rtctime.dsleep (usec, option)
static int rtctime_dsleep (lua_State *L)
{
......@@ -222,6 +231,7 @@ static int rtctime_epoch2cal (lua_State *L)
static const LUA_REG_TYPE rtctime_map[] = {
{ LSTRKEY("set"), LFUNCVAL(rtctime_set) },
{ LSTRKEY("get"), LFUNCVAL(rtctime_get) },
{ LSTRKEY("adjust_delta"), LFUNCVAL(rtctime_adjust_delta) },
{ LSTRKEY("dsleep"), LFUNCVAL(rtctime_dsleep) },
{ LSTRKEY("dsleep_aligned"), LFUNCVAL(rtctime_dsleep_aligned) },
{ LSTRKEY("epoch2cal"), LFUNCVAL(rtctime_epoch2cal) },
......
......@@ -765,6 +765,8 @@ static void encode_lua_object(lua_State *L, ENC_DATA *data, int argno, const cha
}
*d = '\0';
luaL_addstring(&b, value);
} else if (*str == '"') {
luaL_addstring(&b, "\\\"");
} else {
luaL_addchar(&b, *str);
}
......
......@@ -132,6 +132,7 @@ typedef struct
typedef struct {
int32_t sync_cb_ref;
int32_t err_cb_ref;
int32_t list_ref;
os_timer_t timer;
} sntp_repeat_t;
......@@ -225,6 +226,9 @@ static void sntp_handle_result(lua_State *L) {
const uint32_t MICROSECONDS = 1000000;
if (state->best.stratum == 0) {
// This could be because none of the servers are reachable, or maybe we haven't been able to look
// them up.
server_count = 0; // Reset for next time.
handle_error(L, NTP_TIMEOUT_ERR, NULL);
return;
}
......@@ -382,14 +386,13 @@ static void sntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
if (ipaddr == NULL)
{
sntp_dbg("DNS Fail!\n");
task_post_low(tasknumber, (uint32_t) name);
}
else
{
serverp[server_count] = *ipaddr;
server_count++;
task_post_low(tasknumber, SNTP_DOLOOKUPS_ID);
}
task_post_low(tasknumber, SNTP_DOLOOKUPS_ID);
}
......@@ -614,17 +617,29 @@ static int sntp_getoffset(lua_State *L)
static void sntp_dolookups (lua_State *L) {
// Step through each element of the table, converting it to an address
// at the end, start the lookups
//
// at the end, start the lookups. If we have already looked everything up,
// then move straight to sending the packets.
if (state->list_ref == LUA_NOREF) {
sntp_dosend(L);
sntp_dosend();
return;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, state->list_ref);
while (1) {
int l;
if (lua_objlen(L, -1) <= state->lookup_pos) {
// We reached the end
sntp_dosend(L);
if (server_count == 0) {
// Oh dear -- no valid entries -- generate an error
// This means that all the arguments are invalid. Just pick the first
lua_rawgeti(L, -1, 1);
const char *hostname = luaL_checklstring(L, -1, &l);
handle_error(L, NTP_DNS_ERR, hostname);
lua_pop(L, 1);
} else {
sntp_dosend();
}
break;
}
......@@ -632,8 +647,6 @@ static void sntp_dolookups (lua_State *L) {
lua_rawgeti(L, -1, state->lookup_pos);
int l;
const char *hostname = luaL_checklstring(L, -1, &l);
lua_pop(L, 1);
......@@ -692,6 +705,8 @@ static char *set_repeat_mode(lua_State *L, bool enable)
repeat->sync_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, LUA_REGISTRYINDEX, state->err_cb_ref);
repeat->err_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, LUA_REGISTRYINDEX, state->list_ref);
repeat->list_ref = luaL_ref(L, LUA_REGISTRYINDEX);
os_timer_setfn(&repeat->timer, on_long_timeout, NULL);
os_timer_arm(&repeat->timer, 1000 * 1000, 1);
} else {
......@@ -699,6 +714,7 @@ static char *set_repeat_mode(lua_State *L, bool enable)
os_timer_disarm (&repeat->timer);
luaL_unref (L, LUA_REGISTRYINDEX, repeat->sync_cb_ref);
luaL_unref (L, LUA_REGISTRYINDEX, repeat->err_cb_ref);
luaL_unref (L, LUA_REGISTRYINDEX, repeat->list_ref);
c_free(repeat);
repeat = NULL;
}
......@@ -718,8 +734,12 @@ static void on_long_timeout (void *arg)
state->sync_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, LUA_REGISTRYINDEX, repeat->err_cb_ref);
state->err_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (server_count == 0) {
lua_rawgeti(L, LUA_REGISTRYINDEX, repeat->list_ref);
state->list_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
state->is_on_timeout = 1;
sntp_dosend (L);
sntp_dolookups(L);
}
}
}
......@@ -794,7 +814,7 @@ static int sntp_sync (lua_State *L)
goto good_ret;
}
sntp_dosend (L);
sntp_dosend ();
good_ret:
if (!lua_isnoneornil(L, 4)) {
......@@ -825,10 +845,8 @@ static void sntp_task(os_param_t param, uint8_t prio)
sntp_handle_result(L);
} else if (param == SNTP_DOLOOKUPS_ID) {
sntp_dolookups(L);
} else if (param >= 0 && param <= NTP_MAX_ERR_ID) {
handle_error(L, param, NULL);
} else {
handle_error(L, NTP_DNS_ERR, (const char *) param);
handle_error(L, param, NULL);
}
}
......
/************************************************************************
* lsqlite3 *
* Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie *
* All rights reserved. *
* Author : Tiago Dionizio <tiago.dionizio@ist.utl.pt> *
* Author : Doug Currie <doug.currie@alum.mit.edu> *
* Library : lsqlite3 - an SQLite 3 database binding for Lua 5 *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
************************************************************************/
#define LSQLITE_VERSION "0.9.4"
#define LSQLITE_OMIT_UPDATE_HOOK 1
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
#include <c_stdlib.h>
#include <c_string.h>
#include <assert.h>
#define LUA_LIB
#include "module.h"
#include "lua.h"
#include "lauxlib.h"
#if LUA_VERSION_NUM > 501
/*
** Lua 5.2
*/
#define lua_strlen lua_rawlen
/* luaL_typerror always used with arg at ndx == NULL */
#define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str)
/* luaL_register used once, so below expansion is OK for this case */
#define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0)
/* luaL_openlib always used with name == NULL */
#define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup)
#if LUA_VERSION_NUM > 502
/*
** Lua 5.3
*/
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#endif
#endif
#include "sqlite3.h"
/* compile time features */
#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK)
#define SQLITE_OMIT_PROGRESS_CALLBACK 0
#endif
#if !defined(LSQLITE_OMIT_UPDATE_HOOK)
#define LSQLITE_OMIT_UPDATE_HOOK 0
#endif
#if defined(LSQLITE_OMIT_OPEN_V2)
#define SQLITE3_OPEN(L,filename,flags) sqlite3_open(L,filename)
#else
#define SQLITE3_OPEN(L,filename,flags) sqlite3_open_v2(L,filename,flags,NULL)
#endif
typedef struct sdb sdb;
typedef struct sdb_vm sdb_vm;
typedef struct sdb_bu sdb_bu;
typedef struct sdb_func sdb_func;
/* to use as C user data so i know what function sqlite is calling */
struct sdb_func {
/* references to associated lua values */
int fn_step;
int fn_finalize;
int udata;
sdb *db;
char aggregate;
sdb_func *next;
};
/* information about database */
struct sdb {
/* associated lua state */
lua_State *L;
/* sqlite database handle */
sqlite3 *db;
/* sql functions stack usage */
sdb_func *func; /* top SQL function being called */
/* references */
int busy_cb; /* busy callback */
int busy_udata;
int progress_cb; /* progress handler */
int progress_udata;
int trace_cb; /* trace callback */
int trace_udata;
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
int update_hook_cb; /* update_hook callback */
int update_hook_udata;
int commit_hook_cb; /* commit_hook callback */
int commit_hook_udata;
int rollback_hook_cb; /* rollback_hook callback */
int rollback_hook_udata;
#endif
};
static const char *sqlite_meta = ":sqlite3";
static const char *sqlite_vm_meta = ":sqlite3:vm";
static const char *sqlite_bu_meta = ":sqlite3:bu";
static const char *sqlite_ctx_meta = ":sqlite3:ctx";
static int sqlite_ctx_meta_ref;
/* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32
** or 64 bits (or something else?). This helper macro tries to do "the right thing."
*/
#if LUA_VERSION_NUM > 502
#define PUSH_INT64(L,i64in,fallback) \
do { \
sqlite_int64 i64 = i64in; \
lua_Integer i = (lua_Integer )i64; \
if (i == i64) lua_pushinteger(L, i);\
else { \
lua_Number n = (lua_Number)i64; \
if (n == i64) lua_pushnumber(L, n); \
else fallback; \
} \
} while (0)
#else
#define PUSH_INT64(L,i64in,fallback) \
do { \
sqlite_int64 i64 = i64in; \
lua_Number n = (lua_Number)i64; \
if (n == i64) lua_pushnumber(L, n); \
else fallback; \
} while (0)
#endif
/*
** =======================================================
** Database Virtual Machine Operations
** =======================================================
*/
static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) {
switch (sqlite3_column_type(vm, idx)) {
case SQLITE_INTEGER:
PUSH_INT64(L, sqlite3_column_int64(vm, idx)
, lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx)
, sqlite3_column_bytes(vm, idx)));
break;
case SQLITE_FLOAT:
lua_pushnumber(L, sqlite3_column_double(vm, idx));
break;
case SQLITE_TEXT:
lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx));
break;
case SQLITE_BLOB:
lua_pushlstring(L, sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx));
break;
case SQLITE_NULL:
lua_pushnil(L);
break;
default:
lua_pushnil(L);
break;
}
}
/* virtual machine information */
struct sdb_vm {
sdb *db; /* associated database handle */
sqlite3_stmt *vm; /* virtual machine */
/* sqlite3_step info */
int columns; /* number of columns in result */
char has_values; /* true when step succeeds */
char temp; /* temporary vm used in db:rows */
};
/* called with db,sql text on the lua stack */
static sdb_vm *newvm(lua_State *L, sdb *db) {
sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); /* db sql svm_ud -- */
luaL_getmetatable(L, sqlite_vm_meta);
lua_setmetatable(L, -2); /* set metatable */
svm->db = db;
svm->columns = 0;
svm->has_values = 0;
svm->vm = NULL;
svm->temp = 0;
/* add an entry on the database table: svm -> db to keep db live while svm is live */
lua_pushlightuserdata(L, db); /* db sql svm_ud db_lud -- */
lua_rawget(L, LUA_REGISTRYINDEX); /* db sql svm_ud reg[db_lud] -- */
lua_pushlightuserdata(L, svm); /* db sql svm_ud reg[db_lud] svm_lud -- */
lua_pushvalue(L, -5); /* db sql svm_ud reg[db_lud] svm_lud db -- */
lua_rawset(L, -3); /* (reg[db_lud])[svm_lud] = db ; set the db for this vm */
lua_pop(L, 1); /* db sql svm_ud -- */
return svm;
}
static int cleanupvm(lua_State *L, sdb_vm *svm) {
/* remove entry in database table - no harm if not present in the table */
lua_pushlightuserdata(L, svm->db);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, svm);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1);
svm->columns = 0;
svm->has_values = 0;
if (!svm->vm) return 0;
lua_pushinteger(L, sqlite3_finalize(svm->vm));
svm->vm = NULL;
return 1;
}
static int stepvm(lua_State *L, sdb_vm *svm) {
return sqlite3_step(svm->vm);
}
static sdb_vm *lsqlite_getvm(lua_State *L, int index) {
sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta);
if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine");
return svm;
}
static sdb_vm *lsqlite_checkvm(lua_State *L, int index) {
sdb_vm *svm = lsqlite_getvm(L, index);
if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine");
return svm;
}
static int dbvm_isopen(lua_State *L) {
sdb_vm *svm = lsqlite_getvm(L, 1);
lua_pushboolean(L, svm->vm != NULL ? 1 : 0);
return 1;
}
static int dbvm_tostring(lua_State *L) {
char buff[39];
sdb_vm *svm = lsqlite_getvm(L, 1);
if (svm->vm == NULL)
strcpy(buff, "closed");
else
c_sprintf(buff, "%p", svm);
lua_pushfstring(L, "sqlite virtual machine (%s)", buff);
return 1;
}
static int dbvm_gc(lua_State *L) {
sdb_vm *svm = lsqlite_getvm(L, 1);
if (svm->vm != NULL) /* ignore closed vms */
cleanupvm(L, svm);
return 0;
}
static int dbvm_step(lua_State *L) {
int result;
sdb_vm *svm = lsqlite_checkvm(L, 1);
result = stepvm(L, svm);
svm->has_values = result == SQLITE_ROW ? 1 : 0;
svm->columns = sqlite3_data_count(svm->vm);
lua_pushinteger(L, result);
return 1;
}
static int dbvm_finalize(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
return cleanupvm(L, svm);
}
static int dbvm_reset(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_reset(svm->vm);
lua_pushinteger(L, sqlite3_errcode(svm->db->db));
return 1;
}
static void dbvm_check_contents(lua_State *L, sdb_vm *svm) {
if (!svm->has_values) {
luaL_error(L, "misuse of function");
}
}
static void dbvm_check_index(lua_State *L, sdb_vm *svm, int index) {
if (index < 0 || index >= svm->columns) {
luaL_error(L, "index out of range [0..%d]", svm->columns - 1);
}
}
static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) {
if (index < 1 || index > sqlite3_bind_parameter_count(svm->vm)) {
luaL_error(L, "bind index out of range [1..%d]", sqlite3_bind_parameter_count(svm->vm));
}
}
static int dbvm_last_insert_rowid(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
/* conversion warning: int64 -> luaNumber */
sqlite_int64 rowid = sqlite3_last_insert_rowid(svm->db->db);
PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid));
return 1;
}
/*
** =======================================================
** Virtual Machine - generic info
** =======================================================
*/
static int dbvm_columns(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
lua_pushinteger(L, sqlite3_column_count(svm->vm));
return 1;
}
/*
** =======================================================
** Virtual Machine - getters
** =======================================================
*/
static int dbvm_get_value(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checkint(L, 2);
dbvm_check_contents(L, svm);
dbvm_check_index(L, svm, index);
vm_push_column(L, svm->vm, index);
return 1;
}
static int dbvm_get_name(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checknumber(L, 2);
dbvm_check_index(L, svm, index);
lua_pushstring(L, sqlite3_column_name(svm->vm, index));
return 1;
}
#if 0
static int dbvm_get_type(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checknumber(L, 2);
dbvm_check_index(L, svm, index);
lua_pushstring(L, sqlite3_column_decltype(svm->vm, index));
return 1;
}
#else
static int dbvm_get_type(lua_State *L) {
lua_pushliteral(L, "column decltype support disabled at compile time");
lua_error(L);
return 0;
}
#endif
static int dbvm_get_values(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = svm->columns;
int n;
dbvm_check_contents(L, svm);
lua_createtable(L, columns, 0);
for (n = 0; n < columns;) {
vm_push_column(L, vm, n++);
lua_rawseti(L, -2, n);
}
return 1;
}
static int dbvm_get_names(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
int n;
lua_createtable(L, columns, 0);
for (n = 0; n < columns;) {
lua_pushstring(L, sqlite3_column_name(vm, n++));
lua_rawseti(L, -2, n);
}
return 1;
}
#if 0
static int dbvm_get_types(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
int n;
lua_createtable(L, columns, 0);
for (n = 0; n < columns;) {
lua_pushstring(L, sqlite3_column_decltype(vm, n++));
lua_rawseti(L, -2, n);
}
return 1;
}
#else
static int dbvm_get_types(lua_State *L) {
lua_pushliteral(L, "column decltype support disabled at compile time");
lua_error(L);
return 0;
}
#endif
static int dbvm_get_uvalues(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = svm->columns;
int n;
dbvm_check_contents(L, svm);
lua_checkstack(L, columns);
for (n = 0; n < columns; ++n)
vm_push_column(L, vm, n);
return columns;
}
static int dbvm_get_unames(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
int n;
lua_checkstack(L, columns);
for (n = 0; n < columns; ++n)
lua_pushstring(L, sqlite3_column_name(vm, n));
return columns;
}
#if 0
static int dbvm_get_utypes(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
int n;
lua_checkstack(L, columns);
for (n = 0; n < columns; ++n)
lua_pushstring(L, sqlite3_column_decltype(vm, n));
return columns;
}
#else
static int dbvm_get_utypes(lua_State *L) {
lua_pushliteral(L, "column decltype support disabled at compile time");
lua_error(L);
return 0;
}
#endif
static int dbvm_get_named_values(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = svm->columns;
int n;
dbvm_check_contents(L, svm);
lua_createtable(L, 0, columns);
for (n = 0; n < columns; ++n) {
lua_pushstring(L, sqlite3_column_name(vm, n));
vm_push_column(L, vm, n);
lua_rawset(L, -3);
}
return 1;
}
#if 0
static int dbvm_get_named_types(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int columns = sqlite3_column_count(vm);
int n;
lua_createtable(L, 0, columns);
for (n = 0; n < columns; ++n) {
lua_pushstring(L, sqlite3_column_name(vm, n));
lua_pushstring(L, sqlite3_column_decltype(vm, n));
lua_rawset(L, -3);
}
return 1;
}
#else
static int dbvm_get_named_types(lua_State *L) {
lua_pushliteral(L, "column decltype support disabled at compile time");
lua_error(L);
return 0;
}
#endif
/*
** =======================================================
** Virtual Machine - Bind
** =======================================================
*/
static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) {
switch (lua_type(L, lindex)) {
case LUA_TSTRING:
return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT);
case LUA_TNUMBER:
#if LUA_VERSION_NUM > 502
if (lua_isinteger(L, lindex))
return sqlite3_bind_int64(vm, index, lua_tointeger(L, lindex));
#endif
return sqlite3_bind_double(vm, index, lua_tonumber(L, lindex));
case LUA_TBOOLEAN:
return sqlite3_bind_int(vm, index, lua_toboolean(L, lindex) ? 1 : 0);
case LUA_TNONE:
case LUA_TNIL:
return sqlite3_bind_null(vm, index);
default:
luaL_error(L, "index (%d) - invalid data type for bind (%s)", index, lua_typename(L, lua_type(L, lindex)));
return SQLITE_MISUSE; /*!*/
}
}
static int dbvm_bind_parameter_count(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
lua_pushinteger(L, sqlite3_bind_parameter_count(svm->vm));
return 1;
}
static int dbvm_bind_parameter_name(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checknumber(L, 2);
dbvm_check_bind_index(L, svm, index);
lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index));
return 1;
}
static int dbvm_bind(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int index = luaL_checkint(L, 2);
int result;
dbvm_check_bind_index(L, svm, index);
result = dbvm_bind_index(L, vm, index, 3);
lua_pushinteger(L, result);
return 1;
}
static int dbvm_bind_blob(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checkint(L, 2);
const char *value = luaL_checkstring(L, 3);
int len = lua_strlen(L, 3);
lua_pushinteger(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT));
return 1;
}
static int dbvm_bind_values(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int top = lua_gettop(L);
int result, n;
if (top - 1 != sqlite3_bind_parameter_count(vm))
luaL_error(L,
"incorrect number of parameters to bind (%d given, %d to bind)",
top - 1,
sqlite3_bind_parameter_count(vm)
);
for (n = 2; n <= top; ++n) {
if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) {
lua_pushinteger(L, result);
return 1;
}
}
lua_pushinteger(L, SQLITE_OK);
return 1;
}
static int dbvm_bind_names(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm = svm->vm;
int count = sqlite3_bind_parameter_count(vm);
const char *name;
int result, n;
luaL_checktype(L, 2, LUA_TTABLE);
for (n = 1; n <= count; ++n) {
name = sqlite3_bind_parameter_name(vm, n);
if (name && (name[0] == ':' || name[0] == '$')) {
lua_pushstring(L, ++name);
lua_gettable(L, 2);
result = dbvm_bind_index(L, vm, n, -1);
lua_pop(L, 1);
}
else {
lua_pushinteger(L, n);
lua_gettable(L, 2);
result = dbvm_bind_index(L, vm, n, -1);
lua_pop(L, 1);
}
if (result != SQLITE_OK) {
lua_pushinteger(L, result);
return 1;
}
}
lua_pushinteger(L, SQLITE_OK);
return 1;
}
/*
** =======================================================
** Database (internal management)
** =======================================================
*/
/*
** When creating database handles, always creates a `closed' database handle
** before opening the actual database; so, if there is a memory error, the
** database is not left opened.
**
** Creates a new 'table' and leaves it in the stack
*/
static sdb *newdb (lua_State *L) {
sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb));
db->L = L;
db->db = NULL; /* database handle is currently `closed' */
db->func = NULL;
db->busy_cb =
db->busy_udata =
db->progress_cb =
db->progress_udata =
db->trace_cb =
db->trace_udata =
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
db->update_hook_cb =
db->update_hook_udata =
db->commit_hook_cb =
db->commit_hook_udata =
db->rollback_hook_cb =
db->rollback_hook_udata =
#endif
LUA_NOREF;
luaL_getmetatable(L, sqlite_meta);
lua_setmetatable(L, -2); /* set metatable */
/* to keep track of 'open' virtual machines */
lua_pushlightuserdata(L, db);
lua_newtable(L);
lua_rawset(L, LUA_REGISTRYINDEX);
return db;
}
static int cleanupdb(lua_State *L, sdb *db) {
sdb_func *func;
sdb_func *func_next;
int top;
int result;
/* free associated virtual machines */
lua_pushlightuserdata(L, db);
lua_rawget(L, LUA_REGISTRYINDEX);
/* close all used handles */
top = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, -2)) {
sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */
cleanupvm(L, svm);
lua_settop(L, top);
lua_pushnil(L);
}
lua_pop(L, 1); /* pop vm table */
/* remove entry in lua registry table */
lua_pushlightuserdata(L, db);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
/* 'free' all references */
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
#endif
/* close database */
result = sqlite3_close(db->db);
db->db = NULL;
/* free associated memory with created functions */
func = db->func;
while (func) {
func_next = func->next;
luaL_unref(L, LUA_REGISTRYINDEX, func->fn_step);
luaL_unref(L, LUA_REGISTRYINDEX, func->fn_finalize);
luaL_unref(L, LUA_REGISTRYINDEX, func->udata);
c_free(func);
func = func_next;
}
db->func = NULL;
return result;
}
static sdb *lsqlite_getdb(lua_State *L, int index) {
sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta);
if (db == NULL) luaL_typerror(L, index, "invalid sqlite database");
return db;
}
static sdb *lsqlite_checkdb(lua_State *L, int index) {
sdb *db = lsqlite_getdb(L, index);
if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database");
return db;
}
/*
** =======================================================
** User Defined Functions - Context Methods
** =======================================================
*/
typedef struct {
sqlite3_context *ctx;
int ud;
} lcontext;
static lcontext *lsqlite_make_context(lua_State *L) {
lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext));
lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref);
lua_setmetatable(L, -2);
ctx->ctx = NULL;
ctx->ud = LUA_NOREF;
return ctx;
}
static lcontext *lsqlite_getcontext(lua_State *L, int index) {
lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta);
if (ctx == NULL) luaL_typerror(L, index, "sqlite context");
return ctx;
}
static lcontext *lsqlite_checkcontext(lua_State *L, int index) {
lcontext *ctx = lsqlite_getcontext(L, index);
if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context");
return ctx;
}
static int lcontext_tostring(lua_State *L) {
char buff[39];
lcontext *ctx = lsqlite_getcontext(L, 1);
if (ctx->ctx == NULL)
strcpy(buff, "closed");
else
c_sprintf(buff, "%p", ctx->ctx);
lua_pushfstring(L, "sqlite function context (%s)", buff);
return 1;
}
static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) {
sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx);
if (!func->aggregate) {
luaL_error(L, "attempt to call aggregate method from scalar function");
}
}
static int lcontext_user_data(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx);
lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata);
return 1;
}
static int lcontext_get_aggregate_context(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
lcontext_check_aggregate(L, ctx);
lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud);
return 1;
}
static int lcontext_set_aggregate_context(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
lcontext_check_aggregate(L, ctx);
lua_settop(L, 2);
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
ctx->ud = luaL_ref(L, LUA_REGISTRYINDEX);
return 0;
}
#if 0
static int lcontext_aggregate_count(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
lcontext_check_aggregate(L, ctx);
lua_pushinteger(L, sqlite3_aggregate_count(ctx->ctx));
return 1;
}
#else
static int lcontext_aggregate_count(lua_State *L) {
lua_pushliteral(L, "aggregate count support disabled at compile time");
lua_error(L);
return 0;
}
#endif
#if 0
void *sqlite3_get_auxdata(sqlite3_context*, int);
void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
#endif
static int lcontext_result(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
switch (lua_type(L, 2)) {
case LUA_TNUMBER:
#if LUA_VERSION_NUM > 502
if (lua_isinteger(L, 2))
sqlite3_result_int64(ctx->ctx, luaL_checkinteger(L, 2));
else
#endif
sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2));
break;
case LUA_TSTRING:
sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
break;
case LUA_TNIL:
case LUA_TNONE:
sqlite3_result_null(ctx->ctx);
break;
default:
luaL_error(L, "invalid result type %s", lua_typename(L, 2));
break;
}
return 0;
}
static int lcontext_result_blob(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *blob = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT);
return 0;
}
static int lcontext_result_double(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
double d = luaL_checknumber(L, 2);
sqlite3_result_double(ctx->ctx, d);
return 0;
}
static int lcontext_result_error(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *err = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
sqlite3_result_error(ctx->ctx, err, size);
return 0;
}
static int lcontext_result_int(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
int i = luaL_checkint(L, 2);
sqlite3_result_int(ctx->ctx, i);
return 0;
}
static int lcontext_result_null(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
sqlite3_result_null(ctx->ctx);
return 0;
}
static int lcontext_result_text(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *text = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT);
return 0;
}
/*
** =======================================================
** Database Methods
** =======================================================
*/
static int db_isopen(lua_State *L) {
sdb *db = lsqlite_getdb(L, 1);
lua_pushboolean(L, db->db != NULL ? 1 : 0);
return 1;
}
static int db_last_insert_rowid(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
/* conversion warning: int64 -> luaNumber */
sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db);
PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid));
return 1;
}
static int db_changes(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushinteger(L, sqlite3_changes(db->db));
return 1;
}
static int db_total_changes(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushinteger(L, sqlite3_total_changes(db->db));
return 1;
}
static int db_errcode(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushinteger(L, sqlite3_errcode(db->db));
return 1;
}
static int db_errmsg(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushstring(L, sqlite3_errmsg(db->db));
return 1;
}
static int db_interrupt(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
sqlite3_interrupt(db->db);
return 0;
}
static int db_db_filename(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
const char *db_name = luaL_checkstring(L, 2);
// sqlite3_db_filename may return NULL, in that case Lua pushes nil...
lua_pushstring(L, sqlite3_db_filename(db->db, db_name));
return 1;
}
/*
** Registering SQL functions:
*/
static void db_push_value(lua_State *L, sqlite3_value *value) {
switch (sqlite3_value_type(value)) {
case SQLITE_TEXT:
lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value));
break;
case SQLITE_INTEGER:
PUSH_INT64(L, sqlite3_value_int64(value)
, lua_pushlstring(L, (const char*)sqlite3_value_text(value)
, sqlite3_value_bytes(value)));
break;
case SQLITE_FLOAT:
lua_pushnumber(L, sqlite3_value_double(value));
break;
case SQLITE_BLOB:
lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value));
break;
case SQLITE_NULL:
lua_pushnil(L);
break;
default:
/* things done properly (SQLite + Lua SQLite)
** this should never happen */
lua_pushnil(L);
break;
}
}
/*
** callback functions used when calling registered sql functions
*/
/* scalar function to be called
** callback params: context, values... */
static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) {
sdb_func *func = (sdb_func*)sqlite3_user_data(context);
lua_State *L = func->db->L;
int n;
lcontext *ctx;
int top = lua_gettop(L);
/* ensure there is enough space in the stack */
lua_checkstack(L, argc + 3);
lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_step); /* function to call */
if (!func->aggregate) {
ctx = lsqlite_make_context(L); /* push context - used to set results */
}
else {
/* reuse context userdata value */
void *p = sqlite3_aggregate_context(context, 1);
/* i think it is OK to use assume that using a light user data
** as an entry on LUA REGISTRY table will be unique */
lua_pushlightuserdata(L, p);
lua_rawget(L, LUA_REGISTRYINDEX); /* context table */
if (lua_isnil(L, -1)) { /* not yet created? */
lua_pop(L, 1);
ctx = lsqlite_make_context(L);
lua_pushlightuserdata(L, p);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
else
ctx = lsqlite_getcontext(L, -1);
}
/* push params */
for (n = 0; n < argc; ++n) {
db_push_value(L, argv[n]);
}
/* set context */
ctx->ctx = context;
if (lua_pcall(L, argc + 1, 0, 0)) {
const char *errmsg = lua_tostring(L, -1);
int size = lua_strlen(L, -1);
sqlite3_result_error(context, errmsg, size);
}
/* invalidate context */
ctx->ctx = NULL;
if (!func->aggregate) {
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
}
lua_settop(L, top);
}
static void db_sql_finalize_function(sqlite3_context *context) {
sdb_func *func = (sdb_func*)sqlite3_user_data(context);
lua_State *L = func->db->L;
void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */
lcontext *ctx;
int top = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */
/* i think it is OK to use assume that using a light user data
** as an entry on LUA REGISTRY table will be unique */
lua_pushlightuserdata(L, p);
lua_rawget(L, LUA_REGISTRYINDEX); /* context table */
if (lua_isnil(L, -1)) { /* not yet created? - shouldn't happen in finalize function */
lua_pop(L, 1);
ctx = lsqlite_make_context(L);
lua_pushlightuserdata(L, p);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
else
ctx = lsqlite_getcontext(L, -1);
/* set context */
ctx->ctx = context;
if (lua_pcall(L, 1, 0, 0)) {
sqlite3_result_error(context, lua_tostring(L, -1), -1);
}
/* invalidate context */
ctx->ctx = NULL;
/* cleanup context */
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
/* remove it from registry */
lua_pushlightuserdata(L, p);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_settop(L, top);
}
/*
** Register a normal function
** Params: db, function name, number arguments, [ callback | step, finalize], user data
** Returns: true on sucess
**
** Normal function:
** Params: context, params
**
** Aggregate function:
** Params of step: context, params
** Params of finalize: context
*/
static int db_register_function(lua_State *L, int aggregate) {
sdb *db = lsqlite_checkdb(L, 1);
const char *name;
int args;
int result;
sdb_func *func;
/* safety measure */
if (aggregate) aggregate = 1;
name = luaL_checkstring(L, 2);
args = luaL_checkint(L, 3);
luaL_checktype(L, 4, LUA_TFUNCTION);
if (aggregate) luaL_checktype(L, 5, LUA_TFUNCTION);
/* maybe an alternative way to allocate memory should be used/avoided */
func = (sdb_func*)c_malloc(sizeof(sdb_func));
if (func == NULL) {
luaL_error(L, "out of memory");
}
result = sqlite3_create_function(
db->db, name, args, SQLITE_UTF8, func,
aggregate ? NULL : db_sql_normal_function,
aggregate ? db_sql_normal_function : NULL,
aggregate ? db_sql_finalize_function : NULL
);
if (result == SQLITE_OK) {
/* safety measures for userdata field to be present in the stack */
lua_settop(L, 5 + aggregate);
/* save registered function in db function list */
func->db = db;
func->aggregate = aggregate;
func->next = db->func;
db->func = func;
/* save the setp/normal function callback */
lua_pushvalue(L, 4);
func->fn_step = luaL_ref(L, LUA_REGISTRYINDEX);
/* save user data */
lua_pushvalue(L, 5+aggregate);
func->udata = luaL_ref(L, LUA_REGISTRYINDEX);
if (aggregate) {
lua_pushvalue(L, 5);
func->fn_finalize = luaL_ref(L, LUA_REGISTRYINDEX);
}
else
func->fn_finalize = LUA_NOREF;
}
else {
/* free allocated memory */
c_free(func);
}
lua_pushboolean(L, result == SQLITE_OK ? 1 : 0);
return 1;
}
static int db_create_function(lua_State *L) {
return db_register_function(L, 0);
}
static int db_create_aggregate(lua_State *L) {
return db_register_function(L, 1);
}
/* create_collation; contributed by Thomas Lauer
*/
typedef struct {
lua_State *L;
int ref;
} scc;
static int collwrapper(scc *co,int l1,const void *p1,
int l2,const void *p2) {
int res=0;
lua_State *L=co->L;
lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref);
lua_pushlstring(L,p1,l1);
lua_pushlstring(L,p2,l2);
if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1);
lua_pop(L,1);
return res;
}
static void collfree(scc *co) {
if (co) {
luaL_unref(co->L,LUA_REGISTRYINDEX,co->ref);
c_free(co);
}
}
static int db_create_collation(lua_State *L) {
sdb *db=lsqlite_checkdb(L,1);
const char *collname=luaL_checkstring(L,2);
scc *co=NULL;
int (*collfunc)(scc *,int,const void *,int,const void *)=NULL;
lua_settop(L,3); /* default args to nil, and exclude extras */
if (lua_isfunction(L,3)) collfunc=collwrapper;
else if (!lua_isnil(L,3))
luaL_error(L,"create_collation: function or nil expected");
if (collfunc != NULL) {
co=(scc *)c_malloc(sizeof(scc)); /* userdata is a no-no as it
will be garbage-collected */
if (co) {
co->L=L;
/* lua_settop(L,3) above means we don't need: lua_pushvalue(L,3); */
co->ref=luaL_ref(L,LUA_REGISTRYINDEX);
}
else luaL_error(L,"create_collation: could not allocate callback");
}
sqlite3_create_collation_v2(db->db, collname, SQLITE_UTF8,
(void *)co,
(int(*)(void*,int,const void*,int,const void*))collfunc,
(void(*)(void*))collfree);
return 0;
}
/* Thanks to Wolfgang Oertl...
*/
#if 0
static int db_load_extension(lua_State *L) {
sdb *db=lsqlite_checkdb(L,1);
const char *extname=luaL_optstring(L,2,NULL);
const char *entrypoint=luaL_optstring(L,3,NULL);
int result;
char *errmsg = NULL;
if (extname == NULL) {
result = sqlite3_enable_load_extension(db->db,0); /* disable extension loading */
}
else {
sqlite3_enable_load_extension(db->db,1); /* enable extension loading */
result = sqlite3_load_extension(db->db,extname,entrypoint,&errmsg);
}
if (result == SQLITE_OK) {
lua_pushboolean(L,1);
return 1;
}
lua_pushboolean(L,0); /* so, assert(load_extension(...)) works */
lua_pushstring(L,errmsg);
sqlite3_free(errmsg);
return 2;
}
#else
static int db_load_extension(lua_State *L) {
lua_pushliteral(L, "db_load_extension support disabled at compile time");
lua_error(L);
return 0;
}
#endif
/*
** trace callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata, sql
*/
static void db_trace_callback(void *user, const char *sql) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_udata); /* get callback user data */
lua_pushstring(L, sql); /* traced sql statement */
/* call lua function */
lua_pcall(L, 2, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
#if 0
static int db_trace(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
db->trace_cb =
db->trace_udata = LUA_NOREF;
/* clear trace handler */
sqlite3_trace(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
db->trace_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->trace_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set trace handler */
sqlite3_trace(db->db, db_trace_callback, db);
}
return 0;
}
#else
static int db_trace(lua_State *L) {
lua_pushliteral(L, "db_trace support disabled at compile time");
lua_error(L);
return 0;
}
#endif
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
/*
** update_hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE},
** database name, table name (containing the affected row), rowid of the row
*/
static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
lua_Number n;
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */
lua_pushinteger(L, op);
lua_pushstring(L, dbname); /* update_hook database name */
lua_pushstring(L, tblname); /* update_hook database name */
PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid));
/* call lua function */
lua_pcall(L, 5, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
static int db_update_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
db->update_hook_cb =
db->update_hook_udata = LUA_NOREF;
/* clear update_hook handler */
sqlite3_update_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
db->update_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->update_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set update_hook handler */
sqlite3_update_hook(db->db, db_update_hook_callback, db);
}
return 0;
}
/*
** commit_hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata
** Returned value: Return false or nil to continue the COMMIT operation normally.
** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK.
*/
static int db_commit_hook_callback(void *user) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
int rollback = 0;
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_udata); /* get callback user data */
/* call lua function */
if (!lua_pcall(L, 1, 1, 0))
rollback = lua_toboolean(L, -1); /* use result if there was no error */
lua_settop(L, top);
return rollback;
}
static int db_commit_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
db->commit_hook_cb =
db->commit_hook_udata = LUA_NOREF;
/* clear commit_hook handler */
sqlite3_commit_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
db->commit_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->commit_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set commit_hook handler */
sqlite3_commit_hook(db->db, db_commit_hook_callback, db);
}
return 0;
}
/*
** rollback hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata
*/
static void db_rollback_hook_callback(void *user) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); /* get callback user data */
/* call lua function */
lua_pcall(L, 1, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
static int db_rollback_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
db->rollback_hook_cb =
db->rollback_hook_udata = LUA_NOREF;
/* clear rollback_hook handler */
sqlite3_rollback_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
db->rollback_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->rollback_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set rollback_hook handler */
sqlite3_rollback_hook(db->db, db_rollback_hook_callback, db);
}
return 0;
}
#endif /* #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK */
#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK
/*
** progress handler:
** Params: database, number of opcodes, callback function, userdata
**
** callback function:
** Params: userdata
** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue
*/
static int db_progress_callback(void *user) {
int result = 1; /* abort by default */
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata);
/* call lua function */
if (!lua_pcall(L, 1, 1, 0))
result = lua_toboolean(L, -1);
lua_settop(L, top);
return result;
}
static int db_progress_handler(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
db->progress_cb =
db->progress_udata = LUA_NOREF;
/* clear busy handler */
sqlite3_progress_handler(db->db, 0, NULL, NULL);
}
else {
int nop = luaL_checkint(L, 2); /* number of opcodes */
luaL_checktype(L, 3, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 4);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
db->progress_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->progress_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set progress callback */
sqlite3_progress_handler(db->db, nop, db_progress_callback, db);
}
return 0;
}
#else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */
static int db_progress_handler(lua_State *L) {
lua_pushliteral(L, "progress callback support disabled at compile time");
lua_error(L);
return 0;
}
#endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */
/* Online Backup API */
#if 0
sqlite3_backup *sqlite3_backup_init(
sqlite3 *pDest, /* Destination database handle */
const char *zDestName, /* Destination database name */
sqlite3 *pSource, /* Source database handle */
const char *zSourceName /* Source database name */
);
int sqlite3_backup_step(sqlite3_backup *p, int nPage);
int sqlite3_backup_finish(sqlite3_backup *p);
int sqlite3_backup_remaining(sqlite3_backup *p);
int sqlite3_backup_pagecount(sqlite3_backup *p);
#endif
struct sdb_bu {
sqlite3_backup *bu; /* backup structure */
};
static int cleanupbu(lua_State *L, sdb_bu *sbu) {
if (!sbu->bu) return 0; /* already finished */
/* remove table from registry */
lua_pushlightuserdata(L, sbu->bu);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_pushinteger(L, sqlite3_backup_finish(sbu->bu));
sbu->bu = NULL;
return 1;
}
static int lsqlite_backup_init(lua_State *L) {
sdb *target_db = lsqlite_checkdb(L, 1);
const char *target_nm = luaL_checkstring(L, 2);
sdb *source_db = lsqlite_checkdb(L, 3);
const char *source_nm = luaL_checkstring(L, 4);
sqlite3_backup *bu = sqlite3_backup_init(target_db->db, target_nm, source_db->db, source_nm);
if (NULL != bu) {
sdb_bu *sbu = (sdb_bu*)lua_newuserdata(L, sizeof(sdb_bu));
luaL_getmetatable(L, sqlite_bu_meta);
lua_setmetatable(L, -2); /* set metatable */
sbu->bu = bu;
/* create table from registry */
/* to prevent referenced databases from being garbage collected while bu is live */
lua_pushlightuserdata(L, bu);
lua_createtable(L, 2, 0);
/* add source and target dbs to table at indices 1 and 2 */
lua_pushvalue(L, 1); /* target db */
lua_rawseti(L, -2, 1);
lua_pushvalue(L, 3); /* source db */
lua_rawseti(L, -2, 2);
/* put table in registry with key lightuserdata bu */
lua_rawset(L, LUA_REGISTRYINDEX);
return 1;
}
else {
return 0;
}
}
static sdb_bu *lsqlite_getbu(lua_State *L, int index) {
sdb_bu *sbu = (sdb_bu*)luaL_checkudata(L, index, sqlite_bu_meta);
if (sbu == NULL) luaL_typerror(L, index, "sqlite database backup");
return sbu;
}
static sdb_bu *lsqlite_checkbu(lua_State *L, int index) {
sdb_bu *sbu = lsqlite_getbu(L, index);
if (sbu->bu == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database backup");
return sbu;
}
static int dbbu_gc(lua_State *L) {
sdb_bu *sbu = lsqlite_getbu(L, 1);
if (sbu->bu != NULL) {
cleanupbu(L, sbu);
lua_pop(L, 1);
}
/* else ignore if already finished */
return 0;
}
static int dbbu_step(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
int nPage = luaL_checkint(L, 2);
lua_pushinteger(L, sqlite3_backup_step(sbu->bu, nPage));
return 1;
}
static int dbbu_remaining(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
lua_pushinteger(L, sqlite3_backup_remaining(sbu->bu));
return 1;
}
static int dbbu_pagecount(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
lua_pushinteger(L, sqlite3_backup_pagecount(sbu->bu));
return 1;
}
static int dbbu_finish(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
return cleanupbu(L, sbu);
}
/* end of Online Backup API */
/*
** busy handler:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata, number of tries
** returns: 0 to return immediatly and return SQLITE_BUSY, non-zero to try again
*/
static int db_busy_callback(void *user, int tries) {
int retry = 0; /* abort by default */
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata);
lua_pushinteger(L, tries);
/* call lua function */
if (!lua_pcall(L, 2, 1, 0))
retry = lua_toboolean(L, -1);
lua_settop(L, top);
return retry;
}
static int db_busy_handler(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
db->busy_cb =
db->busy_udata = LUA_NOREF;
/* clear busy handler */
sqlite3_busy_handler(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
db->busy_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->busy_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set busy handler */
sqlite3_busy_handler(db->db, db_busy_callback, db);
}
return 0;
}
static int db_busy_timeout(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
int timeout = luaL_checkint(L, 2);
sqlite3_busy_timeout(db->db, timeout);
/* if there was a timeout callback registered, it is now
** invalid/useless. free any references we may have */
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
db->busy_cb =
db->busy_udata = LUA_NOREF;
return 0;
}
/*
** Params: db, sql, callback, user
** returns: code [, errmsg]
**
** Callback:
** Params: user, number of columns, values, names
** Returns: 0 to continue, other value will cause abort
*/
static int db_exec_callback(void* user, int columns, char **data, char **names) {
int result = SQLITE_ABORT; /* abort by default */
lua_State *L = (lua_State*)user;
int n;
int top = lua_gettop(L);
lua_pushvalue(L, 3); /* function to call */
lua_pushvalue(L, 4); /* user data */
lua_pushinteger(L, columns); /* total number of rows in result */
/* column values */
lua_pushvalue(L, 6);
for (n = 0; n < columns;) {
lua_pushstring(L, data[n++]);
lua_rawseti(L, -2, n);
}
/* columns names */
lua_pushvalue(L, 5);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_createtable(L, columns, 0);
lua_pushvalue(L, -1);
lua_replace(L, 5);
for (n = 0; n < columns;) {
lua_pushstring(L, names[n++]);
lua_rawseti(L, -2, n);
}
}
/* call lua function */
if (!lua_pcall(L, 4, 1, 0)) {
#if LUA_VERSION_NUM > 502
if (lua_isinteger(L, -1))
result = lua_tointeger(L, -1);
else
#endif
if (lua_isnumber(L, -1))
result = lua_tonumber(L, -1);
}
lua_settop(L, top);
return result;
}
static int db_exec(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
const char *sql = luaL_checkstring(L, 2);
int result;
if (!lua_isnoneornil(L, 3)) {
/* stack:
** 3: callback function
** 4: userdata
** 5: column names
** 6: reusable column values
*/
luaL_checktype(L, 3, LUA_TFUNCTION);
lua_settop(L, 4); /* 'trap' userdata - nil extra parameters */
lua_pushnil(L); /* column names not known at this point */
lua_newtable(L); /* column values table */
result = sqlite3_exec(db->db, sql, db_exec_callback, L, NULL);
}
else {
/* no callbacks */
result = sqlite3_exec(db->db, sql, NULL, NULL, NULL);
}
lua_pushinteger(L, result);
return 1;
}
/*
** Params: db, sql
** returns: code, compiled length or error message
*/
static int db_prepare(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
const char *sql = luaL_checkstring(L, 2);
int sql_len = lua_strlen(L, 2);
const char *sqltail;
sdb_vm *svm;
lua_settop(L,2); /* db,sql is on top of stack for call to newvm */
svm = newvm(L, db);
if (sqlite3_prepare_v2(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) {
lua_pushnil(L);
lua_pushinteger(L, sqlite3_errcode(db->db));
if (cleanupvm(L, svm) == 1)
lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */
return 2;
}
/* vm already in the stack */
lua_pushstring(L, sqltail);
return 2;
}
static int db_do_next_row(lua_State *L, int packed) {
int result;
sdb_vm *svm = lsqlite_checkvm(L, 1);
sqlite3_stmt *vm;
int columns;
int i;
result = stepvm(L, svm);
vm = svm->vm; /* stepvm may change svm->vm if re-prepare is needed */
svm->has_values = result == SQLITE_ROW ? 1 : 0;
svm->columns = columns = sqlite3_data_count(vm);
if (result == SQLITE_ROW) {
if (packed) {
if (packed == 1) {
lua_createtable(L, columns, 0);
for (i = 0; i < columns;) {
vm_push_column(L, vm, i);
lua_rawseti(L, -2, ++i);
}
}
else {
lua_createtable(L, 0, columns);
for (i = 0; i < columns; ++i) {
lua_pushstring(L, sqlite3_column_name(vm, i));
vm_push_column(L, vm, i);
lua_rawset(L, -3);
}
}
return 1;
}
else {
lua_checkstack(L, columns);
for (i = 0; i < columns; ++i)
vm_push_column(L, vm, i);
return svm->columns;
}
}
if (svm->temp) {
/* finalize and check for errors */
result = sqlite3_finalize(vm);
svm->vm = NULL;
cleanupvm(L, svm);
}
else if (result == SQLITE_DONE) {
result = sqlite3_reset(vm);
}
if (result != SQLITE_OK) {
lua_pushstring(L, sqlite3_errmsg(svm->db->db));
lua_error(L);
}
return 0;
}
static int db_next_row(lua_State *L) {
return db_do_next_row(L, 0);
}
static int db_next_packed_row(lua_State *L) {
return db_do_next_row(L, 1);
}
static int db_next_named_row(lua_State *L) {
return db_do_next_row(L, 2);
}
static int dbvm_do_rows(lua_State *L, int(*f)(lua_State *)) {
/* sdb_vm *svm = */
lsqlite_checkvm(L, 1);
lua_pushvalue(L,1);
lua_pushcfunction(L, f);
lua_insert(L, -2);
return 2;
}
static int dbvm_rows(lua_State *L) {
return dbvm_do_rows(L, db_next_packed_row);
}
static int dbvm_nrows(lua_State *L) {
return dbvm_do_rows(L, db_next_named_row);
}
static int dbvm_urows(lua_State *L) {
return dbvm_do_rows(L, db_next_row);
}
static int db_do_rows(lua_State *L, int(*f)(lua_State *)) {
sdb *db = lsqlite_checkdb(L, 1);
const char *sql = luaL_checkstring(L, 2);
sdb_vm *svm;
lua_settop(L,2); /* db,sql is on top of stack for call to newvm */
svm = newvm(L, db);
svm->temp = 1;
if (sqlite3_prepare_v2(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) {
lua_pushstring(L, sqlite3_errmsg(svm->db->db));
if (cleanupvm(L, svm) == 1)
lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */
lua_error(L);
}
lua_pushcfunction(L, f);
lua_insert(L, -2);
return 2;
}
static int db_rows(lua_State *L) {
return db_do_rows(L, db_next_packed_row);
}
static int db_nrows(lua_State *L) {
return db_do_rows(L, db_next_named_row);
}
/* unpacked version of db:rows */
static int db_urows(lua_State *L) {
return db_do_rows(L, db_next_row);
}
static int db_tostring(lua_State *L) {
char buff[32];
sdb *db = lsqlite_getdb(L, 1);
if (db->db == NULL)
strcpy(buff, "closed");
else
c_sprintf(buff, "%p", lua_touserdata(L, 1));
lua_pushfstring(L, "sqlite database (%s)", buff);
return 1;
}
static int db_close(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushinteger(L, cleanupdb(L, db));
return 1;
}
static int db_close_vm(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
/* cleanup temporary only tables? */
int temp = lua_toboolean(L, 2);
/* free associated virtual machines */
lua_pushlightuserdata(L, db);
lua_rawget(L, LUA_REGISTRYINDEX);
/* close all used handles */
lua_pushnil(L);
while (lua_next(L, -2)) {
sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */
if ((!temp || svm->temp) && svm->vm)
{
sqlite3_finalize(svm->vm);
svm->vm = NULL;
}
/* leave key in the stack */
lua_pop(L, 1);
}
return 0;
}
/* From: Wolfgang Oertl
When using lsqlite3 in a multithreaded environment, each thread has a separate Lua
environment, but full userdata structures can't be passed from one thread to another.
This is possible with lightuserdata, however. See: lsqlite_open_ptr().
*/
static int db_get_ptr(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushlightuserdata(L, db->db);
return 1;
}
static int db_gc(lua_State *L) {
sdb *db = lsqlite_getdb(L, 1);
if (db->db != NULL) /* ignore closed databases */
cleanupdb(L, db);
return 0;
}
/*
** =======================================================
** General library functions
** =======================================================
*/
static int lsqlite_version(lua_State *L) {
lua_pushstring(L, sqlite3_libversion());
return 1;
}
static int lsqlite_complete(lua_State *L) {
const char *sql = luaL_checkstring(L, 1);
lua_pushboolean(L, sqlite3_complete(sql));
return 1;
}
#if 0
static int lsqlite_complete(lua_State *L) {
lua_pushliteral(L, "sql complete support disabled at compile time");
lua_error(L);
return 0;
}
#endif
#ifndef _WIN32
static int lsqlite_temp_directory(lua_State *L) {
const char *oldtemp = sqlite3_temp_directory;
if (!lua_isnone(L, 1)) {
const char *temp = luaL_optstring(L, 1, NULL);
if (sqlite3_temp_directory) {
sqlite3_free((char*)sqlite3_temp_directory);
}
if (temp) {
sqlite3_temp_directory = sqlite3_mprintf("%s", temp);
}
else {
sqlite3_temp_directory = NULL;
}
}
lua_pushstring(L, oldtemp);
return 1;
}
#endif
static int lsqlite_do_open(lua_State *L, const char *filename, int flags) {
sdb *db = newdb(L); /* create and leave in stack */
if (SQLITE3_OPEN(filename, &db->db, flags) == SQLITE_OK) {
/* database handle already in the stack - return it */
return 1;
}
/* failed to open database */
lua_pushnil(L); /* push nil */
lua_pushinteger(L, sqlite3_errcode(db->db));
lua_pushstring(L, sqlite3_errmsg(db->db)); /* push error message */
/* clean things up */
cleanupdb(L, db);
/* return */
return 3;
}
static int lsqlite_open(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
int flags = luaL_optinteger(L, 2, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
return lsqlite_do_open(L, filename, flags);
}
static int lsqlite_open_memory(lua_State *L) {
return lsqlite_do_open(L, ":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
}
/* From: Wolfgang Oertl
When using lsqlite3 in a multithreaded environment, each thread has a separate Lua
environment, but full userdata structures can't be passed from one thread to another.
This is possible with lightuserdata, however. See: db_get_ptr().
*/
static int lsqlite_open_ptr(lua_State *L) {
sqlite3 *db_ptr;
sdb *db;
int rc;
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
db_ptr = lua_touserdata(L, 1);
/* This is the only API function that runs sqlite3SafetyCheck regardless of
* SQLITE_ENABLE_API_ARMOR and does almost nothing (without an SQL
* statement) */
rc = sqlite3_exec(db_ptr, NULL, NULL, NULL, NULL);
if (rc != SQLITE_OK)
luaL_argerror(L, 1, "not a valid SQLite3 pointer");
db = newdb(L); /* create and leave in stack */
db->db = db_ptr;
return 1;
}
#ifndef LSQLITE_VERSION
/* should be defined in rockspec, but just in case... */
#define LSQLITE_VERSION "unknown"
#endif
/* Version number of this library
*/
static int lsqlite_lversion(lua_State *L) {
lua_pushstring(L, LSQLITE_VERSION);
return 1;
}
/*
** =======================================================
** Register functions
** =======================================================
*/
/* ======================================================= */
static const LUA_REG_TYPE dblib[] = {
{ LSTRKEY( "isopen" ), LFUNCVAL( db_isopen ) },
{ LSTRKEY( "last_insert_rowid" ), LFUNCVAL ( db_last_insert_rowid ) },
{ LSTRKEY( "changes" ), LFUNCVAL ( db_changes ) },
{ LSTRKEY( "total_changes" ), LFUNCVAL ( db_total_changes ) },
{ LSTRKEY( "errcode" ), LFUNCVAL ( db_errcode ) },
{ LSTRKEY( "error_code" ), LFUNCVAL ( db_errcode ) },
{ LSTRKEY( "errmsg" ), LFUNCVAL ( db_errmsg ) },
{ LSTRKEY( "error_message" ), LFUNCVAL ( db_errmsg ) },
{ LSTRKEY( "interrupt" ), LFUNCVAL ( db_interrupt ) },
{ LSTRKEY( "db_filename" ), LFUNCVAL ( db_db_filename ) },
{ LSTRKEY( "create_function" ), LFUNCVAL ( db_create_function ) },
{ LSTRKEY( "create_aggregate" ), LFUNCVAL ( db_create_aggregate ) },
{ LSTRKEY( "create_collation" ), LFUNCVAL ( db_create_collation ) },
{ LSTRKEY( "load_extension" ), LFUNCVAL ( db_load_extension ) },
{ LSTRKEY( "trace" ), LFUNCVAL ( db_trace ) },
{ LSTRKEY( "progress_handler" ), LFUNCVAL ( db_progress_handler ) },
{ LSTRKEY( "busy_timeout" ), LFUNCVAL ( db_busy_timeout ) },
{ LSTRKEY( "busy_handler" ), LFUNCVAL ( db_busy_handler ) },
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
{ LSTRKEY( "update_hook" ), LFUNCVAL ( db_update_hook ) },
{ LSTRKEY( "commit_hook" ), LFUNCVAL ( db_commit_hook ) },
{ LSTRKEY( "rollback_hook" ), LFUNCVAL ( db_rollback_hook ) },
#endif
{ LSTRKEY( "prepare" ), LFUNCVAL ( db_prepare ) },
{ LSTRKEY( "rows" ), LFUNCVAL ( db_rows ) },
{ LSTRKEY( "urows" ), LFUNCVAL ( db_urows ) },
{ LSTRKEY( "nrows" ), LFUNCVAL ( db_nrows ) },
{ LSTRKEY( "exec" ), LFUNCVAL ( db_exec ) },
{ LSTRKEY( "execute" ), LFUNCVAL ( db_exec ) },
{ LSTRKEY( "close" ), LFUNCVAL ( db_close ) },
{ LSTRKEY( "close_vm" ), LFUNCVAL ( db_close_vm ) },
{ LSTRKEY( "get_ptr" ), LFUNCVAL ( db_get_ptr ) },
{ LSTRKEY( "__tostring" ), LFUNCVAL ( db_tostring ) },
{ LSTRKEY( "__gc" ), LFUNCVAL ( db_gc ) },
{ LSTRKEY( "__index" ), LROVAL ( dblib ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE vmlib[] = {
{ LSTRKEY( "isopen" ), LFUNCVAL ( dbvm_isopen ) },
{ LSTRKEY( "step" ), LFUNCVAL ( dbvm_step ) },
{ LSTRKEY( "reset" ), LFUNCVAL ( dbvm_reset ) },
{ LSTRKEY( "finalize" ), LFUNCVAL ( dbvm_finalize ) },
{ LSTRKEY( "columns" ), LFUNCVAL ( dbvm_columns ) },
{ LSTRKEY( "bind" ), LFUNCVAL ( dbvm_bind ) },
{ LSTRKEY( "bind_values" ), LFUNCVAL ( dbvm_bind_values ) },
{ LSTRKEY( "bind_names" ), LFUNCVAL ( dbvm_bind_names ) },
{ LSTRKEY( "bind_blob" ), LFUNCVAL ( dbvm_bind_blob ) },
{ LSTRKEY( "bind_parameter_count" ),LFUNCVAL ( dbvm_bind_parameter_count )},
{ LSTRKEY( "bind_parameter_name" ), LFUNCVAL ( dbvm_bind_parameter_name )},
{ LSTRKEY( "get_value" ), LFUNCVAL ( dbvm_get_value ) },
{ LSTRKEY( "get_values" ), LFUNCVAL ( dbvm_get_values ) },
{ LSTRKEY( "get_name" ), LFUNCVAL ( dbvm_get_name ) },
{ LSTRKEY( "get_names" ), LFUNCVAL ( dbvm_get_names ) },
{ LSTRKEY( "get_type" ), LFUNCVAL ( dbvm_get_type ) },
{ LSTRKEY( "get_types" ), LFUNCVAL ( dbvm_get_types ) },
{ LSTRKEY( "get_uvalues" ), LFUNCVAL ( dbvm_get_uvalues ) },
{ LSTRKEY( "get_unames" ), LFUNCVAL ( dbvm_get_unames ) },
{ LSTRKEY( "get_utypes" ), LFUNCVAL ( dbvm_get_utypes ) },
{ LSTRKEY( "get_named_values" ), LFUNCVAL ( dbvm_get_named_values ) },
{ LSTRKEY( "get_named_types" ), LFUNCVAL ( dbvm_get_named_types ) },
{ LSTRKEY( "rows" ), LFUNCVAL ( dbvm_rows ) },
{ LSTRKEY( "urows" ), LFUNCVAL ( dbvm_urows ) },
{ LSTRKEY( "nrows" ), LFUNCVAL ( dbvm_nrows ) },
{ LSTRKEY( "last_insert_rowid" ), LFUNCVAL ( dbvm_last_insert_rowid ) },
/* compatibility names (added by request) */
{ LSTRKEY( "idata" ), LFUNCVAL ( dbvm_get_values ) },
{ LSTRKEY( "inames" ), LFUNCVAL ( dbvm_get_names ) },
{ LSTRKEY( "itypes" ), LFUNCVAL ( dbvm_get_types ) },
{ LSTRKEY( "data" ), LFUNCVAL ( dbvm_get_named_values ) },
{ LSTRKEY( "type" ), LFUNCVAL ( dbvm_get_named_types ) },
{ LSTRKEY( "__tostring" ), LFUNCVAL ( dbvm_tostring ) },
{ LSTRKEY( "__gc" ), LFUNCVAL ( dbvm_gc ) },
{ LSTRKEY( "__index" ), LROVAL ( vmlib ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE ctxlib[] = {
{ LSTRKEY( "user_data" ), LFUNCVAL ( lcontext_user_data ) },
{ LSTRKEY( "get_aggregate_data" ), LFUNCVAL ( lcontext_get_aggregate_context ) },
{ LSTRKEY( "set_aggregate_data" ), LFUNCVAL ( lcontext_set_aggregate_context ) },
{ LSTRKEY( "aggregate_count" ), LFUNCVAL ( lcontext_aggregate_count ) },
{ LSTRKEY( "result" ), LFUNCVAL ( lcontext_result ) },
{ LSTRKEY( "result_null" ), LFUNCVAL ( lcontext_result_null ) },
{ LSTRKEY( "result_number" ), LFUNCVAL ( lcontext_result_double ) },
{ LSTRKEY( "result_double" ), LFUNCVAL ( lcontext_result_double ) },
{ LSTRKEY( "result_int" ), LFUNCVAL ( lcontext_result_int ) },
{ LSTRKEY( "result_text" ), LFUNCVAL ( lcontext_result_text ) },
{ LSTRKEY( "result_blob" ), LFUNCVAL ( lcontext_result_blob ) },
{ LSTRKEY( "result_error" ), LFUNCVAL ( lcontext_result_error ) },
{ LSTRKEY( "__tostring" ), LFUNCVAL ( lcontext_tostring ) },
{ LSTRKEY( "__index" ), LROVAL ( ctxlib ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE dbbulib[] = {
{ LSTRKEY( "step" ), LFUNCVAL ( dbbu_step ) },
{ LSTRKEY( "remaining" ), LFUNCVAL ( dbbu_remaining ) },
{ LSTRKEY( "pagecount" ), LFUNCVAL ( dbbu_pagecount ) },
{ LSTRKEY( "finish" ), LFUNCVAL ( dbbu_finish ) },
// { LSTRKEY( "__tostring" ), LFUNCVAL ( dbbu_tostring ) },
{ LSTRKEY( "__gc" ), LFUNCVAL ( dbbu_gc ) },
{ LSTRKEY( "__index" ), LROVAL ( dbbulib ) },
{ LNILKEY, LNILVAL }
};
#define SC(s) { LSTRKEY( #s ), LNUMVAL ( SQLITE_ ## s ) },
#define LSC(s) { LSTRKEY( #s ), LNUMVAL ( LSQLITE_ ## s ) },
static const LUA_REG_TYPE sqlitelib[] = {
{ LSTRKEY( "lversion" ), LFUNCVAL ( lsqlite_lversion ) },
{ LSTRKEY( "version" ), LFUNCVAL ( lsqlite_version ) },
{ LSTRKEY( "complete" ), LFUNCVAL ( lsqlite_complete ) },
#ifndef _WIN32
{ LSTRKEY( "temp_directory" ), LFUNCVAL ( lsqlite_temp_directory ) },
#endif
{ LSTRKEY( "open" ), LFUNCVAL ( lsqlite_open ) },
{ LSTRKEY( "open_memory" ), LFUNCVAL ( lsqlite_open_memory ) },
{ LSTRKEY( "open_ptr" ), LFUNCVAL ( lsqlite_open_ptr ) },
{ LSTRKEY( "backup_init" ), LFUNCVAL ( lsqlite_backup_init ) },
SC(OK) SC(ERROR) SC(INTERNAL) SC(PERM)
SC(ABORT) SC(BUSY) SC(LOCKED) SC(NOMEM)
SC(READONLY) SC(INTERRUPT) SC(IOERR) SC(CORRUPT)
SC(NOTFOUND) SC(FULL) SC(CANTOPEN) SC(PROTOCOL)
SC(EMPTY) SC(SCHEMA) SC(TOOBIG) SC(CONSTRAINT)
SC(MISMATCH) SC(MISUSE) SC(NOLFS)
SC(FORMAT) SC(NOTADB)
/* sqlite_step specific return values */
SC(RANGE) SC(ROW) SC(DONE)
/* column types */
SC(INTEGER) SC(FLOAT) SC(TEXT) SC(BLOB)
SC(NULL)
/* Authorizer Action Codes */
SC(CREATE_INDEX )
SC(CREATE_TABLE )
SC(CREATE_TEMP_INDEX )
SC(CREATE_TEMP_TABLE )
SC(CREATE_TEMP_TRIGGER)
SC(CREATE_TEMP_VIEW )
SC(CREATE_TRIGGER )
SC(CREATE_VIEW )
SC(DELETE )
SC(DROP_INDEX )
SC(DROP_TABLE )
SC(DROP_TEMP_INDEX )
SC(DROP_TEMP_TABLE )
SC(DROP_TEMP_TRIGGER )
SC(DROP_TEMP_VIEW )
SC(DROP_TRIGGER )
SC(DROP_VIEW )
SC(INSERT )
SC(PRAGMA )
SC(READ )
SC(SELECT )
SC(TRANSACTION )
SC(UPDATE )
SC(ATTACH )
SC(DETACH )
SC(ALTER_TABLE )
SC(REINDEX )
SC(ANALYZE )
SC(CREATE_VTABLE )
SC(DROP_VTABLE )
SC(FUNCTION )
SC(SAVEPOINT )
/* file open flags */
SC(OPEN_READONLY)
SC(OPEN_READWRITE)
SC(OPEN_CREATE)
SC(OPEN_URI)
SC(OPEN_MEMORY)
SC(OPEN_NOMUTEX)
SC(OPEN_FULLMUTEX)
SC(OPEN_SHAREDCACHE)
SC(OPEN_PRIVATECACHE)
{ LNILKEY, LNILVAL }
};
LUALIB_API int luaopen_lsqlite3(lua_State *L) {
luaL_rometatable(L, sqlite_meta, (void *)dblib);
luaL_rometatable(L, sqlite_vm_meta, (void *)vmlib);
luaL_rometatable(L, sqlite_bu_meta, (void *)dbbulib);
luaL_rometatable(L, sqlite_ctx_meta, (void *)ctxlib);
return 1;
}
NODEMCU_MODULE(SQLITE3, "sqlite3", sqlitelib, luaopen_lsqlite3);
......@@ -15,9 +15,13 @@
#include "mem.h"
#include "lwip/ip_addr.h"
#include "espconn.h"
#include "sys/espconn_mbedtls.h"
#include "lwip/err.h"
#include "lwip/dns.h"
#include "mbedtls/debug.h"
#include "user_mbedtls.h"
#ifdef HAVE_SSL_SERVER_CRT
#include HAVE_SSL_SERVER_CRT
#else
......@@ -611,6 +615,13 @@ static int tls_cert_verify(lua_State *L)
return 1;
}
#if defined(MBEDTLS_DEBUG_C)
static int tls_set_debug_threshold(lua_State *L) {
mbedtls_debug_set_threshold(luaL_checkint( L, 1 ));
return 0;
}
#endif
static const LUA_REG_TYPE tls_socket_map[] = {
{ LSTRKEY( "connect" ), LFUNCVAL( tls_socket_connect ) },
{ LSTRKEY( "close" ), LFUNCVAL( tls_socket_close ) },
......@@ -634,6 +645,9 @@ const LUA_REG_TYPE tls_cert_map[] = {
static const LUA_REG_TYPE tls_map[] = {
{ LSTRKEY( "createConnection" ), LFUNCVAL( tls_socket_create ) },
#if defined(MBEDTLS_DEBUG_C)
{ LSTRKEY( "setDebug" ), LFUNCVAL( tls_set_debug_threshold ) },
#endif
{ LSTRKEY( "cert" ), LROVAL( tls_cert_map ) },
{ LSTRKEY( "__metatable" ), LROVAL( tls_map ) },
{ LNILKEY, LNILVAL }
......
......@@ -128,9 +128,9 @@ static int tmr_delay( lua_State* L ){
sint32_t us = luaL_checkinteger(L, 1);
if(us <= 0)
return luaL_error(L, "wrong arg range");
while(us >= 10000){
us -= 10000;
os_delay_us(10000);
while(us >= 1000000){
us -= 1000000;
os_delay_us(1000000);
system_soft_wdt_feed ();
}
if(us>0){
......
......@@ -215,6 +215,124 @@ static int wifi_exit_smart( lua_State* L )
}
#endif // WIFI_SMART_ENABLE
// Lua: wifi.getcountry()
static int wifi_getcountry( lua_State* L ){
wifi_country_t cfg = {0};
if(wifi_get_country(&cfg)){
lua_newtable(L);
lua_pushstring(L, "country");
lua_pushstring(L, cfg.cc);
lua_rawset(L, -3);
lua_pushstring(L, "start_ch");
lua_pushnumber(L, cfg.schan);
lua_rawset(L, -3);
lua_pushstring(L, "end_ch");
lua_pushnumber(L, (cfg.schan + cfg.nchan)-1);
lua_rawset(L, -3);
lua_pushstring(L, "policy");
lua_pushnumber(L, cfg.policy);
lua_rawset(L, -3);
return 1;
}
else{
return luaL_error(L, "Unable to get country info");
}
}
// Lua: wifi.setcountry()
static int wifi_setcountry( lua_State* L ){
size_t len;
uint8 start_ch;
uint8 end_ch;
wifi_country_t cfg = {0};
if(lua_istable(L, 1)){
lua_getfield(L, 1, "country");
if (!lua_isnil(L, -1)){
if( lua_isstring(L, -1) ){
const char *country_code = luaL_checklstring( L, -1, &len );
luaL_argcheck(L, (len==2 && isalpha(country_code[0]) && isalpha(country_code[1])), 1, "country: country code must be 2 chars");
c_memcpy(cfg.cc, country_code, len);
if(cfg.cc[0] >= 0x61) cfg.cc[0]=cfg.cc[0]-32; //if lowercase change to uppercase
if(cfg.cc[1] >= 0x61) cfg.cc[1]=cfg.cc[1]-32; //if lowercase change to uppercase
}
else
return luaL_argerror( L, 1, "country: must be string" );
}
else{
cfg.cc[0]='C';
cfg.cc[1]='N';
cfg.cc[2]=0x00;
}
lua_pop(L, 1);
lua_getfield(L, 1, "start_ch");
if (!lua_isnil(L, -1)){
if(lua_isnumber(L, -1)){
start_ch = (uint8)luaL_checknumber(L, -1);
luaL_argcheck(L, (start_ch >= 1 && start_ch <= 14), 1, "start_ch: Range:1-14");
cfg.schan = start_ch;
}
else
luaL_argerror(L, 1, "start_ch: must be a number");
}
else
cfg.schan = 1;
lua_pop(L, 1);
lua_getfield(L, 1, "end_ch");
if (!lua_isnil(L, -1)){
if(lua_isnumber(L, -1)){
end_ch = (uint8)luaL_checknumber(L, -1);
luaL_argcheck(L, (end_ch >= 1 && end_ch <= 14), 1, "end_ch: Range:1-14");
luaL_argcheck(L, (end_ch >= cfg.schan), 1, "end_ch: can't be less than start_ch");
cfg.nchan = (end_ch-cfg.schan)+1; //cfg.nchan must equal total number of channels
}
else
luaL_argerror(L, 1, "end_ch: must be a number");
}
else
cfg.nchan = (13-cfg.schan)+1;
lua_pop(L, 1);
lua_getfield(L, 1, "policy");
if (!lua_isnil(L, -1)){
if(lua_isnumber(L, -1)){
uint8 policy = (uint8)luaL_checknumber(L, -1);
luaL_argcheck(L, (policy == WIFI_COUNTRY_POLICY_AUTO || policy == WIFI_COUNTRY_POLICY_MANUAL), 1, "policy: must be 0 or 1");
cfg.policy = policy;
}
else
luaL_argerror(L, 1, "policy: must be a number");
}
else
cfg.policy = WIFI_COUNTRY_POLICY_AUTO;
lua_pop(L, 1);
lua_pop(L, 1); //pop table from stack
bool retval = wifi_set_country(&cfg);
WIFI_DBG("\n country_cfg:\tcc:\"%s\"\tschan:%d\tnchan:%d\tpolicy:%d\n", cfg.cc, cfg.schan, cfg.nchan, cfg.policy);
if (retval==1)
lua_pushboolean(L, true);
else
return luaL_error(L, "Unable to set country info");
}
else
return luaL_argerror(L, 1, "Table not found!");
return 1;
}
// Lua: wifi.setmode(mode, save_to_flash)
static int wifi_setmode( lua_State* L )
{
......@@ -298,6 +416,17 @@ static int wifi_getphymode( lua_State* L )
return 1;
}
// Lua: wifi.setmaxtxpower()
static int wifi_setmaxtxpower( lua_State* L )
{
unsigned power;
power = luaL_checkinteger( L, 1 );
luaL_argcheck(L, (power > 0 && power < 83), 1, "tx power out of range (0->82)");
system_phy_set_max_tpw( (uint8_t) power);
return 1;
}
#ifdef PMSLEEP_ENABLE
/* Begin WiFi suspend functions*/
#include "pmSleep.h"
......@@ -711,10 +840,7 @@ static int wifi_station_clear_config ( lua_State* L )
bool auto_connect=true;
bool save_to_flash=true;
memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid));
memset(sta_conf.password, 0, sizeof(sta_conf.password));
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
sta_conf.bssid_set=0;
memset(&sta_conf, 0, sizeof(sta_conf));
wifi_station_disconnect();
......@@ -742,10 +868,9 @@ static int wifi_station_config( lua_State* L )
bool save_to_flash=true;
size_t sl, pl, ml;
memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid));
memset(sta_conf.password, 0, sizeof(sta_conf.password));
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
sta_conf.bssid_set=0;
memset(&sta_conf, 0, sizeof(sta_conf));
sta_conf.threshold.rssi = -127;
sta_conf.threshold.authmode = AUTH_OPEN;
if(lua_istable(L, 1))
{
......@@ -838,7 +963,7 @@ static int wifi_station_config( lua_State* L )
lua_State* L_temp = NULL;
lua_getfield(L, 1, "connect_cb");
lua_getfield(L, 1, "connected_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
......@@ -851,12 +976,12 @@ static int wifi_station_config( lua_State* L )
}
else
{
return luaL_argerror(L, 1, "connect_cb:not function");
return luaL_argerror(L, 1, "connected_cb:not function");
}
}
lua_pop(L, 1);
lua_getfield(L, 1, "disconnect_cb");
lua_getfield(L, 1, "disconnected_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
......@@ -869,7 +994,7 @@ static int wifi_station_config( lua_State* L )
}
else
{
return luaL_argerror(L, 1, "disconnect_cb:not function");
return luaL_argerror(L, 1, "disconnected_cb:not function");
}
}
lua_pop(L, 1);
......@@ -979,6 +1104,7 @@ static int wifi_station_connect4lua( lua_State* L )
if(lua_isfunction(L, 1)){
lua_pushnumber(L, EVENT_STAMODE_CONNECTED);
lua_pushvalue(L, 1);
lua_remove(L, 1);
wifi_event_monitor_register(L);
}
#endif
......@@ -993,6 +1119,7 @@ static int wifi_station_disconnect4lua( lua_State* L )
if(lua_isfunction(L, 1)){
lua_pushnumber(L, EVENT_STAMODE_DISCONNECTED);
lua_pushvalue(L, 1);
lua_remove(L, 1);
wifi_event_monitor_register(L);
}
#endif
......@@ -1019,6 +1146,8 @@ static int wifi_station_listap( lua_State* L )
return luaL_error( L, "Can't list ap in SOFTAP mode" );
}
struct scan_config scan_cfg;
memset(&scan_cfg, 0, sizeof(scan_cfg));
getap_output_format=0;
if (lua_type(L, 1)==LUA_TTABLE)
......@@ -1048,10 +1177,6 @@ static int wifi_station_listap( lua_State* L )
return luaL_error( L, "wrong arg type" );
}
}
else
{
scan_cfg.ssid=NULL;
}
lua_getfield(L, 1, "bssid");
if (!lua_isnil(L, -1)) /* found? */
......@@ -1072,10 +1197,6 @@ static int wifi_station_listap( lua_State* L )
return luaL_error( L, "wrong arg type" );
}
}
else
{
scan_cfg.bssid=NULL;
}
lua_getfield(L, 1, "channel");
......@@ -1094,10 +1215,6 @@ static int wifi_station_listap( lua_State* L )
return luaL_error( L, "wrong arg type" );
}
}
else
{
scan_cfg.channel=0;
}
lua_getfield(L, 1, "show_hidden");
if (!lua_isnil(L, -1)) /* found? */
......@@ -1116,10 +1233,6 @@ static int wifi_station_listap( lua_State* L )
return luaL_error( L, "wrong arg type" );
}
}
else
{
scan_cfg.show_hidden=0;
}
if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION)
{
......@@ -1191,24 +1304,21 @@ static int wifi_sta_gethostname( lua_State* L )
}
// Used by wifi_sta_sethostname_lua and wifi_change_default_hostname
static bool wifi_sta_sethostname(const char *hostname, size_t len)
// This function checks host name to ensure that it follows RFC 952 & RFC 1123 host name standards.
static bool wifi_sta_checkhostname(const char *hostname, size_t len)
{
//this function follows RFC 952 & RFC 1123 host name standards.
//the hostname must be 32 chars or less and first and last char must be alphanumeric
if (!isalnum(hostname[0]) || !isalnum(hostname[len-1]) || len > 32)
{
if (len == 0 || len > 32 || !isalnum(hostname[0]) || !isalnum(hostname[len-1])){
return false;
}
for (int i=1; i<len; i++)
{
//characters in the middle of the host name can be alphanumeric or a hyphen(-) only
if (!(isalnum(hostname[i]) || hostname[i]=='-'))
{
//characters in the middle of the host name must be alphanumeric or a hyphen(-) only
for (int i=1; i<len; i++){
if (!(isalnum(hostname[i]) || hostname[i]=='-')){
return false;
}
}
return wifi_station_set_hostname((char*)hostname);
return true;
}
// Lua: wifi.sta.sethostname()
......@@ -1216,8 +1326,9 @@ static int wifi_sta_sethostname_lua( lua_State* L )
{
size_t len;
const char *hostname = luaL_checklstring(L, 1, &len);
luaL_argcheck(L, wifi_sta_sethostname(hostname, len), 1, "Invalid hostname");
return 0;
luaL_argcheck(L, wifi_sta_checkhostname(hostname, len), 1, "Invalid hostname");
lua_pushboolean(L, wifi_station_set_hostname((char*)hostname));
return 1;
}
// Lua: wifi.sta.sleeptype(type)
......@@ -1794,8 +1905,11 @@ static const LUA_REG_TYPE wifi_map[] = {
{ LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) },
{ LSTRKEY( "getdefaultmode" ), LFUNCVAL( wifi_getdefaultmode ) },
{ LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) },
{ LSTRKEY( "getcountry" ), LFUNCVAL( wifi_getcountry ) },
{ LSTRKEY( "setcountry" ), LFUNCVAL( wifi_setcountry ) },
{ LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) },
{ LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) },
{ LSTRKEY( "setmaxtxpower" ), LFUNCVAL( wifi_setmaxtxpower ) },
#ifdef PMSLEEP_ENABLE
{ LSTRKEY( "suspend" ), LFUNCVAL( wifi_suspend ) },
{ LSTRKEY( "resume" ), LFUNCVAL( wifi_resume ) },
......@@ -1811,6 +1925,9 @@ static const LUA_REG_TYPE wifi_map[] = {
{ LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) },
#if defined(WIFI_SDK_EVENT_MONITOR_ENABLE)
{ LSTRKEY( "eventmon" ), LROVAL( wifi_event_monitor_map ) }, //declared in wifi_eventmon.c
#endif
#if defined(LUA_USE_MODULES_WIFI_MONITOR)
{ LSTRKEY( "monitor" ), LROVAL( wifi_monitor_map ) }, //declared in wifi_monitor.c
#endif
{ LSTRKEY( "NULLMODE" ), LNUMVAL( NULL_MODE ) },
{ LSTRKEY( "STATION" ), LNUMVAL( STATION_MODE ) },
......@@ -1838,6 +1955,9 @@ static const LUA_REG_TYPE wifi_map[] = {
{ LSTRKEY( "STA_FAIL" ), LNUMVAL( STATION_CONNECT_FAIL ) },
{ LSTRKEY( "STA_GOTIP" ), LNUMVAL( STATION_GOT_IP ) },
{ LSTRKEY( "COUNTRY_AUTO" ), LNUMVAL( WIFI_COUNTRY_POLICY_AUTO ) },
{ LSTRKEY( "COUNTRY_MANUAL" ), LNUMVAL( WIFI_COUNTRY_POLICY_MANUAL ) },
{ LSTRKEY( "__metatable" ), LROVAL( wifi_map ) },
{ LNILKEY, LNILVAL }
};
......@@ -1847,36 +1967,31 @@ void wifi_change_default_host_name(void)
{
uint8 opmode_temp=wifi_get_opmode();
wifi_set_opmode_current(STATION_MODE);
#ifndef WIFI_STA_HOSTNAME
char temp[32];
char temp[33] = {0};//32 chars + NULL
uint8_t mac[6];
wifi_get_macaddr(STATION_IF, mac);
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
wifi_sta_sethostname((const char*)temp, strlen(temp));
#ifndef WIFI_STA_HOSTNAME
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
#elif defined(WIFI_STA_HOSTNAME) && !defined(WIFI_STA_HOSTNAME_APPEND_MAC)
if(!wifi_sta_sethostname(WIFI_STA_HOSTNAME, strlen(WIFI_STA_HOSTNAME)))
{
char temp[32];
uint8_t mac[6];
wifi_get_macaddr(STATION_IF, mac);
if(wifi_sta_checkhostname(WIFI_STA_HOSTNAME, strlen(WIFI_STA_HOSTNAME))){
c_sprintf(temp, "%s", WIFI_STA_HOSTNAME);
}
else{
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
wifi_sta_sethostname((const char*)temp, strlen(temp));
}
#elif defined(WIFI_STA_HOSTNAME) && defined(WIFI_STA_HOSTNAME_APPEND_MAC)
char temp[32];
uint8_t mac[6];
wifi_get_macaddr(STATION_IF, mac);
c_sprintf(temp, "%s%X%X%X", WIFI_STA_HOSTNAME, (mac)[3], (mac)[4], (mac)[5]);
if(!wifi_sta_sethostname(temp, strlen(temp)))
{
if(strlen(WIFI_STA_HOSTNAME) <= 26 && wifi_sta_checkhostname(WIFI_STA_HOSTNAME, strlen(WIFI_STA_HOSTNAME))){
c_sprintf(temp, "%s%X%X%X", WIFI_STA_HOSTNAME, (mac)[3], (mac)[4], (mac)[5]);
}
else{
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
wifi_sta_sethostname((const char*)temp, strlen(temp));
}
#endif
if(opmode_temp!=wifi_get_opmode())
{
wifi_station_set_hostname((char*)temp);
if(opmode_temp != wifi_get_opmode()){
wifi_set_opmode_current(opmode_temp);
}
}
......@@ -1891,6 +2006,9 @@ int luaopen_wifi( lua_State *L )
}
#if defined(WIFI_SDK_EVENT_MONITOR_ENABLE)
wifi_eventmon_init();
#endif
#if defined(LUA_USE_MODULES_WIFI_MONITOR)
wifi_monitor_init(L);
#endif
return 0;
}
......
......@@ -67,4 +67,9 @@ enum wifi_suspension_state
int wifi_event_monitor_register(lua_State* L);
#endif
#ifdef LUA_USE_MODULES_WIFI_MONITOR
extern const LUA_REG_TYPE wifi_monitor_map[];
int wifi_monitor_init(lua_State *L);
#endif
#endif /* APP_MODULES_WIFI_COMMON_H_ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment