Lua: fix crash when a string argument is missing or an array instead. Implemented universal error messages and checks for function arguments

git-svn-id: svn://pulkomandy.tk/GrafX2/trunk@1356 416bcca6-2ee7-4201-b75f-2eb2f807beb1
This commit is contained in:
Yves Rizoud 2010-02-20 01:33:22 +00:00
parent 14464fef39
commit b393482c58

257
factory.c
View File

@ -48,6 +48,7 @@
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
#include <lualib.h> #include <lualib.h>
#include <float.h> // for DBL_MAX
/// ///
/// Number of characters for name in fileselector. /// Number of characters for name in fileselector.
@ -73,20 +74,65 @@ static inline byte clamp_byte(double value)
else return (byte)value; else return (byte)value;
} }
///
/// This macro reads a Lua argument into a double or integral lvalue.
/// If argument is invalid, it will break the caller and raise a verbose message.
/// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L)
/// @param index Index of the argument to check, starting at 1.
/// @param func_name The name of the lua callback, to display a message in case of error.
/// @param dest Destination lvalue. Can be a double, or any integral type. Conversion will "floor".
/// @param min_value Check for minimum value. Pass a double, or if you don't care, -DBL_MAX.
/// @param max_value Check for maximum value. Pass a double, or if you don't care, DBL_MAX.
#define LUA_ARG_NUMBER(index, func_name, dest, min_value, max_value) \
do { \
double value; \
if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, (index)); \
if (!lua_isnumber(L, (index))) return luaL_error(L, "%s: Argument %d is not a number.", func_name, (index)); \
value = lua_tonumber(L, (index)); \
if ((min_value) != -DBL_MAX && value<(min_value)) return luaL_error(L, "%s: Argument %d was too small, it had value of %f and minimum should be %f.", func_name, (index), value, (double)(min_value)); \
if ((max_value) != DBL_MAX && value>(max_value)) return luaL_error(L, "%s: Argument %d was too big, it had value of %f and maximum should be %f.", func_name, (index), value, (double)(max_value)); \
dest = value; \
} while(0)
///
/// This macro reads a Lua argument into a string.
/// If argument is invalid, it will break the caller and raise a verbose message.
/// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L)
/// @param index Index of the argument to check, starting at 1.
/// @param func_name The name of the lua callback, to display a message in case of error.
/// @param dest Destination string pointer, ideally a const char *.
#define LUA_ARG_STRING(index, func_name, dest) \
do { \
if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, index); \
if (!lua_isstring(L, (index))) return luaL_error(L, "%s: Argument %d is not a string.", func_name, index); \
dest = lua_tostring(L, (index)); \
} while (0)
/// Check if 'num' arguments were provided exactly
#define LUA_ARG_LIMIT(num, func_name) \
do { \
if (nb_args != (num)) \
return luaL_error(L, "%s: Expected %d arguments, but found %d.", func_name, (num), nb_args); \
} while(0)
// Wrapper functions to call C from Lua // Wrapper functions to call C from Lua
int L_SetBrushSize(lua_State* L) int L_SetBrushSize(lua_State* L)
{ {
int w = lua_tonumber(L,1); int w;
int h = lua_tonumber(L,2); int h;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "setbrushsize");
LUA_ARG_NUMBER(1, "setbrushsize", w, 1, 10000);
LUA_ARG_NUMBER(2, "setbrushsize", h, 1, 10000);
if (w<1 || h<1)
{
return luaL_error(L, "SetBrushSize: Unreasonable arguments");
}
if (Realloc_brush(w, h)) if (Realloc_brush(w, h))
{ {
return luaL_error(L, "SetBrushSize: Resize failed"); return luaL_error(L, "setbrushsize: Resize failed");
} }
Brush_was_altered=1; Brush_was_altered=1;
// Fill with Back_color // Fill with Back_color
@ -112,10 +158,16 @@ int L_GetBrushTransparentColor(lua_State* L)
int L_PutBrushPixel(lua_State* L) int L_PutBrushPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
uint8_t c = lua_tonumber(L,3); uint8_t c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (3, "putbrushpixel");
LUA_ARG_NUMBER(1, "putbrushpixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "putbrushpixel", y, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(3, "putbrushpixel", c, INT_MIN, INT_MAX);
Brush_was_altered=1; Brush_was_altered=1;
if (x<0 || y<0 || x>=Brush_width || y>=Brush_height) if (x<0 || y<0 || x>=Brush_width || y>=Brush_height)
@ -129,9 +181,15 @@ int L_PutBrushPixel(lua_State* L)
int L_GetBrushPixel(lua_State* L) int L_GetBrushPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
uint8_t c; uint8_t c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getbrushpixel");
LUA_ARG_NUMBER(1, "getbrushpixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getbrushpixel", y, INT_MIN, INT_MAX);
if (x<0 || y<0 || x>=Brush_width || y>=Brush_height) if (x<0 || y<0 || x>=Brush_width || y>=Brush_height)
{ {
c = Back_color; // Return 'transparent' c = Back_color; // Return 'transparent'
@ -146,9 +204,15 @@ int L_GetBrushPixel(lua_State* L)
int L_GetBrushBackupPixel(lua_State* L) int L_GetBrushBackupPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
uint8_t c; uint8_t c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getbrushbackuppixel");
LUA_ARG_NUMBER(1, "getbrushbackuppixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getbrushbackuppixel", y, INT_MIN, INT_MAX);
if (x<0 || y<0 || x>=Brush_backup_width || y>=Brush_backup_height) if (x<0 || y<0 || x>=Brush_backup_width || y>=Brush_backup_height)
{ {
c = Back_color; // Return 'transparent' c = Back_color; // Return 'transparent'
@ -163,11 +227,14 @@ int L_GetBrushBackupPixel(lua_State* L)
int L_SetPictureSize(lua_State* L) int L_SetPictureSize(lua_State* L)
{ {
int w = lua_tonumber(L,1);
int h = lua_tonumber(L,2);
if (w<1 || h<1 || w>9999 || h>9999) int w;
return luaL_error(L, "SetPictureSize: Unreasonable dimensions"); int h;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "setpicturesize");
LUA_ARG_NUMBER(1, "setpicturesize", w, 1, 9999);
LUA_ARG_NUMBER(2, "setpicturesize", h, 1, 9999);
Resize_image(w, h); // TODO: a return value to catch runtime errors Resize_image(w, h); // TODO: a return value to catch runtime errors
return 0; return 0;
@ -175,16 +242,22 @@ int L_SetPictureSize(lua_State* L)
int L_GetPictureSize(lua_State* L) int L_GetPictureSize(lua_State* L)
{ {
lua_pushinteger(L, Main_image_width); lua_pushinteger(L, Main_image_width);
lua_pushinteger(L, Main_image_height); lua_pushinteger(L, Main_image_height);
return 2; return 2;
} }
int L_PutPicturePixel(lua_State* L) int L_PutPicturePixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int c = lua_tonumber(L,3); int c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (3, "putpicturepixel");
LUA_ARG_NUMBER(1, "putpicturepixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "putpicturepixel", y, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(3, "putpicturepixel", c, INT_MIN, INT_MAX);
// Bound check // Bound check
if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height) if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height)
@ -198,8 +271,14 @@ int L_PutPicturePixel(lua_State* L)
int L_GetPicturePixel(lua_State* L) int L_GetPicturePixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getpicturepixel");
LUA_ARG_NUMBER(1, "getpicturepixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getpicturepixel", y, INT_MIN, INT_MAX);
// Bound check // Bound check
if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height) if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height)
{ {
@ -213,8 +292,14 @@ int L_GetPicturePixel(lua_State* L)
int L_GetBackupPixel(lua_State* L) int L_GetBackupPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getbackuppixel");
LUA_ARG_NUMBER(1, "getbackuppixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getbackuppixel", y, INT_MIN, INT_MAX);
// Bound check // Bound check
if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height) if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height)
{ {
@ -228,8 +313,14 @@ int L_GetBackupPixel(lua_State* L)
int L_GetLayerPixel(lua_State* L) int L_GetLayerPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getlayerpixel");
LUA_ARG_NUMBER(1, "getlayerpixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getlayerpixel", y, INT_MIN, INT_MAX);
// Bound check // Bound check
if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height) if (x<0 || y<0 || x>=Main_image_width || y>=Main_image_height)
{ {
@ -252,8 +343,14 @@ int L_GetSparePictureSize(lua_State* L)
int L_GetSpareLayerPixel(lua_State* L) int L_GetSpareLayerPixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getsparelayerpixel");
LUA_ARG_NUMBER(1, "getsparelayerpixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getsparelayerpixel", y, INT_MIN, INT_MAX);
// Bound check // Bound check
if (x<0 || y<0 || x>=Spare_image_width || y>=Spare_image_height) if (x<0 || y<0 || x>=Spare_image_width || y>=Spare_image_height)
{ {
@ -267,8 +364,14 @@ int L_GetSpareLayerPixel(lua_State* L)
int L_GetSparePicturePixel(lua_State* L) int L_GetSparePicturePixel(lua_State* L)
{ {
int x = lua_tonumber(L,1); int x;
int y = lua_tonumber(L,2); int y;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (2, "getsparepicturepixel");
LUA_ARG_NUMBER(1, "getsparepicturepixel", x, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(2, "getsparepicturepixel", y, INT_MIN, INT_MAX);
// Some bound checking is done by the function itself, here's the rest. // Some bound checking is done by the function itself, here's the rest.
if (x<0 || y<0) if (x<0 || y<0)
{ {
@ -282,7 +385,11 @@ int L_GetSparePicturePixel(lua_State* L)
int L_GetSpareColor(lua_State* L) int L_GetSpareColor(lua_State* L)
{ {
byte c=lua_tonumber(L,1); byte c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (1, "getsparecolor");
LUA_ARG_NUMBER(1, "getsparecolor", c, INT_MIN, INT_MAX);
lua_pushinteger(L, Spare_palette[c].R); lua_pushinteger(L, Spare_palette[c].R);
lua_pushinteger(L, Spare_palette[c].G); lua_pushinteger(L, Spare_palette[c].G);
@ -300,15 +407,20 @@ int L_GetSpareTransColor(lua_State* L)
int L_SetColor(lua_State* L) int L_SetColor(lua_State* L)
{ {
byte c=lua_tonumber(L,1); byte c;
double r, g, b;
byte r=clamp_byte((double)lua_tonumber(L,2)); int nb_args=lua_gettop(L);
byte g=clamp_byte((double)lua_tonumber(L,3));
byte b=clamp_byte((double)lua_tonumber(L,4));
Main_palette[c].R=Round_palette_component(r); LUA_ARG_LIMIT (4, "setcolor");
Main_palette[c].G=Round_palette_component(g); LUA_ARG_NUMBER(1, "setcolor", c, INT_MIN, INT_MAX);
Main_palette[c].B=Round_palette_component(b); LUA_ARG_NUMBER(2, "setcolor", r, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(3, "setcolor", g, INT_MIN, INT_MAX);
LUA_ARG_NUMBER(4, "setcolor", b, INT_MIN, INT_MAX);
Main_palette[c].R=Round_palette_component(clamp_byte(r));
Main_palette[c].G=Round_palette_component(clamp_byte(g));
Main_palette[c].B=Round_palette_component(clamp_byte(b));
// Set_color(c, r, g, b); Not needed. Update screen when script is finished // Set_color(c, r, g, b); Not needed. Update screen when script is finished
Palette_has_changed=1; Palette_has_changed=1;
return 0; return 0;
@ -316,7 +428,11 @@ int L_SetColor(lua_State* L)
int L_GetColor(lua_State* L) int L_GetColor(lua_State* L)
{ {
byte c=lua_tonumber(L,1); byte c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (1, "getcolor");
LUA_ARG_NUMBER(1, "getcolor", c, INT_MIN, INT_MAX);
lua_pushinteger(L, Main_palette[c].R); lua_pushinteger(L, Main_palette[c].R);
lua_pushinteger(L, Main_palette[c].G); lua_pushinteger(L, Main_palette[c].G);
@ -326,7 +442,11 @@ int L_GetColor(lua_State* L)
int L_GetBackupColor(lua_State* L) int L_GetBackupColor(lua_State* L)
{ {
byte c=lua_tonumber(L,1); byte c;
int nb_args=lua_gettop(L);
LUA_ARG_LIMIT (1, "getbackupcolor");
LUA_ARG_NUMBER(1, "getbackupcolor", c, INT_MIN, INT_MAX);
lua_pushinteger(L, Main_backups->Pages->Next->Palette[c].R); lua_pushinteger(L, Main_backups->Pages->Next->Palette[c].R);
lua_pushinteger(L, Main_backups->Pages->Next->Palette[c].G); lua_pushinteger(L, Main_backups->Pages->Next->Palette[c].G);
@ -336,11 +456,16 @@ int L_GetBackupColor(lua_State* L)
int L_MatchColor(lua_State* L) int L_MatchColor(lua_State* L)
{ {
byte r=clamp_byte(lua_tonumber(L,1)); double r, g, b;
byte g=clamp_byte(lua_tonumber(L,2)); int c;
byte b=clamp_byte(lua_tonumber(L,3)); int nb_args=lua_gettop(L);
int c = Best_color_nonexcluded(r,g,b); LUA_ARG_LIMIT (3, "matchcolor");
LUA_ARG_NUMBER(1, "matchcolor", r, -DBL_MAX, DBL_MAX);
LUA_ARG_NUMBER(2, "matchcolor", g, -DBL_MAX, DBL_MAX);
LUA_ARG_NUMBER(3, "matchcolor", b, -DBL_MAX, DBL_MAX);
c = Best_color_nonexcluded(clamp_byte(r),clamp_byte(g),clamp_byte(b));
lua_pushinteger(L, c); lua_pushinteger(L, c);
return 1; return 1;
} }
@ -398,42 +523,43 @@ int L_InputBox(lua_State* L)
nb_args = lua_gettop (L); nb_args = lua_gettop (L);
if (nb_args < 6) if (nb_args < 6)
{ {
return luaL_error(L, "InputBox: Less than 6 arguments"); return luaL_error(L, "inputbox: Less than 6 arguments");
} }
if ((nb_args - 1) % args_per_setting) if ((nb_args - 1) % args_per_setting)
{ {
return luaL_error(L, "InputBox: Wrong number of arguments"); return luaL_error(L, "inputbox: Wrong number of arguments");
} }
nb_settings = (nb_args-1)/args_per_setting; nb_settings = (nb_args-1)/args_per_setting;
if (nb_settings > max_settings) if (nb_settings > max_settings)
{ {
return luaL_error(L, "InputBox: Too many settings, limit reached"); return luaL_error(L, "inputbox: Too many settings, limit reached");
} }
max_label_length=4; // Minimum size to account for OK / Cancel buttons max_label_length=4; // Minimum size to account for OK / Cancel buttons
// First argument is window caption // First argument is window caption
window_caption = lua_tostring(L,1); LUA_ARG_STRING(1, "inputbox", window_caption);
caption_length = strlen(window_caption); caption_length = strlen(window_caption);
if ( caption_length > 14) if ( caption_length > 14)
max_label_length = caption_length - 10; max_label_length = caption_length - 10;
for (setting=0; setting<nb_settings; setting++) for (setting=0; setting<nb_settings; setting++)
{ {
label[setting] = lua_tostring(L,setting*args_per_setting+2); LUA_ARG_STRING(setting*args_per_setting+2, "inputbox", label[setting]);
if (strlen(label[setting]) > max_label_length) if (strlen(label[setting]) > max_label_length)
max_label_length = strlen(label[setting]); max_label_length = strlen(label[setting]);
current_value[setting] = lua_tonumber(L,setting*args_per_setting+3); LUA_ARG_NUMBER(setting*args_per_setting+3, "inputbox", current_value[setting], -DBL_MAX, DBL_MAX);
min_value[setting] = lua_tonumber(L,setting*args_per_setting+4); LUA_ARG_NUMBER(setting*args_per_setting+4, "inputbox", min_value[setting], -DBL_MAX, DBL_MAX);
/*if (min_value[setting] < -999999999999999.0) /*if (min_value[setting] < -999999999999999.0)
min_value[setting] = -999999999999999.0;*/ min_value[setting] = -999999999999999.0;*/
max_value[setting] = lua_tonumber(L,setting*args_per_setting+5); LUA_ARG_NUMBER(setting*args_per_setting+5, "inputbox", max_value[setting], -DBL_MAX, DBL_MAX);
/*if (max_value[setting] > 999999999999999.0) /*if (max_value[setting] > 999999999999999.0)
max_value[setting] = 999999999999999.0;*/ max_value[setting] = 999999999999999.0;*/
decimal_places[setting] = lua_tonumber(L,setting*args_per_setting+6); LUA_ARG_NUMBER(setting*args_per_setting+6, "inputbox", decimal_places[setting], INT_MIN, INT_MAX);
if (decimal_places[setting]>15) if (decimal_places[setting]>15)
decimal_places[setting]=15; decimal_places[setting]=15;
// Keep current value in range // Keep current value in range
@ -588,23 +714,23 @@ int L_InputBox(lua_State* L)
int L_MessageBox(lua_State* L) int L_MessageBox(lua_State* L)
{ {
int nb_args = lua_gettop (L);
const char * caption; const char * caption;
const char * message; const char * message;
int nb_args = lua_gettop (L);
if (nb_args == 1) if (nb_args == 1)
{ {
caption = "Script message"; caption = "Script message";
message = lua_tostring(L,1); LUA_ARG_STRING(1, "messagebox", message);
} }
else if (nb_args == 2) else if (nb_args == 2)
{ {
caption = lua_tostring(L,1); LUA_ARG_STRING(1, "messagebox", caption);
message = lua_tostring(L,2); LUA_ARG_STRING(2, "messagebox", message);
} }
else else
{ {
return luaL_error(L, "MessageBox: Needs one or two arguments."); return luaL_error(L, "messagebox: Needs one or two arguments.");
} }
Verbose_message(caption, message); Verbose_message(caption, message);
@ -884,8 +1010,9 @@ void Button_Brush_Factory(void)
if (luaL_loadfile(L,scriptdir) != 0) if (luaL_loadfile(L,scriptdir) != 0)
{ {
message = lua_tostring(L, 1); int stack_size;
if(message) stack_size= lua_gettop(L);
if (stack_size>0 && (message = lua_tostring(L, stack_size))!=NULL)
Verbose_message("Error!", message); Verbose_message("Error!", message);
else else
Warning_message("Unknown error loading script!"); Warning_message("Unknown error loading script!");