From 568c8829db2f886921c7592cadbadbf4241127b6 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Sun, 27 Apr 2014 16:48:07 +1000 Subject: Centralise the PACKAGE_* stuff, move our libraries to lib, and shuffle stuff to suit. --- .gitignore | 2 +- TODO | 46 +- build.lua | 16 +- docs/README.libraries | 6 +- lib/LSL.lua | 870 +++++++++++++++++++++++ lib/skang.lua | 1686 +++++++++++++++++++++++++++++++++++++++++++++ libraries/LSL.lua | 870 ----------------------- libraries/LumbrJack.c | 85 --- libraries/LumbrJack.h | 38 - libraries/Runnr.c | 318 --------- libraries/Runnr.h | 15 - libraries/build.lua | 24 - libraries/skang.lua | 1686 --------------------------------------------- media/checkme.txt | 2 + src/GuiLua/GuiLua.h | 23 +- src/GuiLua/build.lua | 4 +- src/GuiLua/skang.c | 6 +- src/GuiLua/test.sh | 2 +- src/LuaSL/LuaSL_compile.c | 4 +- src/LuaSL/LuaSL_test.c | 8 +- src/LuaSL/build.lua | 9 + src/LuaSL/test.sh | 2 +- src/extantz/extantz.c | 21 +- src/extantz/extantz.h | 8 +- src/libraries/LumbrJack.c | 85 +++ src/libraries/LumbrJack.h | 38 + src/libraries/Runnr.c | 318 +++++++++ src/libraries/Runnr.h | 15 + src/libraries/SledjHamr.c | 23 + src/libraries/SledjHamr.h | 14 + src/libraries/build.lua | 33 + 31 files changed, 3134 insertions(+), 3143 deletions(-) create mode 100644 lib/LSL.lua create mode 100644 lib/skang.lua delete mode 100644 libraries/LSL.lua delete mode 100644 libraries/LumbrJack.c delete mode 100644 libraries/LumbrJack.h delete mode 100644 libraries/Runnr.c delete mode 100644 libraries/Runnr.h delete mode 100755 libraries/build.lua delete mode 100644 libraries/skang.lua create mode 100644 media/checkme.txt create mode 100644 src/libraries/LumbrJack.c create mode 100644 src/libraries/LumbrJack.h create mode 100644 src/libraries/Runnr.c create mode 100644 src/libraries/Runnr.h create mode 100644 src/libraries/SledjHamr.c create mode 100644 src/libraries/SledjHamr.h create mode 100755 src/libraries/build.lua diff --git a/.gitignore b/.gitignore index 221dde6..12ab02e 100644 --- a/.gitignore +++ b/.gitignore @@ -319,7 +319,7 @@ skang *.lsl.lua *.lsl2 libraries/lemon/lemon -libraries/constants.lsl +lib/constants.lsl src/LuaSL/LuaSL_test src/LuaSL/LuaSL_lemon_yaccer.c src/LuaSL/LuaSL_lemon_yaccer.h diff --git a/TODO b/TODO index 3fa0c8d..240e63d 100644 --- a/TODO +++ b/TODO @@ -3,45 +3,19 @@ FIXES - Project paths ------------- -CFLAGS = CFLAGS .. ' -DPACKAGE_BIN_DIR=\\"' .. baseDir .. '\\"' -CFLAGS = CFLAGS .. ' -DPACKAGE_LIB_DIR=\\"' .. baseDir .. '\\"' -CFLAGS = CFLAGS .. ' -DPACKAGE_DATA_DIR=\\"' .. baseDir .. '\\"' - - // There is also a locale variety, but we are not dealing with locale stuff yet. - // There is a prefix variety, but you can't set it, only read it later. - // These set the fallbacks used by elm_app_info_set(), that are defined at compile time. - elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR); - elm_app_compile_data_dir_set(PACKAGE_DATA_DIR); - elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR); - // Do this after the above calls, but before changing the working directory, or screwing with argv[0]. - // It tries to set up the package paths depending on where the executable is, so things are relocatable. - // First argument is the elm_main() function that Elementary starts us from. - // Second argument should be a lower case string used as the "domain", which is different from the log domain. - // It's used lower case as part of the data directory path. - // So, if prefix is /usr/local, then the system data dir is /usr/local/share, - // and this apps data dir is /usr/local/share/"domain". - // It's used upper case as part of environment variables to override directory paths at run time. - // So "DOMAIN"_PREFIX, "DOMAIN"_BIN_DIR, "DOMAIN"_LIB_DIR, "DOMAIN"_DATA_DIR, and "DOMAIN"_LOCALE_DIR - // Third argument is the name of a file it can check for to make sure it found the correct path. - // This file is looked for in the data dir. - elm_app_info_set(elm_main, "GuiLua", "skang.lua"); - // Once this is all setup, the code can do - - elm_app_prefix_dir_get(); // or bin, lib, data, locale. - The executables currently live in ...../SledjHamr. Test executables stay in the src directory. Media and other data in SledjHamr/media (includes the test sim and Irrlicht examples). -Libraries are in SledjHamr/libraries (including Irrlicht, lemon, and some sources). - Should move LumbrJack and Runnr sources to src, and probably lemon to. +External libraries are in SledjHamr/libraries (Irrlicht and lemon). Irrlicht is statically linked, so except for it's media, we don't need it at run time. We could probably move the Irrlicht examples we actually use (all test data anyway) to Test sim. + Lemon is only used at build time. Builders should be able to - put binaries in /usr/local/bin put libraries - /usr/local/lib/SledjHamr put media in /usr/local/share/SledjHamr If they don't, those things should be in the build directory, where they are now. - And looked up locally to allow moving it as one thing. User should be able to - Run sims from where ever they like. @@ -53,12 +27,6 @@ User should be able to - Write their own skins, using their own media. SOOOO ... - build.lua should set - - PACKAGE_BIN_DIR = SledjHamr - PACKAGE_LIB_DIR = SledjHamr/libraries (should rename this to lib) - PACKAGE_DATA_DIR = SledjHamr/media - PACKAGE_LOCALE_DIR = SledjHamr/locale (doesn't exist yet) - install.lua should - (doesn't exist yet) SledjHamr binaries -> prefix .. '/bin' SledjHamr/libraries -> prefix .. '/lib/SledjHamr' @@ -69,14 +37,6 @@ SOOOO ... For now, we don't care, there's no actual installing going on. SledjHamr/locale -> prefix .. '/share/SledjHamr/locale' - Some library function should call all the elm_app_compile_*_dir_set() functions. - They should be set as -D options only for that library. - elm_app_info_set() has to be called for each different elm_main(), so that might be tricky. - elm_app_*_dir_get() can then be called from anywhere, and will get the same results. - NOTE - this is Elm specific, so non GUI stuff like LuaSL can't use it. - Seems that non Elm examples all just use the PACKAGE_*_DIR type macros directly in strings. - - Irrlicht is flickering like crazy. Hmm, might be Irrlicht's fault, mostly it flickers to black, but I've seen it flicker to the first frame @@ -118,7 +78,7 @@ Write my own, use ePhysics. Hopefully ePhysics has some sort of magnetism / attraction thingy. See the forces demo, should be doable. Collision restraints and impulses? -See if Evas allows detaching stuff from one canvas and adding it to +See if Evas allows detaching stuff from one canvas and adding it to another canvas. This allows things like tearing off tabs from windows, tearing off sub menus, and switching windows between internal and external windows, without having to entirely recreate the UI stack diff --git a/build.lua b/build.lua index 083d726..414ebfa 100755 --- a/build.lua +++ b/build.lua @@ -58,11 +58,16 @@ if 'number' == type(args) then end end +bin_d = baseDir +lib_d = baseDir .. '/lib' +data_d = baseDir .. '/media' +locale_d = baseDir .. '/locale' + -- Likely this will fail, coz Lua likes to strip out environmont variables. -- On the other hand, there's a more direct way to get to environment variables, it would fail to. CFLAGOPTS = readCommand('echo "$CFLAGOPTS"') -CFLAGS = '-g -Wall -I include -I ' .. baseDir .. '/libraries' +CFLAGS = '-g -Wall -I ' .. baseDir .. '/src/libraries' CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'luajit') CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'eo') CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'eet') @@ -71,13 +76,10 @@ CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'ecore-evas') CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'ecore-file') CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'edje') CFLAGS = CFLAGS .. ' ' .. pkgConfig('cflags', 'elementary') -CFLAGS = CFLAGS .. ' -DPACKAGE_BIN_DIR=\\"' .. baseDir .. '\\"' -CFLAGS = CFLAGS .. ' -DPACKAGE_LIB_DIR=\\"' .. baseDir .. '\\"' -CFLAGS = CFLAGS .. ' -DPACKAGE_DATA_DIR=\\"' .. baseDir .. '\\"' CFLAGS = CFLAGS .. ' ' .. CFLAGOPTS -LDFLAGS = '-L ' .. baseDir .. '/libraries ' .. pkgConfig('libs-only-L', 'luajit') .. ' -L /usr/lib -L /lib' -libs = '-lLumbrJack -lRunnr ' .. pkgConfig('libs', 'elementary') .. ' ' .. pkgConfig('libs', 'luajit') .. ' -lpthread -lm -Wl,-rpath,' .. baseDir .. '/libraries' +LDFLAGS = '-L ' .. baseDir .. '/lib ' .. pkgConfig('libs-only-L', 'luajit') .. ' -L /usr/lib -L /lib' +libs = '-lLumbrJack -lRunnr -lSledjHamr ' .. pkgConfig('libs', 'elementary') .. ' ' .. pkgConfig('libs', 'luajit') .. ' -lpthread -lm -Wl,-rpath,' .. baseDir .. '/lib' LFLAGS = '-d' EDJE_FLAGS = '-id ' .. baseDir .. '/media -fd ' .. baseDir .. '/media' @@ -90,7 +92,7 @@ if 'nil' == type(args) then print('_______________ BUILDING Irrlicht _______________') -- Irrlicht is an external project that comes with make files anyway, and doesn't otherwise pass the test. runCommand('Irrlicht','libraries/irrlicht-1.8.1/source/Irrlicht', 'make') - buildSub('libraries', 'libraries') + buildSub('libraries', 'src/libraries') buildSub('LuaSL', 'src/LuaSL') buildSub('GuiLua', 'src/GuiLua') buildSub('extantz', 'src/extantz') diff --git a/docs/README.libraries b/docs/README.libraries index a26fda1..6d36009 100644 --- a/docs/README.libraries +++ b/docs/README.libraries @@ -26,6 +26,8 @@ Merged it into LuaSL, so it's no longer here. These are the libraries written for the SledjHamr project. -LumbrJack is a library of logging stuff, coz every one wants to log. +libLumbrJack is a library of logging stuff, coz every one wants to log. -Runnr is for running Lua scripts. Named (sorta) after a friend of mine. +libRunnr is for running Lua scripts. Named (sorta) after a friend of mine. + +libSledjHamr is a motley collection of stuff that is needed. diff --git a/lib/LSL.lua b/lib/LSL.lua new file mode 100644 index 0000000..662c880 --- /dev/null +++ b/lib/LSL.lua @@ -0,0 +1,870 @@ +-- A module of LSL stuffs. + +-- Using a module means it gets compiled each time? Maybe not if I can use bytecode files. Perhaps LuaJIT caches these? +-- Does not seem to be slowing it down noticably, but that might change once the stubs are filled out. + +-- Use it like this - +-- local _LSL = require 'LSL' + +--[[ From http://lua-users.org/wiki/LuaModuleFunctionCritiqued +A related note on C code: The luaL_register [9] function in C is +somewhat analogous to the module function in Lua, so luaL_register +shares similar problems, at least when a non-NULL libname is used. +Furthermore, the luaL_newmetatable/luaL_getmetatable/luaL_checkudata +functions use a C string as a key into the global registry. This poses +some potential for name conflicts--either because the modules were +written by different people or because they are different versions of +the same module loaded simultaneously. To address this, one may instead +use a lightuserdata (pointer to variable of static linkage to ensure +global uniqueness) for this key or store the metatable as an +upvalue--either way is a bit more efficient and less error prone. +]] + +-- http://www.lua.org/pil/15.4.html looks useful. +-- http://www.lua.org/pil/15.5.html the last part about autoloading functions might be useful. + + +local LSL = {}; +local SID = ""; +local scriptName = ""; +local running = true +local paused = false +local currentState = {} +local detectedGroups = {} +local detectedGrabs = {} +local detectedKeys = {} +local detectedLinkNumbers = {} +local detectedNames = {} +local detectedOwners = {} +local detectedPoss = {} +local detectedRots = {} +local detectedTouchBinormals = {} +local detectedTouchFaces = {} +local detectedTouchNormals = {} +local detectedTouchPoss = {} +local detectedTouchSTs = {} +local detectedTouchUVs = {} +local detectedTypes = {} +local detectedVels = {} +local waitAndProcess + + +-- Debugging aids + +-- Functions to print tables. +local print_table, print_table_start + +function print_table_start(table, space, name) + print(space .. name .. ": "); + print(space .. "{"); + print_table(table, space .. " "); + print(space .. "}"); +end + +function print_table(table, space) + for k, v in pairs(table) do + if type(v) == "table" then + print_table_start(v, space, k); + elseif type(v) == "string" then + print(space .. k .. ': "' .. v .. '";') + else + print(space .. k .. ": " .. v .. ";") + end + end +end + +function msg(...) + print(SID, ...) -- The comma adds a tab, fancy that. B-) +end + + +-- Stuff called from the wire protocol has to be global, but I think this means just global to this file. + +events = {} + +function start() paused = false end +function stop() paused = true end +function quit() running = false end + +function events.detectedGroups(list) detectedGroups = list end +function events.detectedGrabs(list) detectedGrabs = list end +function events.detectedKeys(list) detectedKeys = list end +function events.detectedLinkNumbers(list) detectedLinkNumbers = list end +function events.detectedNames(list) detectedNames = list end +function events.detectedOwners(list) detectedOwners = list end +function events.detectedPoss(list) detectedPoss = list end +function events.detectedRots(list) detectedRots = list end +function events.detectedTouchBinormals(list) detectedTouchBinormals = list end +function events.detectedTouchFaces(list) detectedTouchFaces = list end +function events.detectedTouchNormals(list) detectedTouchNormals = list end +function events.detectedTouchPoss(list) detectedTouchPoss = list end +function events.detectedTouchSTs(list) detectedTouchSTs = list end +function events.detectedTouchUVs(list) detectedTouchUVs = list end +function events.detectedTypes(list) detectedTypes = list end +function events.detectedVels(list) detectedVels = list end + +function events.detectsClear() + detectedGroups = {} + detectedGrabs = {} + detectedKeys = {} + detectedLinkNumbers = {} + detectedNames = {} + detectedOwners = {} + detectedPoss = {} + detectedRots = {} + detectedTouchBinormals = {} + detectedTouchFaces = {} + detectedTouchNormals = {} + detectedTouchPoss = {} + detectedTouchSTs = {} + detectedTouchUVs = {} + detectedTypes = {} + detectedVels = {} +end + +function events.at_rot_target(tnum, targetrot, ourrot) if nil ~= currentState.at_rot_target then currentState.at_rot_target(tnum, targetrot, ourrot) end events.detectsClear() end +function events.at_target(tnum, targetpos, ourpos) if nil ~= currentState.at_target then currentState.at_target(tnum, targetpos, ourpos) end events.detectsClear() end +function events.attach(id) if nil ~= currentState.attach then currentState.attach(id) end events.detectsClear() end +function events.changed(change) if nil ~= currentState.changed then currentState.changed(change) end events.detectsClear() end +function events.collision_start(num_detected) if nil ~= currentState.collision_start then currentState.collision_start(num_detected) end events.detectsClear() end +function events.collision(num_detected) if nil ~= currentState.collision then currentState.collision(num_detected) end events.detectsClear() end +function events.collision_end(num_detected) if nil ~= currentState.collision_end then currentState.collision_end(num_detected) end events.detectsClear() end +function events.control(id, held, changed) if nil ~= currentState.control then currentState.control(id, held, changed) end events.detectsClear() end +function events.dataserver(queryid, data) if nil ~= currentState.dataserver then currentState.dataserver(queryid, data) end events.detectsClear() end +function events.email(Time, address, subj, message, num_left) if nil ~= currentState.email then currentState.email(Time, address, subj, message, num_left) end events.detectsClear() end +function events.http_request(request_id, status, metadata, body) if nil ~= currentState.http_request then currentState.http_request(request_id, status, metadata, body) end events.detectsClear() end +function events.http_response(request_id, status, metadata, body) if nil ~= currentState.http_response then currentState.http_response(request_id, status, metadata, body) end events.detectsClear() end +function events.land_collision_start(pos) if nil ~= currentState.land_collision_start then currentState.land_collision_start(pos) end events.detectsClear() end +function events.land_collision(pos) if nil ~= currentState.land_collision then currentState.land_collision(pos) end events.detectsClear() end +function events.land_collision_end(pos) if nil ~= currentState.land_collision_end then currentState.land_collision_end(pos) end events.detectsClear() end +function events.link_message(sender_num, num, str, id) if nil ~= currentState.link_message then currentState.link_message(sender_num, num, str, id) end events.detectsClear() end +function events.listen(channel, name, id, message) if nil ~= currentState.listen then currentState.listen(channel, name, id, message) end events.detectsClear() end +function events.money(id, amount) if nil ~= currentState.money then currentState.money(id, amount) end events.detectsClear() end +function events.moving_start() if nil ~= currentState.moving_start then currentState.moving_start() end events.detectsClear() end +function events.moving_end() if nil ~= currentState.moving_end then currentState.moving_end() end events.detectsClear() end +function events.no_sensor() if nil ~= currentState.no_sensor then currentState.no_sensor() end events.detectsClear() end +function events.not_at_rot_target() if nil ~= currentState.not_at_rot_target then currentState.not_at_rot_target() end events.detectsClear() end +function events.not_at_target() if nil ~= currentState.not_at_target then currentState.not_at_target() end events.detectsClear() end +function events.object_rez(id) if nil ~= currentState.object_rez then currentState.object_rez() end events.detectsClear() end +function events.on_rez(start_param) if nil ~= currentState.on_rezz then currentState.on_rez(start_param) end events.detectsClear() end +function events.remote_data(event_type, channel, message_id, sender, idata, sdata) if nil ~= currentState.remote_data then currentState.remote_data(event_type, channel, message_id, sender, idata, sdata) end events.detectsClear() end +function events.run_time_permissions(perm) if nil ~= currentState.run_time_permisions then currentState.run_time_permissions(perm) end events.detectsClear() end +function events.sensor(num_detected) if nil ~= currentState.sensor then currentState.sensor(num_detected) end events.detectsClear() end +function events.state_entry() if nil ~= currentState.state_entry then currentState.state_entry() end events.detectsClear() end +function events.state_exit() if nil ~= currentState.state_exit then currentState.state_exit() end events.detectsClear() end +function events.timer() if nil ~= currentState.timer then currentState.timer() end events.detectsClear() end +function events.touch_start(num_detected) if nil ~= currentState.touch_start then currentState.touch_start(num_detected) end events.detectsClear() end +function events.touch(num_detected) if nil ~= currentState.touch then currentState.touch(num_detected) end events.detectsClear() end +function events.touch_end(num_detected) if nil ~= currentState.touch_end then currentState.touch_end(num_detected) end events.detectsClear() end +function events.transaction_result(id, success, data) if nil ~= currentState.transaction_result then currentState.transaction_result(id, success, data) end events.detectsClear() end + + +-- LSL function and constant creation stuff. + +local args2string -- Pre declare this. +local functions = {} +local mt = {} + +local function value2string(value, Type) + local temp = "" + + if "float" == Type then temp = temp .. value + elseif "integer" == Type then temp = temp .. value + elseif "key" == Type then temp = "\"" .. value .. "\"" + elseif "list" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]" + elseif "table" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]" + elseif "string" == Type then temp = "\"" .. value .. "\"" + elseif "rotation" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ", " .. value.s .. ">" + elseif "vector" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ">" + else + temp = temp .. value + end + return temp +end + +function args2string(doType, ...) + local temp = "" + local first = true + + for j,w in ipairs( {...} ) do + if first then first = false else temp = temp .. ", " end + if doType then + temp = temp .. value2string(w, type(w)) + else + temp = temp .. w + end + end + return temp +end + +function mt.callAndReturn(name, ...) + luaproc.sendback(name .. "(" .. args2string(true, ...) .. ")") +end + +function mt.callAndWait(name, ...) + mt.callAndReturn(name, ...); + -- Eventually a sendForth() is called, which should end up passing through SendToChannel(). + -- Wait for the result, which should be a Lua value as a string. + return waitAndProcess(true) +end + +local function newConst(Type, name, value) + LSL[name] = value + return { Type = Type, name = name } +end + +local function newFunc(Type, name, ... ) + functions[name] = { Type = Type, args = {...} } + if "" == Type then + LSL[name] = function(...) mt.callAndReturn(name, ... ) end + else + LSL[name] = function(...) return mt.callAndWait(name, ... ) end + end +end + + +-- LSL constants. + +local constants = +{ + newConst("float", "PI", 3.14159265358979323846264338327950), + newConst("float", "PI_BY_TWO", LSL.PI / 2), -- 1.57079632679489661923132169163975 + newConst("float", "TWO_PI", LSL.PI * 2), -- 6.28318530717958647692528676655900 + newConst("float", "DEG_TO_RAD", LSL.PI / 180.0), -- 0.01745329252 + newConst("float", "RAD_TO_DEG", 180.0 / LSL.PI), -- 57.2957795131 + newConst("float", "SQRT2", 1.4142135623730950488016887242097), + + newConst("integer", "CHANGED_INVENTORY", 0x001), + newConst("integer", "CHANGED_COLOR", 0x002), + newConst("integer", "CHANGED_SHAPE", 0x004), + newConst("integer", "CHANGED_SCALE", 0x008), + newConst("integer", "CHANGED_TEXTURE", 0x010), + newConst("integer", "CHANGED_LINK", 0x020), + newConst("integer", "CHANGED_ALLOWED_DROP", 0x040), + newConst("integer", "CHANGED_OWNER", 0x080), + newConst("integer", "CHANGED_REGION", 0x100), + newConst("integer", "CHANGED_TELEPORT", 0x200), + newConst("integer", "CHANGED_REGION_START", 0x400), + newConst("integer", "CHANGED_MEDIA", 0x800), + + newConst("integer", "DEBUG_CHANNEL", 2147483647), + newConst("integer", "PUBLIC_CHANNEL", 0), + + newConst("integer", "INVENTORY_ALL", -1), + newConst("integer", "INVENTORY_NONE", -1), + newConst("integer", "INVENTORY_TEXTURE", 0), + newConst("integer", "INVENTORY_SOUND", 1), + newConst("integer", "INVENTORY_LANDMARK", 3), + newConst("integer", "INVENTORY_CLOTHING", 5), + newConst("integer", "INVENTORY_OBJECT", 6), + newConst("integer", "INVENTORY_NOTECARD", 7), + newConst("integer", "INVENTORY_SCRIPT", 10), + newConst("integer", "INVENTORY_BODYPART", 13), + newConst("integer", "INVENTORY_ANIMATION", 20), + newConst("integer", "INVENTORY_GESTURE", 21), + + newConst("integer", "ALL_SIDES", -1), + newConst("integer", "LINK_SET", -1), + newConst("integer", "LINK_ROOT", 1), + newConst("integer", "LINK_ALL_OTHERS", -2), + newConst("integer", "LINK_ALL_CHILDREN", -3), + newConst("integer", "LINK_THIS", -4), + + newConst("integer", "PERM_ALL", 0x7FFFFFFF), + newConst("integer", "PERM_COPY", 0x00008000), + newConst("integer", "PERM_MODIFY", 0x00004000), + newConst("integer", "PERM_MOVE", 0x00080000), + newConst("integer", "PERM_TRANSFER", 0x00002000), + newConst("integer", "MASK_BASE", 0), + newConst("integer", "MASK_OWNER", 1), + newConst("integer", "MASK_GROUP", 2), + newConst("integer", "MASK_EVERYONE", 3), + newConst("integer", "MASK_NEXT", 4), + newConst("integer", "PERMISSION_DEBIT", 0x0002), + newConst("integer", "PERMISSION_TAKE_CONTROLS", 0x0004), + newConst("integer", "PERMISSION_TRIGGER_ANIMATION", 0x0010), + newConst("integer", "PERMISSION_ATTACH", 0x0020), + newConst("integer", "PERMISSION_CHANGE_LINKS", 0x0080), + newConst("integer", "PERMISSION_TRACK_CAMERA", 0x0400), + newConst("integer", "PERMISSION_CONTRAL_CAMERA", 0x0800), + + newConst("integer", "AGENT", 0x01), + newConst("integer", "ACTIVE", 0x02), + newConst("integer", "PASSIVE", 0x04), + newConst("integer", "SCRIPTED", 0x08), + + newConst("integer", "OBJECT_UNKNOWN_DETAIL", -1), + + newConst("integer", "PRIM_BUMP_SHINY", 19), + newConst("integer", "PRIM_COLOR", 18), + newConst("integer", "PRIM_FLEXIBLE", 21), + newConst("integer", "PRIM_FULLBRIGHT", 20), + newConst("integer", "PRIM_GLOW", 25), + newConst("integer", "PRIM_MATERIAL", 2), + newConst("integer", "PRIM_PHANTOM", 5), + newConst("integer", "PRIM_PHYSICS", 3), + newConst("integer", "PRIM_POINT_LIGHT", 23), + newConst("integer", "PRIM_POSITION", 6), + newConst("integer", "PRIM_ROTATION", 8), + newConst("integer", "PRIM_SIZE", 7), + newConst("integer", "PRIM_TEMP_ON_REZ", 4), + newConst("integer", "PRIM_TYPE", 9), + newConst("integer", "PRIM_TYPE_OLD", 1), + newConst("integer", "PRIM_TEXGEN", 22), + newConst("integer", "PRIM_TEXTURE", 17), + newConst("integer", "PRIM_TEXT", 26), + + newConst("integer", "PRIM_BUMP_NONE", 0), + newConst("integer", "PRIM_BUMP_BRIGHT", 1), + newConst("integer", "PRIM_BUMP_DARK", 2), + newConst("integer", "PRIM_BUMP_WOOD", 3), + newConst("integer", "PRIM_BUMP_BARK", 4), + newConst("integer", "PRIM_BUMP_BRICKS", 5), + newConst("integer", "PRIM_BUMP_CHECKER", 6), + newConst("integer", "PRIM_BUMP_CONCRETE", 7), + newConst("integer", "PRIM_BUMP_TILE", 8), + newConst("integer", "PRIM_BUMP_STONE", 9), + newConst("integer", "PRIM_BUMP_DISKS", 10), + newConst("integer", "PRIM_BUMP_GRAVEL", 11), + newConst("integer", "PRIM_BUMP_BLOBS", 12), + newConst("integer", "PRIM_BUMP_SIDING", 13), + newConst("integer", "PRIM_BUMP_LARGETILE", 14), + newConst("integer", "PRIM_BUMP_STUCCO", 15), + newConst("integer", "PRIM_BUMP_SUCTION", 16), + newConst("integer", "PRIM_BUMP_WEAVE", 17), + + newConst("integer", "PRIM_HOLE_DEFAULT", 0), + newConst("integer", "PRIM_HOLE_CIRCLE", 16), + newConst("integer", "PRIM_HOLE_SQUARE", 32), + newConst("integer", "PRIM_HOLE_TRIANGLE", 48), + + newConst("integer", "PRIM_MATERIAL_STONE", 0), + newConst("integer", "PRIM_MATERIAL_METAL", 1), + newConst("integer", "PRIM_MATERIAL_GLASS", 2), + newConst("integer", "PRIM_MATERIAL_WOOD", 3), + newConst("integer", "PRIM_MATERIAL_FLESH", 4), + newConst("integer", "PRIM_MATERIAL_PLASTIC", 5), + newConst("integer", "PRIM_MATERIAL_RUBBER", 6), + newConst("integer", "PRIM_MATERIAL_LIGHT", 7), + + newConst("integer", "PRIM_SCULPT_TYPE_SPHERE", 1), + newConst("integer", "PRIM_SCULPT_TYPE_TORUS", 2), + newConst("integer", "PRIM_SCULPT_TYPE_PLANE", 3), + newConst("integer", "PRIM_SCULPT_TYPE_CYLINDER", 4), + newConst("integer", "PRIM_SCULPT_TYPE_MESH", 5), + newConst("integer", "PRIM_SCULPT_TYPE_MIMESH", 6), + + newConst("integer", "PRIM_SHINY_NONE", 0), + newConst("integer", "PRIM_SHINY_LOW", 1), + newConst("integer", "PRIM_SHINY_MEDIUM", 2), + newConst("integer", "PRIM_SHINY_HIGH", 3), + + newConst("integer", "PRIM_TYPE_BOX", 0), + newConst("integer", "PRIM_TYPE_CYLINDER", 1), + newConst("integer", "PRIM_TYPE_PRISM", 2), + newConst("integer", "PRIM_TYPE_SPHERE", 3), + newConst("integer", "PRIM_TYPE_TORUS", 4), + newConst("integer", "PRIM_TYPE_TUBE", 5), + newConst("integer", "PRIM_TYPE_RING", 6), + newConst("integer", "PRIM_TYPE_SCULPT", 7), + + newConst("integer", "STRING_TRIM", 3), + newConst("integer", "STRING_TRIM_HEAD", 1), + newConst("integer", "STRING_TRIM_TAIL", 2), + + newConst("integer", "TRUE", 1), + newConst("integer", "FALSE", 0), + + newConst("integer", "TOUCH_INVALID_FACE", 0x7FFFFFFF), + newConst("vector", "TOUCH_INVALID_TEXCOORD", {x=-1.0, y=-1.0, z=0.0}), + newConst("vector", "TOUCH_INVALID_VECTOR", {x=0.0, y=0.0, z=0.0}), + + newConst("integer", "TYPE_INTEGER", 1), + newConst("integer", "TYPE_FLOAT", 2), + newConst("integer", "TYPE_STRING", 3), + newConst("integer", "TYPE_KEY", 4), + newConst("integer", "TYPE_VECTOR", 5), + newConst("integer", "TYPE_ROTATION", 6), + newConst("integer", "TYPE_INVALID", 0), + + newConst("string", "NULL_KEY", "00000000-0000-0000-0000-000000000000"), + newConst("string", "EOF", "\\n\\n\\n"), -- Corner case, dealt with later. + + newConst("rotation", "ZERO_ROTATION", {x=0.0, y=0.0, z=0.0, s=1.0}), + newConst("vector", "ZERO_VECTOR", {x=0.0, y=0.0, z=0.0}), + +-- TODO - Temporary dummy variables to get vector and rotation thingies to work for now. + + newConst("float", "s", 1.0), + newConst("float", "x", 0.0), + newConst("float", "y", 0.0), + newConst("float", "z", 0.0), +} + +-- ll*() function definitions + +-- LSL avatar functions +newFunc("key", "llAvatarOnSitTarget") +newFunc("list", "llGetAnimationList", "key id") +newFunc("integer", "llGetPermissions") +newFunc("key", "llGetPermissionsKey") +newFunc("string", "llKey2Name", "key avatar") +newFunc("", "llRequestPermissions", "key avatar", "integer perms") +newFunc("integer", "llSameGroup", "key avatar") +newFunc("", "llStartAnimation", "string anim") +newFunc("", "llStopAnimation", "string anim") +newFunc("", "llUnSit", "key avatar") + +-- LSL collision / detect / sensor functions +newFunc("integer", "llDetectedGroup", "integer index") +newFunc("vector", "llDetectedGrab", "integer index") +newFunc("key", "llDetectedKey", "integer index") +newFunc("integer", "llDetectedLinkNumber", "integer index") +newFunc("string", "llDetectedName", "integer index") +newFunc("key", "llDetectedOwner", "integer index") +newFunc("vector", "llDetectedPos", "integer index") +newFunc("rotation", "llDetectedRot", "integer index") +newFunc("vector", "llDetectedTouchBinormal", "integer index") +newFunc("integer", "llDetectedTouchFace", "integer index") +newFunc("vector", "llDetectedTouchNormal", "integer index") +newFunc("vector", "llDetectedTouchPos", "integer index") +newFunc("vector", "llDetectedTouchST", "integer index") +newFunc("vector", "llDetectedTouchUV", "integer index") +newFunc("integer", "llDetectedType", "integer index") +newFunc("vector", "llDetectedVel", "integer index") + + +-- LSL communications functions +newFunc("", "llDialog", "key avatar", "string caption", "list arseBackwardsMenu", "integer channel") +newFunc("integer", "llListen", "integer channel", "string name", "key id", "string msg") +newFunc("", "llListenRemove", "integer handle") +newFunc("", "llOwnerSay", "string text") +newFunc("", "llSay", "integer channel", "string text") +newFunc("", "llShout", "integer channel", "string text") +newFunc("", "llWhisper", "integer channel", "string text") +newFunc("", "llMessageLinked", "integer link", "integer num", "string text", "key aKey") + +-- LSL inventory functions. +newFunc("string", "llGetInventoryName", "integer Type", "integer index") +newFunc("integer", "llGetInventoryNumber", "integer Type") +newFunc("integer", "llGetInventoryType", "string name") +newFunc("key", "llGetNotecardLine", "string name", "integer index") +newFunc("", "llRezAtRoot", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") +newFunc("", "llRezObject", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") + +-- LSL list functions. +newFunc("list", "llCSV2List", "string text") +newFunc("list", "llDeleteSubList", "list l", "integer start", "integer End") +newFunc("string", "llDumpList2String", "list l", "string separator") +newFunc("integer", "llGetListLength", "list l") +newFunc("string", "llList2CSV", "list l") +newFunc("float", "llList2Float", "list l", "integer index") +newFunc("integer", "llList2Integer", "list l", "integer index") +newFunc("key", "llList2Key", "list l", "integer index") +newFunc("list", "llList2List", "list l", "integer start", "integer End") +newFunc("string", "llList2String", "list l", "integer index") +newFunc("rotation", "llList2Rotation", "list l", "integer index") +newFunc("vector", "llList2Vector", "list l", "integer index") +newFunc("integer", "llListFindList", "list l", "list l1") +newFunc("list", "llListInsertList", "list l", "list l1", "integer index") +newFunc("list", "llListReplaceList", "list l", "list part", "integer start", "integer End") +newFunc("list", "llListSort", "list l", "integer stride", "integer ascending") +newFunc("list", "llParseString2List", "string In", "list l", "list l1") +newFunc("list", "llParseStringKeepNulls", "string In", "list l", "list l1") + +-- LSL math functions +newFunc("rotation", "llEuler2Rot", "vector vec") +newFunc("float", "llFrand", "float max") +newFunc("float", "llPow", "float number", "float places") +newFunc("vector", "llRot2Euler", "rotation rot") +newFunc("integer", "llRound", "float number") + +-- LSL media functions +newFunc("", "llPlaySound", "string name", "float volume") + +-- LSL object / prim functions +newFunc("", "llDie") +newFunc("key", "llGetKey") +newFunc("integer", "llGetLinkNumber") +newFunc("string", "llGetObjectDesc") +newFunc("string", "llGetObjectName") +newFunc("key", "llGetOwner") +newFunc("", "llSetObjectDesc", "string text") +newFunc("", "llSetObjectName", "string text") +newFunc("", "llSetPrimitiveParams", "list params") +newFunc("", "llSetSitText", "string text") +newFunc("", "llSetText", "string text", "vector colour", "float alpha") +newFunc("", "llSitTarget", "vector pos", "rotation rot") + +-- LSL rotation / scaling / translation functions +newFunc("vector", "llGetPos") +newFunc("rotation", "llGetRot") +newFunc("", "llSetPos", "vector pos") +newFunc("", "llSetRot", "rotation rot") +newFunc("", "llSetScale", "vector scale") + +-- LSL script functions +newFunc("integer", "llGetFreeMemory") +newFunc("string", "llGetScriptName") +newFunc("", "llResetOtherScript", "string name") +newFunc("", "llResetScript") +newFunc("", "llSetScriptState", "string name", "integer running") + +-- LSL string functions +newFunc("string", "llGetSubString", "string text", "integer start", "integer End") +newFunc("integer", "llStringLength", "string text") +newFunc("string", "llStringTrim", "string text", "integer type") +newFunc("integer", "llSubStringIndex", "string text", "string sub") + +-- LSL texture functions +newFunc("float", "llGetAlpha", "integer side") +newFunc("", "llSetAlpha", "float alpha", "integer side") +newFunc("", "llSetColor", "vector colour", "integer side") + +-- LSL time functions +newFunc("float", "llGetTime") +newFunc("", "llResetTime") +newFunc("", "llSetTimerEvent", "float seconds") +newFunc("float", "llSleep", "float seconds") -- Faked return type, it actually does not return anything. This forces it to wait. Actually fully implements llSleep(). B-) + + +-- TODO - fake this for now. +function --[[integer]] LSL.llGetInventoryType(--[[string]] name) return LSL.INVENTORY_SCRIPT end; + + +-- LSL collision / detect / sensor functions +function --[[integer]] LSL.llDetectedGroup( --[[integer]] index) return detectedGroups [index + 1] or false end +function --[[vector]] LSL.llDetectedGrab( --[[integer]] index) return detectedGrabs [index + 1] or LSL.ZERO_VECTOR end +function --[[key]] LSL.llDetectedKey( --[[integer]] index) return detectedKeys [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY +function --[[integer]] LSL.llDetectedLinkNumber( --[[integer]] index) return detectedLinkNumbers [index + 1] or 0 end +function --[[string]] LSL.llDetectedName( --[[integer]] index) return detectedNames [index + 1] or "" end -- LL says it returns NULL_KEY, LSL Wiki says an empty string. Apparently there used to be an exploit for creating multi kb names, normally they are 255 characters. +function --[[key]] LSL.llDetectedOwner( --[[integer]] index) return detectedOwners [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY +function --[[vector]] LSL.llDetectedPos( --[[integer]] index) return detectedPoss [index + 1] or LSL.ZERO_VECTOR end +function --[[rotation]] LSL.llDetectedRot( --[[integer]] index) return detectedRots [index + 1] or LSL.ZERO_ROTATION end +function --[[vector]] LSL.llDetectedTouchBinormal( --[[integer]] index) return detectedTouchBinormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end +function --[[integer]] LSL.llDetectedTouchFace( --[[integer]] index) return detectedTouchFaces [index + 1] or LSL.TOUCH_INVALID_FACE end +function --[[vector]] LSL.llDetectedTouchNormal( --[[integer]] index) return detectedTouchNormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end +function --[[vector]] LSL.llDetectedTouchPos( --[[integer]] index) return detectedTouchPoss [index + 1] or LSL.TOUCH_INVALID_VECTOR end +function --[[vector]] LSL.llDetectedTouchST( --[[integer]] index) return detectedTouchSTs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end +function --[[vector]] LSL.llDetectedTouchUV( --[[integer]] index) return detectedTouchUVs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end +function --[[integer]] LSL.llDetectedType( --[[integer]] index) return detectedTypes [index + 1] or 0 end +function --[[vector]] LSL.llDetectedVel( --[[integer]] index) return detectedVels [index + 1] or LSL.ZERO_VECTOR end + + +-- LSL list functions. + +--function --[[list]] LSL.llCSV2List(--[[string]] text) return {} end; +function --[[list]] LSL.llDeleteSubList(--[[list]] l,--[[integer]] start,--[[integer]] eNd) + local result = {} + local x = 1 + + -- Deal with the impedance mismatch. + start = start + 1 + eNd = eNd + 1 + for i = 1,#l do + if i < start then result[x] = l[i]; x = x + 1 + elseif i > eNd then result[x] = l[i]; x = x + 1 + end + end + + return result +end + +function --[[string]] LSL.llDumpList2String(--[[list]] l, --[[string]] separator) + local result = "" + for i = 1,#l do + if "" ~= result then result = result .. separator end + result = result .. l[i] + end + return result +end + +function --[[integer]] LSL.llGetListLength(--[[list]] l) + return #l +end + +function --[[string]] LSL.llList2CSV(--[[list]] l) + return LSL.llDumpList2String(l, ",") +end + +function --[[float]] LSL.llList2Float(--[[list]] l,--[[integer]] index) + local result = tonumber(l[index]) + if nil == result then result = 0.0 end + return result +end + +function --[[integer]] LSL.llList2Integer(--[[list]] l,--[[integer]] index) + local result = tonumber(l[index+1]) + if nil == result then result = 0 end + return result +end + +function --[[key]] LSL.llList2Key(--[[list]] l,--[[integer]] index) + local result = l[index+1] + if result then return "" .. result else return LSL.NULL_KEY end +end + +function --[[list]] LSL.llList2List(--[[list]] l,--[[integer]] start,--[[integer]] eNd) + local result = {} + local x = 1 + + --[[ TODO - +Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would capture the entire list. +If start is larger than end the list returned is the exclusion of the entries, so 6, 4 would give the entire list except for the 5th entry. + ]] + + -- Deal with the impedance mismatch. + start = start + 1 + eNd = eNd + 1 + for i = 1,#l do + if i >= start then result[x] = l[i]; x = x + 1 + elseif i <= eNd then result[x] = l[i]; x = x + 1 + end + end + + return result +end + +function --[[string]] LSL.llList2String(--[[list]] l,--[[integer]] index) + local result = l[index+1] + if result then return "" .. result else return "" end +end + +function --[[rotation]] LSL.llList2Rotation(--[[list]] l,--[[integer]] index) + local result = l[index+1] + if nil == result then result = LSL.ZERO_ROTATION end + -- TODO - check if it's not an actual rotation, then return LSS.ZERO_ROTATION + return result +end + +function --[[vector]] LSL.llList2Vector(--[[list]] l,--[[integer]] index) + local result = l[index+1] + if nil == result then result = LSL.ZERO_VECTOR end + -- TODO - check if it's not an actual rotation, then return LSS.ZERO_VECTOR + return result +end + +--function --[[integer]] LSL.llListFindList(--[[list]] l, --[[list]] l1) return 0 end; +function --[[list]] LSL.llListInsertList(--[[list]] l, --[[list]] l1,--[[integer]] index) + local result = {} + local x = 1 + local y + for i = 1,index do + result[x] = l[i] + x = x + 1 + end + y = x + for i = 1,#ll do + result[x] = ll[i] + x = x + 1 + end + for i = y,#l do + result[x] = l[i] + x = x + 1 + end + return result +end + +function --[[list]] LSL.llListReplaceList(--[[list]] l, --[[list]] part,--[[integer]] start,--[[integer]] eNd) + local result = {} + local x = 1 + local y + for i = 1,index do + result[x] = l[i] + x = x + 1 + end + for i = 1,#part do + result[x] = part[i] + x = x + 1 + end + for i = index,#l do + result[x] = l[i] + x = x + 1 + end + return result +end + +function --[[list]] LSL.llListSort(--[[list]] l,--[[integer]] stride,--[[integer]] ascending) + local result = {} + + -- TODO - Deal with stride and ascending. + for i = 1,#l do + result[x] = l[i]; x = x + 1 + end + table.sort(result) + + return result +end + +--function --[[list]] LSL.llParseString2List(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end; +--function --[[list]] LSL.llParseStringKeepNulls(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end; + + +-- LSL script functions + +function --[[string]] LSL.llGetScriptName() + return scriptName +end + + +-- LSL string functions + +function --[[string]] LSL.llGetSubString(--[[string]] text, --[[integer]] start, --[[integer]] End) + -- Deal with the impedance mismatch. + if 0 <= start then start = start + 1 end + if 0 <= End then End = End + 1 end +-- TODO - If start is larger than end the substring is the exclusion of the entries, so 6,4 would give the entire string except for the 5th character. + return string.sub(text, start, End) +end + +function --[[integer]] LSL.llSubStringIndex(--[[string]] text, --[[string]] sub) + local start, End = string.find(text, sub, 1, true) + + if nil == start then return -1 else return start - 1 end +end + + +-- Crements stuff. + +function LSL.preDecrement(name) _G[name] = _G[name] - 1; return _G[name]; end; +function LSL.preIncrement(name) _G[name] = _G[name] + 1; return _G[name]; end; +function LSL.postDecrement(name) local temp = _G[name]; _G[name] = _G[name] - 1; return temp; end; +function LSL.postIncrement(name) local temp = _G[name]; _G[name] = _G[name] + 1; return temp; end; + + +-- State stuff + +function LSL.stateChange(x) + if currentState ~= x then -- Changing to the same state is a NOP. + -- TODO - Should clear out pending events, except timer() + -- Also forget about any event setup that needs setting up via some ll*() function, except timer(). + if nil ~= currentState.state_exit then + currentState.state_exit(); + end + currentState = x; + --[[ Never return to the current states event handler. In theory. lol + Notably, it's not actually legal to do a state change from a function, only from handlers. + There is a hack though, but it's unsupported, so I don't have to worry about it so much. + + Write out "state new;" as "return _LSL.stateChange(newState);", with stateChange() returning new.state_entry()) which will force two tail calls. + + The caller of stateChange() might be a function rather than an event handler. + Which will return to the event handler (possibly via other nested function calls) as if the state never changed. + http://lslwiki.net/lslwiki/wakka.php?wakka=FunctionStateChangeHack seems to imply that this is exactly what LSL does. + Soooo, this might actually work perfectly. + Except for one minor quirk, as that page shows - only the top level function's state sticks, unless the handler does one to, then the handlers one overrides things. + Which I can probably ignore anyway, as this entire calling states within functions thing is an unsupported hack. + ]] + if nil ~= currentState.state_entry then + return currentState.state_entry(); + end + end +end; + +function LSL.mainLoop(sid, name, x) + local status, errorMsg + local result + + SID = sid + scriptName = name + LSL.EOF = "\n\n\n" -- Fix this up now. + + LSL.stateChange(x); + waitAndProcess(false) + msg("Script quitting.") +end + +function waitAndProcess(returnWanted) + local Type = "event" + + if returnWanted then Type = "result" end + while running do + local message = luaproc.receive(SID) + if message then + -- TODO - should we be discarding return values while paused? I don't think so, so we need to process those, + if paused then + if "start()" == message then paused = false end + else + result, errorMsg = loadstring(message) -- "The environment of the returned function is the global environment." Though normally, a function inherits it's environment from the function creating it. Which is what we want. lol + if nil == result then + msg("Not a valid " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg) + else + -- Set the functions environment to ours, for the protection of the script, coz loadstring sets it to the global environment instead. + -- TODO - On the other hand, we will need the global environment when we call event handlers. So we should probably stash it around here somewhere. + -- Meh, seems to be working fine as it is. + setfenv(result, getfenv(1)) + status, result = pcall(result) + if not status then + msg("Error from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. result) + elseif result then + -- Check if we are waiting for a return, and got it. + if returnWanted and string.match(message, "^return ") then + return result + end + -- Otherwise, just run it and keep looping. + status, errorMsg = luaproc.send(sid, result) + if not status then + msg("Error sending results from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg) + end + end + end + end + end + end +end + +-- Typecasting stuff. + +function LSL.floatTypecast(x) + local temp = tonumber(x) + if nil == temp then temp = 0 end + return temp; +end + +function LSL.integerTypecast(x) + local temp = tonumber(x) + if nil == temp then temp = 0 end + return temp; +end + +function LSL.keyTypecast(x) + return "" .. x; +end + +function LSL.listTypecast(x) + return {x}; +end + +function LSL.rotationTypecast(x) + return x; +end + +function LSL.stringTypecast(x) + return "" .. x; +end + +function LSL.vectorTypecast(x) + return x; +end + + +-- Called at compiler set up time, to produce the constants.lsl file. +function LSL.gimmeLSL() + for i,v in ipairs(constants) do + local value = LSL[v.name] + + print(v.Type .. " " .. v.name .. " = " .. value2string(value, v.Type) .. ";") + end + + for k in pairs(functions) do + local v = functions[k] + if nil == v.args then + print(v.Type .. " " .. k .. "(){}") + else + print(v.Type .. " " .. k .. "(" .. args2string(false, unpack(v.args)) .. "){}") + end + end +end + + +return LSL; diff --git a/lib/skang.lua b/lib/skang.lua new file mode 100644 index 0000000..633a574 --- /dev/null +++ b/lib/skang.lua @@ -0,0 +1,1686 @@ +--[[ TODO - This should be in C, but so far development has been quite rapid doing it in Lua. + +C will let us - + Actually do the widget stuff. + Slap meta tables on all value types. + Which lets us put the meta table on the variable, instead of on the table, which I think is cleaner. + Figure out the directory separator. + Network stuff. No need to look at Lua socket stuff, we have Ecore_Con. + Database stuff. No need to look at Lua SQL stuff, we have esskyuehl. Maybe. + + Actually, we could have the best of both worlds, since it is currently a C / Lua hybrid. B-) +]] + + +--[[ Skang package + +In here should live all the internals of matrix-RAD that don't +specifically relate to widgets. This would include the "window" though. + +skang.module(Evas) +skang.module(Elementary) +skang.skang('some/skang/file.skang') + +This package is also what "apps" that use the system should "inherit" +from, in the same way matrix-RAD apps did. Skang "apps" could be Lua +modules. They could also be C code, like the extantz modules are likely +to be. Skang "apps" would automatically be associated with skang files +of the same name. + +For a .skang file, the skang command (written in C) would strip off the +first line, add the two implied lines, then run it as Lua. The +skang.load() command would do the same. So that skang C comand would +just pass the file name to skang.load() in this library. B-) + +The old skang argument types are - + +{"name", "java.lang.String"}, +{"action", "java.lang.String"}, +{"type", "java.lang.String"}, +{"data", "java.lang.String"}, +{"URL", "java.lang.String"}, +{"file", "java.lang.String"}, +{"method", "java.lang.String"}, +{"lx", "java.lang.String"}, +{"ly", "java.lang.String"}, +{"lw", "java.lang.String"}, +{"lh", "java.lang.String"}, +{"normal", "java.lang.String"}, +{"ghost", "java.lang.String"}, +{"active", "java.lang.String"}, +{"toggle", "java.lang.String"}, +{"boolean","java.lang.Boolean"}, +{"number", "java.lang.Integer"}, +{"int", "java.lang.Integer"}, +{"x", "java.lang.Integer"}, +{"y", "java.lang.Integer"}, +{"w", "java.lang.Integer"}, +{"h", "java.lang.Integer"}, +{"r", "java.lang.Integer"}, +{"g", "java.lang.Integer"}, +{"b", "java.lang.Integer"}, +{"alpha", "java.lang.Integer"}, +{"acl", "net.matrix_rad.security.ACL"}, +]] + + +-- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular. +-- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module". +do -- Only I'm not gonna indent this. + +mainSkin = {} + +-- TODO - This needs to be expanded a bit to cover things like 1.42 +local versions = { + '0%.0', 'unwritten', 'Just a stub, no code at all, or completely non-existant.', + '0%.1', 'prototype', 'Stuff that has only been prototyped, or partly written. There is some code, and it may even work, but it is not even close to the finished product.', + '%d%.3', 'written', 'Stuff that has already been written. It may not be perfect, but it is considered an aproximation of the finished product.', + '%d%.5', 'alpha', 'Version being tested by official alpha testers.', + '%d%.9', 'beta', 'Version passed alpha testing, but not ready for final release.', + '1%.0', 'final', 'Version ready for final release, fully tested.', + '3%.0', 'poetry', 'Near perfection has been acheived.', + '5%.0', 'nirvana', 'Perfection has been acheived.', + '9%.0', 'bible', 'This is the Whord of Ghod.', +} + +-- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. +-- TODO - Should parse in license type to. +moduleBegin = function (name, author, copyright, version, timestamp, skin, isLua) + local _M = {} -- This is what we return to require(). + local level = 2 + + if 'nil' == type(isLua) then isLua = true end + + package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. + -- Returning it at the end does the same thing. + -- This is so that we can have all the module stuff at the top, in this function. + -- Should do this before any further require(), so that circular references don't blow out. + + -- Save the callers environment. + local savedEnvironment + if isLua then + savedEnvironment = getfenv(level) + else + -- While the above works fine for test_c, it doesn't for GuiLua. Odd. + savedEnvironment = getfenv(1) + end + + -- Clone the environment into _M, so the module can access everything as usual after the setfenv() below. + --[[ TODO - Check if this also clones _G or _ENV. And see if it leaks stuff in either direction. + local _G = _G -- Only sets a local _G for this function. + _M._G = _G -- This clone loop might do this, but we don't want to be able to access the old _G from outside via this leak. + In Lua 5.1 at least, _G was special. In 5.2, _ENV sorta replaces setfenv(), but no idea if this clone loop stomps on that. + ]] + for k, v in pairs(savedEnvironment) do + _M[k] = v + end + + _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. + _M._NAME = name + _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. + _M.isLua = isLua + + -- Parse in an entire copyright message, and strip that down into bits, to put back together. + local date, owner = string.match(copyright, '[Cc]opyright (%d%d%d%d) (.*)') + _M.AUTHOR = author or owner + _M.COPYRIGHT = 'Copyright ' .. date .. ' ' .. _M.AUTHOR + -- Translate the version number into a version string. + local versionName, versionDesc = ' ', '' + for i = 1, #versions / 3 do + if 1 == string.find(version, versions[i]) then + versionName = ' ' .. versions[i + 1] .. ' ' + versionDesc = versions[i + 2] + break + end + end + _M.VERSION = version .. versionName .. timestamp + _M.VERSION_DESC = versionDesc + -- If there is a .skang file, read that in and override the passed in skin. + local f = io.open(name .. '.skang') + if f then + skin = f:read('*l') + if '#' == string.sub(skin, 1, 1) then skin = '' end + skin = skin .. f:read('*a') + f:close() + end + if skin then + skin = "local skang = require 'skang'\nlocal " .. name .. " = require '" .. name .. "'\n" .. skin + if nil == mainSkin._NAME then mainSkin = _M end + end + _M.DEFAULT_SKANG = skin + + --_G[_M._NAME] = _M -- Stuff it into a global of the same name. + -- Not such a good idea to stomp on global name space. + -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway. + -- This is why module() is broken. + _M.savedEnvironment = savedEnvironment + -- NOTE - setfenv() wont work if the environment it refers to is a C function. Worse, getfenv() returns the global environment, so we can't tell. + if isLua then + -- setfenv() sets the environment for the FUNCTION, stack level deep. + -- The number is the stack level - + -- 0 running thread, 1 current function, 2 function that called this function, etc + setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. + -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). + -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. + end + + print('Loaded module ' .. _M._NAME .. ' version ' .. _M.VERSION .. ', ' .. _M.COPYRIGHT .. '.\n ' .. _M.VERSION_DESC) + + return _M +end + + +--[[ Parse command line parameters. + +This is done in two parts. Skang will do an initial scan and tokenise, +then each module gets a chance to pull it's own Things from the result. + +Make the command line parameter getting MUCH more intelligent, try to support the common +command line interfaces - + +arg value +a value +/arg value +/a value +--arg value +--a value +-a value +-ab ('a' and 'b' are both shortcuts.) +arg=value +a=value +arg1=value1&arg2=value2 +arg1=value1|arg2=value2 +a=value1&a=value2 ++arg/-arg (Can't support this generically.) + +Ignore /,-,--,& except as arg introducers. Use = as value introducer. Expect +arg or a. If type is String, expect a value. If type is integer, and next token is +not an integer, increment current value, otherwise expect integer value. If type is +boolean, value beginning with T, t, F, f, etc is true, otherwise value is false, unless +next token starts with an introducer, then value is true. + +TODO - Finish supporting all of the above. + These all need deeper parsing, but we dunno if they might have been inside quoted strings from the shell. + arg=value Shell. + arg1=value1&arg2=value2 For URLs. + arg1=value1|arg2=value2 Can't remember why, probably the old skang multivalue syntax. + Test it all. + Skang command line should have standardish stuff, like --version, --help, --help module.thing. + Lua does these already, might be no need to do them ourselves - + -e 'some code'. + -i go interactive after running the script. + -v version. + - read from stdin non interactively. + LuaJIT also has this - + -- stop processing options. +]] + +ARGS = {} +lua = '' +command = '' + + +-- Do an initial scan and tokenise of the command line arguments. +scanArguments = function (args) + if args then + lua = args[-1] + command = args[0] + for i, v in ipairs(args) do + local pre = '' + if '--' == string.sub(v, 1, 2) then pre = '--'; v = string.sub(v, 3, -1) end + if '-' == string.sub(v, 1, 1) then pre = '-'; v = string.sub(v, 2, -1) end + if '+' == string.sub(v, 1, 1) then pre = '+'; v = string.sub(v, 2, -1) end + -- TODO - Make this the opposite of the directory separator for what ever platform we are running on. + -- Which Lua can't figure out I think. + if '/' == string.sub(v, 1, 1) then pre = '/'; v = string.sub(v, 2, -1) end + if '=' == string.sub(v, 1, 1) then pre = '='; v = string.sub(v, 2, -1) end + if '&' == string.sub(v, 1, 1) then pre = '&'; v = string.sub(v, 2, -1) end + if '|' == string.sub(v, 1, 1) then pre = '|'; v = string.sub(v, 2, -1) end + if '' ~= v then ARGS[i] = {pre, v} end + end + end +end + +parseType = function (module, thingy, v, value) + if 'string' == thingy.types[1] then + if value then + module[v[2] ] = value[2] + value[2] = nil -- Mark it as used. + else + print('ERROR - Expected a string value for ' .. thingy.names[1]) + end + end + + if 'number' == thingy.types[1] then + if value then + -- If the introducer is '-', then this should be a negative number. + if '-' == value[1] then value[1] = ''; value[2] = '-' .. value[2] end + -- Only parse the next value as a number if it doesn't have an introducer. + if ('' == value[1]) or ('=' == value[1]) then + value[2] = tonumber(value[2]) + if value[2] then + module[v[2] ] = value[2] + value[2] = nil -- Mark it as used. + else + print('ERROR - Expected a number value for ' .. thingy.names[1]) + end + else + module[v[2] ] = module[v[2] ] + 1 + end + else + print('ERROR - Expected a number value for ' .. thingy.names[1]) + end + end + + if 'function' == thingy.types[1] then + local args = {} + -- TODO - Should allow more than one argument, but would need to pass in ARGS and i. + if 2 == #thingy.types then + if value then + -- TODO - Should check the type of the arguments. + args[#args + 1] = value[2] + module[v[2] ](args[1]) + value[2] = nil -- Mark it as used. + else + print('ERROR - Expected an argument for ' .. thingy.names[1]) + end + else + module[v[2] ]() + end + end + + if 'boolean' == thingy.types[1] then + if value then + -- Only parse the next value as a boolean if it doesn't have an introducer. + if ('' == value[1]) or ('=' == value[1]) then + module[v[2] ] = isBoolean(value[2]) + value[2] = nil -- Mark it as used. + else + module[v[2] ] = true + end + else + print('ERROR - Expected a boolean value for ' .. thingy.names[1]) + end + end +end + +pullArguments = function (module) + -- Look for our command line arguments. + local metaMum = getmetatable(module) + if metaMum and metaMum.__self then + for i, v in ipairs(ARGS) do + if v[2] then + local thingy = metaMum.__self.stuff[v[2] ] + -- Did we find one of ours? + if thingy then + parseType(module, thingy, v, ARGS[i + 1]) + v[2] = nil -- Mark it as used. + else + -- Didn't find one directly, check for single letter matches in '-abc'. + for k, w in pairs(metaMum.__self.stuff) do + if 1 == #w.names[1] then + for j = 1, #v[2] do + if string.sub(v[2], j, 1) == w.names[1] then + if 1 == j then + v[2] = string.sub(v[2], 2, -1) + if 'boolean' == w.types[1] then module[v[2] ] = true end + elseif #v[2] == j then + v[2] = string.sub(v[2], 1, j - 1) + -- The one at the end is the only one that could have a following value. + parseType(module, w, v, ARGS[i + 1]) + else + v[2] = string.sub(v[2], 1, j - 1) .. string.sub(v[2], j + 1, -1) + if 'boolean' == w.types[1] then module[v[2] ] = true end + end + if '' == v[2] then v[2] = nil end -- Mark it as used. + end + end + end + end + end + end + end + end +end + +-- Restore the environment, and grab paramateres from standard places. +moduleEnd = function (module) + -- See if there is a properties file, and run it in the modules environment. + local properties, err = loadfile(module._NAME .. '.properties') + if properties then + setfenv(properties, getfenv(2)) + properties() + elseif 'cannot open ' ~= string.sub(err, 1, 12) then + print("ERROR - " .. err) + end + + pullArguments(module) + + -- Run the main skin, which is the first skin that is defined. In theory, the skin from the main module. + if mainSkin == module then + print("RUNNING SKIN FOR " .. module._NAME) + local skin, err = loadstring(module.DEFAULT_SKANG) + if skin then + setfenv(skin, getfenv(2)) + skin() + else + print("ERROR - " .. err) + end + end + + if module.isLua then setfenv(2, module.savedEnvironment) end +end + + +-- Call this now so that from now on, this is like any other module. +local _M = moduleBegin('skang', 'David Seikel', 'Copyright 2014 David Seikel', '0.1', '2014-03-27 02:57:00') + +-- This works coz LuaJIT automatically loads the jit module. +if type(jit) == 'table' then + print('Skang is being run by ' .. jit.version .. ' under ' .. jit.os .. ' on a ' .. jit.arch) +else + print('Skang is being run by Lua version ' .. _VERSION) +end + +scanArguments(arg) + + +function printTableStart(table, space, name) + print(space .. name .. ": ") + print(space .. "{") + printTable(table, space .. " ") + print(space .. "}") + if '' == space then print('') end +end + +function printTable(table, space) + if nil == table then return end + for k, v in pairs(table) do + if type(v) == "table" then + if v._NAME then + print(space .. "SKANG module " .. v._NAME .. ";") + else + printTableStart(v, space, k) + end + elseif type(v) == "string" then + print(space .. k .. ': "' .. v .. '";') + elseif type(v) == "function" then + print(space .. "function " .. k .. "();") + elseif type(v) == "userdata" then + print(space .. "userdata " .. k .. ";") + elseif type(v) == "boolean" then + if (v) then + print(space .. "boolean " .. k .. " TRUE ;") + else + print(space .. "boolean " .. k .. " FALSE ;") + end + else + print(space .. k .. ": " .. v .. ";") + end + end +end + + +csv2table = function (csv) + local result = {} + local i = 1 + + for v in string.gmatch(csv, ' *([^,]+)') do + result[i] = v + i = i + 1 + end + return result +end + + +shiftLeft = function (tab) + local result = tab[1] + table.remove(tab, 1) + return result +end + + +-- My clever boolean check, this is the third language I've written this in. B-) +-- true 1 yes ack ok one positive absolutely affirmative 'ah ha' 'shit yeah' 'why not' +local isTrue = 't1aopswy' +-- false 0 no nack nope zero negative nah 'no way' 'get real' 'uh uh' 'fuck off' 'bugger off' +local isFalse = 'f0bgnuz' +isBoolean = function (aBoolean) + local result = false + + if type(aBoolean) ~= 'nil' then + -- The default case, presence of a value means it's true. + result = true + if type(aBoolean) == 'boolean' then result = aBoolean + elseif type(aBoolean) == 'function' then result = aBoolean() + elseif type(aBoolean) == 'number' then result = (aBoolean ~= 0) + elseif type(aBoolean) == 'string' then + if '' == aBoolean then + result = false + else + if 1 == string.find(string.lower(aBoolean), '^[' .. isTrue .. ']') then result = true end + if 1 == string.find(string.lower(aBoolean), '^[' .. isFalse .. ']') then result = false end + end + end + end + return result +end + + +--[[ Thing Java package + +matrix-RAD had Thing as the base class of everything. + +Each "users session" (matrix-RAD term that came from Java +applets/servlets) has a ThingSpace, which is a tree that holds +everything else. It holds the class cache, commands, loaded modules, +variables and their values, widgets and their states. In matrix-RAD I +built BonsiaTree and LeafLike, for the old FDO system I built dumbtrees. + +Other Thing things are - + get/set The getter and setter. + number No idea how this was useful. + skang The owning object, a Skang (actually got this, called module for now). + owner The owning object, a String (module._NAME). + clas Class of the Thing, a Class. (pointless) + type Class of the Thing, a String. (pointless) + realType Real Class of the Thing, a String. (pointless) + myRoot ThingSpace we are in, a ThingSpace. + + Also various functions to wrap checking the security, like canDo, canRead, etc. +]] + + +--[[ Stuff Java package + +In matrix-RAD Stuff took care of multi value Things, like database rows. + +Stuff is an abstract class that gets extended by other classes, like +SquealStuff, which was the only thing extending it. It dealt with the +basic "collection of things" stuff. Each individual thing was called a +stufflet. A final fooStuff would extend SquealStuff, and include an +array of strings called "stufflets" that at least named the stufflets, +but could also include metadata and links to other Stuffs. + +There was various infrastructure for reading and writing Stuff, throwing +rows of Stuff into grids, having choices of Stuff, linking stufflets to +individual widgets, having default Stuffs for windows, validating +Stuffs, etc. + +In Lua, setting up stuff has been folded into the general Thing stuff. + +]] + + +--[[ Thing structure - + +In the following, meta(table) is short for getmetatable(table). + +In Lua everything is supposed to be a first class value, and variables are just places to store values. +All variables are in fact stored in tables, even if it's just the local environment table. +Any variable that has a value of nil, doesn't actually exist. That's the definition. +While non table things can have metatables, Lua can only set metatables on tables, C has no such restriction. +meta(table).__index and __newindex only work on table entries that don't exist. + __index(table, key) is called if table.key is nil. + Though if __index is a table, then try __index[key]. + __newindex(table, key, value) is called if table.key is nil. + Though if __newindex is a table, then try __newindex[key] = value. +Using both __index and __newindex, and keeping the actual values elsewhere, is called a proxy table. +meta(table).__call(table, ...) is called when trying to access table as a function - table(...). + +It's worth repeating - +All variables in Lua are in some table somewhere, even if it's just the global environment table. +Metatables are only associated vith values, not variables. +Lua can only attach metatables to values that are tables, but C can attach metatables to any value. + + +A Thing is a managed variable stored in a parent proxy table, which is usually empty. +So the values stored in this Thing are actually stored in meta(parent)__values[thing]. + parent[thing] -> __index (parent, thing) -> meta(parent).__values[thing] + parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value + + +Each Thing has a description table that includes - + names - An array of names, the first one is the "official" name. + types - An array of types, the first one is the "official" type. + help - A descriptive text for humans to read. + default - The default value. + widget - A default widget definition. + required - If the Thing is required. + isValid - A function that tests if the Thing is valid. + errors - Any errors related to the Thing. + isKeyed - Is this a parent for Things that are stored under an arbitrary key. + stuff - An array of descriptions for sub Things, so Things that are tables can have their own Things. + and other things that aren't actually used yet. +All things that a description doesn't have should be inherited from the Thing table. + setmetatable(aStuff, {__index = Thing}) +Descriptions should be able to be easily shared by various Things. + + +A parent's metatable has __self, which is it's own description. +A parent is free to use it's own name space for anything it wants. +Only the variables it specifies as managed Things are dealt with here. + + +Things that are tables are treated differently, in two different ways even. +Ordinary table Things are basically treated recursively, the table is a parent, and it gets it's own Things. +There is also 'Keyed' type table Things, where the keys to this table are arbitrary, but we still want to store Things in it. +In this case, when a table is assigned to this Keyed Thing, via a new key, a new table Thing is created by copying the parent Thing description. + + +TODO - + test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.stuff[foo], then return an empty table instead? + Do we still need a parent pointer? + Should be in __values I guess. + __values[key].value + __values[key].parent + Weak references might help in here somewhere. + Maybe try looking in the skang table for Things that are not found? + Maybe put Things in the skang table that are unique from modules? + I think this is what matrix-RAD Collisions was all about. +]] + +-- There is no ThingSpace, or Stuff, now it's all just in this meta table. +local Thing = +{ +-- Default Thing values. + names = {'unknown'}, + help = 'No description supplied.', -- help text describing this Thing. + default = '', -- The default value. This could be a funcion, making this a command. + types = {}, -- A list of types. The first is the type of the Thing itself, the rest are for multi value Things. Or argument types for commands. + required = false, -- Is this thing is required. TODO - Maybe fold this into types somehow, or acl? + widget = '', -- Default widget command arguments for creating this Thing as a widget. +-- acl = '', -- Access Control List defining security restrains. +-- boss = '', -- The Thing or person that owns this Thing, otherwise it is self owned. + + action = 'nada', -- An optional action to perform. + tell = '', -- The skang command that created this Thing. + pattern = '.*', -- A pattern to restrict values. + + isKeyed = false, -- Is this thing an arbitrarily Keyed table? + isReadOnly = false, -- Is this Thing read only? + isServer = false, -- Is this Thing server side? + isStub = false, -- Is this Thing a stub? + isStubbed = false, -- Is this Thing stubbed elsewhere? + + hasCrashed = 0, -- How many times this Thing has crashed. + + append = function (self,data) -- Append to the value of this Thing. + end, + + stuff = {}, -- The sub things this Thing has, for modules, tables, and Keyed tables. + errors = {}, -- A list of errors returned by isValid(). + + isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors. + -- Anything that overrides this method, should call this super method first. + local name = self.names[1] + local metaMum = getmetatable(parent) + local value = metaMum.__values[name] + local mum = metaMum.__self.names[1] + + local t = type(value) or 'nil' + self.errors = {} + -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions. + if 'nil' == t then + if self.required then table.insert(self.errors, mum .. '.' .. name .. ' is required!') end + else + if 'widget' == self.types[1] then + -- TODO - Should validate any attached Thing. + elseif self.types[1] ~= t then table.insert(self.errors, mum .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!') + else + if 'number' == t then value = '' .. value end + if ('number' == t) or ('string' == t) then + if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mum .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end + end + end + end + + for k, v in pairs(self.stuff) do + if not v:isValid(value) then + for i, w in ipairs(v.errors) do + table.insert(self.errors, w) + end + end + end + + return #(self.errors) == 0 + end, + + remove = function (self) -- Delete this Thing. + end, +} + + +getStuffed = function (parent, key) + local metaMum = getmetatable(parent) + local thingy + + if metaMum and metaMum.__self then + thingy = metaMum.__self.stuff[key] + + if not thingy then + -- Deal with getting a table entry. + if metaMum.__values[key] then + thingy = getmetatable(metaMum.__values[key]).__self + end + end + end + return metaMum, thingy +end + +local Mum = +{ + +__index = function (parent, key) + -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. + + -- First see if this is a Thing. + local metaMum, thingy = getStuffed(parent, key) + + if thingy then + return metaMum.__values[thingy.names[1] ] or thingy.default + end + + -- Then see if we can inherit it from Thing. + return Thing[key] +end, + +__newindex = function (parent, key, value) + -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. + + -- First see if this is a Thing. + local metaMum, thingy = getStuffed(parent, key) + + if not thingy then + if metaMum.__self.isKeyed then + -- Deal with setting a new Keyed table entry. + local newThing = copy(parent, key) + local newSelf = getmetatable(newThing).__self + rawset(metaMum.__values, key, newThing) + thingy = {} + for k, v in pairs(newSelf) do + thingy[k] = v + end + thingy.names={key} + thingy.types={'table'} + setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. + end + end + + if thingy then + local name = thingy.names[1] + local oldMum + + if 'table' == type(value) then + -- Coz setting it via metaMum screws with the __index stuff somehow. + local oldValue = metaMum.__values[name] + if 'table' == type(oldValue) then + oldMum = getmetatable(oldValue) + if oldMum then + -- TODO - This SHOULD work, but doesn't - + --setmetatable(value, oldMum) + -- Instead we do this - + -- Clear out any values in the old table. + for k, v in pairs(oldMum.__values) do + oldMum.__values[k] = nil + end + for k, v in pairs(value) do + local newK = oldMum.__self.stuff[k] + if newK then newK = newK.names[1] else newK = k end + oldMum.__values[newK] = v + end + end + end + end + if nil == oldMum then metaMum.__values[name] = value end + -- NOTE - invalid values are still stored, this is by design. + if not thingy:isValid(parent) then + for i, v in ipairs(thingy.errors) do + print('ERROR - ' .. v) + end + end + -- TODO - Go through it's linked things and set them to. + -- Done, don't fall through to the rawset() + return + end + + rawset(parent, key, value) -- Stuff it normally. +end, + +__call = function (func, ...) + return thingasm(func, ...) +end, + +} + +newMum = function () + local result = {} +--[[ From an email by Mike Pall - +"Important: create the metatable and its metamethods once and reuse +the _same_ metatable for _every_ instance." + +This is for LuaJIT, he's the author, and concerns performance. + +TODO - Which is the exact opposite of what we are doing. Perhaps we can fix that? +]] + for k, v in pairs(Mum) do + result[k] = v + end + result.__self = {stuff={}} + result.__values = {} + return result +end + + +-- skang.thingasm() Creates a new Thing, or changes an existing one. +--[[ It can be called in many different ways - + +It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss) +Or it can be called with a table - {names, help, pattern='.*', acl='rwx'} + +The first argument can be another Thing (the parent), or a string list of names (see below). + +It can be called by itself, with no parent specified - + thingasm('foo', 'help text) +In this case the surrounding Lua environment becomes the parent of foo. + If the first argument (or first in the table) is a string, then it's this form. +All others include the parent as the first argument, which would be a table. + +It can be called by calling the parent as a function - + foo('bar', 'some help', types='table') -- ___call(foo, 'bar', ...) And now foo is the parent. + foo.bar{'baz', types='Keyed'} -- thingasm({foo.bar, 'baz', ...}) + foo.bar.baz{'field0'} -- thingasm({foo.bar.baz, 'field0'}) + foo.bar.baz{'field1'} +]] + +-- names - a comma seperated list of names, aliases, and shortcuts. The first one is the official name. +-- If this is not a new thing, then only the first one is used to look it up. +-- So to change names, use skang.thingasm{'oldName', names='newName,otherNewName'} +thingasm = function (names, ...) + local params = {...} + local new = false + local parent + local set = true + + -- Check how we were called, and re arrange stuff to match. + if 0 == #params then + if ('table' == type(names)) then -- thingasm{...} + params = names + names = shiftLeft(params) + if 'table' == type(names) then -- thingasm{parent, 'foo', ...} + parent = names + names = shiftLeft(params) + end + end -- thingasm("foo") otherwise + else + if 'table' == type(names) then + parent = names + if 'string' == type(...) then params = {...} -- C or __call(table, string, ..) + elseif 'table' == type(...) then params = ... -- __call(table, table) + end + names = shiftLeft(params) + end -- thingasm('foo', ...) otherwise + end + + -- Break out the names. + names = csv2table(names) + local name = names[1] + local oldNames = {} + + -- TODO - Double check this comment - No need to bitch and return if no names, this will crash for us. + + -- Grab the environment of the calling function if no parent was passed in. + parent = parent or getfenv(2) + local metaMum = getmetatable(parent) + -- Coz at module creation time, Thing is an empty table, or in case this is for a new parent. + if nil == metaMum then + metaMum = newMum() + metaMum.__self.names = {parent._NAME or 'NoName'} + if parent._NAME then metaMum.__self.types = {'Module'} end + setmetatable(parent, metaMum) + end + + local thingy = metaMum.__self.stuff[name] + if not thingy then -- This is a new Thing. + new = true + thingy = {} + thingy.names = names + thingy.stuff = {} + setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. + end + + -- Pull out positional arguments. + thingy.help = params[1] or thingy.help + thingy.default = params[2] or thingy.default + local types = params[3] or table.concat(thingy.types or {}, ',') + + -- Pull out named arguments. + for k, v in pairs(params) do + if 'string' == type(k) then + if 'types' == k then types = v + elseif 'names' == k then + oldNames = thingy.names + thingy.names = cvs2table(v) + elseif 'required' == k then + if isBoolean(v) then thingy.required = true end + else thingy[k] = v + end + end + end + + -- Find type, default to string, then break out the other types. + local typ = type(thingy.default) + if 'nil' == typ then typ = 'string' end + if 'function' == typ then types = typ .. ',' .. types end + if '' == types then types = typ end + thingy.types = csv2table(types) + + if 'widget' == thingy.types[1] then + set = false + local args, err = loadstring('return ' .. thingy.widget) + if args then + setfenv(args, parent) + thingy.Cwidget = widget(args()) +print('\nNO IDEA WHY this does isValid() three times on the action, and the first one being a string.') + parent.W[name] = thingy + else + print("ERROR - " .. err) + end + end + + -- Deal with Keyed and tables. + if 'Keyed' == thingy.types[1] then + set = false + thingy.types[1] = 'table' + thingy.isKeyed = true + end + if 'table' == thingy.types[1] then + -- Default in this case becomes a parent. + if '' == thingy.default then thingy.default = {} end + local thisMum = newMum() + thisMum.__self = thingy + setmetatable(thingy.default, thisMum) + end + + if 'userdata' == thingy.types[1] then + set = false + end + + -- Remove old names, then stash the Thing under all of it's new names. + for i, v in ipairs(oldNames) do + metaMum.__self.stuff[v] = nil + end + for i, v in ipairs(thingy.names) do + metaMum.__self.stuff[v] = thingy + end + + -- This triggers the Mum.__newindex metamethod above. If nothing else, it triggers thingy.isValid() + if new and set then parent[name] = thingy.default end +end + + +fixNames = function (module, name) + local stuff = getmetatable(module) + if stuff then + stuff.__self.names[1] = name + for k, v in pairs(stuff.__self.stuff) do + if 'table' == v.types[1] then + local name = v.names[1] + print(name .. ' -> ' .. name) + fixNames(stuff.__values[name], name) + end + end + end +end + + +copy = function (parent, name) + local result = {} + local stuff = getmetatable(parent).__self.stuff + + for k, v in pairs(stuff) do + local temp = {} + for l, w in pairs(v) do + temp[l] = w + end + temp[1] = table.concat(temp.names, ',') + temp.names = nil + temp.types = table.concat(temp.types, ',') + temp.errors = nil + thingasm(result, temp) + end + getmetatable(result).__self.names = {name} + +-- TODO - Should we copy values to? + + return result +end + +module = function (name) + _G[name] = require(name) + return _G[name] +end + +stuff = function (aThingy, aStuff) + return getmetatable(aThingy).__self.stuff[aStuff] +end + + +get = function (stuff, key, name) + local result + if name then + local thingy = getmetatable(stuff) + if thingy then + local this = thingy.__self.stuff[key] + if this then result = this[name] end + end + else + result = stuff[key] + end + return result +end + + +reset = function (stuff, key, name) + if name then + local thingy = getmetatable(stuff) + if thingy then + local this = thingy.__self.stuff[key] + if this then this[name] = nil end + end + else + stuff[key] = nil + end +end + + +set = function (stuff, key, name, value) + if 'nil' ~= type(value) then + local thingy = getmetatable(stuff) + if thingy then + local this = thingy.__self.stuff[key] + if this then this[name] = value end + end + else + -- In this case, value isn't there, so we are reusing the third argument as the value. + stuff[key] = name + end +end + + +-- Get our C functions installed into skang. +-- This has to be after thingasm is defined. +package.cpath = package.cpath .. ';../../lib/lib?.so' +local GuiLua = require 'GuiLua' + + +thingasm('module,l', 'Load a module.', module, 'file') +thingasm('get', 'Get the current value of an existing Thing or metadata.', get, 'thing,key,name') +thingasm('reset', 'Reset the current value of an existing Thing or metadata.', reset, 'thing,key,name') +thingasm('set', 'Set the current value of an existing Thing or metadata.', set, 'thing,key,name,data') + +thingasm('nada', 'Do nothing.', function () --[[ This function intentionally left blank. ]] end) + + + +-- Widget wrappers. +-- TODO - Fix this up so we don't need .W +local widgets = {} +--thingasm{widgets, 'window', 'The window.', types='userdata'} +thingasm{widgets, 'W', 'Holds all the widgets', types='Keyed'} +widgets.W{'Cwidget', 'The widget.', types='userdata'} +widgets.W{'action,a', 'The action for the widget.', 'nada', types='string'} +local aIsValid = function (self, parent) + local result = Thing.isValid(self, parent) + + if result then + local value = parent[self.names[1] ] +print('NEW ACTION - ' .. self.names[1] .. ' = ' .. value .. ' ' .. type(parent.Cwidget)) + action(parent.Cwidget, value) + end + return result +end + +widgets.W{'look,l', 'The look of the widget.', types='string'} +--[[ +widgets.W{'colour,color,c', 'The colours of the widget.', types='table'} +widgets.W.c{'red,r', 'The red colour of the widget.', 255, types='number'} +widgets.W.c{'green,g', 'The green colour of the widget.', 255, types='number'} +widgets.W.c{'blue,b', 'The blue colour of the widget.', 255, types='number'} +widgets.W.c{'alpha,a', 'The alpha colour of the widget.', 255, types='number'} +-- At this point we want to change widgets.W.c() to go to a different __call, coz right now it's going to the Mum.__call, which wraps thingasm. +-- TODO - Keep an eye on this if we change to a single Mum, instead of the shallow copy we use now. +local wMum = getmetatable(widgets.W.c) +wMum.__call = function (func, ...) + return Colour(func, ...) +end +]] + +window = function(w, h, title, name) + name = name or 'myWindow' + local win = {} + win = copy(widgets, name) + local wMum, wThingy = getStuffed(win.W, 'a') + wThingy.isValid = aIsValid + win.window = Cwindow(w, h, title, name) + return win +end + +thingasm{'window', 'Specifies the size and title of the application Frame.', window, 'number,number,string', acl="GGG"} + + +-- TODO - Some function stubs, for now. Fill them up later. +skang = function (name) +end + +thingasm('skang', 'Parse the contents of a skang file or URL.', skang, 'URL') + + +moduleEnd(_M) + +end + +-- Boss is the person that owns a Thing. + +--[[ The original Skang parameters and commands. + public final static String MY_USAGE[][] = + { + {"skinURL", "skinURL", "Y", "s", null, "URL of skin file.", "", "RI-"}, + {"debug", "debug", "N", "", "0", "Set debugging level to :\n\t-1 - errors and warnings only (-q)\n\t0 - basic information\n\t1 - advanced information (-v)\n\t2 - trace functions\n\t3 - trace protocol\n\t4 - dump packets + stuff\n\t5 - detail", "", ""}, + {"browser", "browser", "N", "", "mozilla %f", "Browser to run.", "", ""}, + {"downloaddir", "downloadDir", "N", "", "download", "Download directory.", "", ""}, + {"sessionID", "sessionID", "N", "", null, "SessionID from servlet.", "", ""}, + {"JSESSIONID", "JSESSIONID", "N", "", null, "JSESSIONID from servlet engine.", "", ""}, + {"servletpath", "servletPath", "N", "", "matrix_rad", "Servlet path.", "", ""}, + {"servletport", "servletPort", "N", "", null, "Servlet port.", "", ""}, + {"servletsslport", "servletSSLPort", "N", "", null, "Servlet SSL port.", "", ""}, + {"HTML", "HTML", "N", "", "false", "Output to HTML?", "", ""}, + {"PHP", "PHP", "N", "", "false", "Output though the PHP wrapper", "", ""}, + {"inbrowser", "inBrowser", "N", "", "true", "Run in browser window?", "", ""}, + {"SSL", "SSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""}, + {"NOSSL", "NOSSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""}, + {"corporate", "corporate", "N", "", null, "Are we doing corporate shit?", "", ""}, + {"", "", "", "", "", "", "", ""} + }; + public final static String MY_SKANG[][] = + { +-- {"module", "addModule", "file,data", "Load a module.", "", ""}, + {"append", "appendThing", "name,data", "Append to the current value of a Thing.", "", ""}, + {"#!java", "bash", "name,name,name,name,name,name,name", "A not so clever unix script compatability hack.", "", ""}, + {"pending", "pendingDoThing", "action", "Do an action when you are ready.", "", ""}, + {"applet", "doIfApplet", "action", "Only do this if we are an applet.", "", ""}, + {"application", "doIfApplication", "action", "Only do this if we are an application.", "", ""}, + {"corporateshit", "doIfCorporateShit", "action", "Only do this if we are doing corporate shit.", "", ""}, + {"realworld", "doIfRealWorld", "action", "Only do this if we are in the real world.", "", ""}, + {"servlet", "doIfServlet", "action", "Only do this if we are a servlet.", "", ""}, + {"do", "doThing", "action", "Do this action.", "", ""}, + {"grab", "getFile", "URL", "Grab a file from a URL.", "", ""}, +-- {"get", "getThing", "name", "Get the current value of an existing thing.", "", ""}, + {"gimmeskin", "gimmeSkin", "", "Returns the modules default skin.", "", ""}, + {"help", "helpThing", "file", "Show help page.", "", ""}, +-- {"nada", "nothing", "data", "Does nothing B-).", "", ""}, + {"postshow", "postShowThings", "URL,name", "POST the values of all Things to the URL, show the returned content.", "", ""}, + {"post", "postThings", "URL", "POST the values of all Things to the URL, return the content.", "", ""}, + {"postparse", "postParseThings", "URL", "POST the values of all Things to the URL, parse the returned content.", "", ""}, + {"quiet", "quiet", "", "Output errors and warnings only.", "", ""}, + {"remove", "removeThing", "name", "Remove an existing thing.", "", ""}, + {"sethelp", "setHelp", "name,data", "Change the help for something.", "", ""}, +-- {"set", "setThing", "name,data", "Set the current value of an existing Thing.", "", ""}, +-- {"skang", "skangRead", "URL", "Parse the contents of a skang file or URL.", "", ""}, +-- {"quit", "startQuit", "", "Quit, exit, remove thyself.", "", ""}, + {"stopwhinging", "stopWhinging", "", "Clear all messages.", "", ""}, + {"tell", "tellThing", "name", "Returns details of an existing Thing.", "", ""}, + {"togglebug", "toggleIgnoreBrowserBug", "", "Toggle ignorance of a certain browser bug.", "", ""}, + {"verbose", "verbose", "", "Output advanced information.", "", ""}, + {"", "", "", "", "", ""} +]] + +--[[ The original SkangAWT parameters and commands. + public final static String MY_USAGE[][] = + { + {"", "", "", "", "", "", "", ""} + }; + public final static String MY_SKANG[][] = + { + {"taction", "tactionWidget", "name,action", "Set the alternative action for a widget.", "", ""}, + {"action", "actionWidget", "name,action", "Set the action for a widget.", "", ""}, + {"pane", "addPane", "name,x,y,w,h,data", "Add a pane to the current module.", "", ""}, + {"widget", "addWidget", "name,type,lx,ly,lw,lh,data,data", "Add a widget to the current skin.", "", ""}, + {"checkboxgroup", "checkBoxGroup", "number", "Make the next 'number' Checkboxes part of a check box group.", "", ""}, +-- {"clear", "clearWidgets", "", "The current skin is cleared of all widgets.", "", ""}, + {"colour", "colourWidget", "name,r,g,b,alpha,r,g,b,alpha", "Set widget's background and foreground colour.", "", "GGG"}, + {"doaction", "doWidget", "name", "Do a widgets action.", "", "GGG"}, + {"disable", "disableWidget", "name", "Disable a widget.", "", "GGG"}, + {"enable", "enableWidget", "name", "Enable a widget.", "", "GGG"}, + {"hide", "hideWidget", "name", "Hide a widget.", "", "GGG"}, + {"hideall", "hideAllWidgets", "name,lx,ly,lw,lh", "Hide all widgets.", "", "GGG"}, + {"look", "lookWidget", "name,normal,ghost,active,toggle", "Set the current look of an existing widget.", "", "GGG"}, + {"mask", "maskWidget", "name,data", "Set the mask for a widget.", "", ""}, + {"onmouse", "onMouse", "name,data", "Do something on mouse hover.", "", ""}, + {"offmouse", "offMouse", "name,data", "Do something off mouse hover.", "", ""}, + {"popup", "popupWidget", "name,data,data,data,data", "Create a popup.", "", "GGG"}, + {"readonly", "readOnlyWidget", "name", "Make a widget read only.", "", "GGG"}, + {"writeonly", "writeOnlyWidget", "name", "Make a widget write only.", "", "GGG"}, + {"satori", "satori", "x,y", "Give me the developers menu.", "", "GGG"}, + {"showloginwindow", "showLoginWindow", "", "Show user login window.", "", "GGG"}, + {"show", "showWidget", "name", "Show a widget.", "", "GGG"}, +-- {"window", "setSkangFrame", "x,y,name", "Specifies the size and title of the application Frame.", "", "GGG"}, + {"stuff", "stuffWidget", "name,data", "Set the stuff for a widget's pane.", "", ""}, + {"stufflet", "stuffWidget", "name,data,data", "Set the stufflet for a widget.", "", ""}, + {"stufflist", "stuffListWidget", "name,data", "List the stuff in this widget.", "", ""}, + {"stuffload", "stuffLoadWidget", "name,data,data", "Load the stuff for a widget.", "", ""}, + {"stuffsave", "stuffSaveWidget", "name,data,data", "Save the stuff for a widget.", "", ""}, + {"stuffdelete", "stuffDeleteWidget", "name,data,data", "Delete the stuff for a widget.", "", "SSS"}, + {"stuffclear", "stuffClearWidget", "name,data", "Clear the stuff for a widget.", "", "SSS"}, + {"rowtowidgets", "rowToWidgets", "name", "Copy Grid row to matching widgets.", "", ""}, + {"widgetstorow", "widgetsToRow", "name,data", "Copy matching widgets to Grid row.", "", ""}, + {"clearrow", "clearRow", "name", "Clear Grid row and matching widgets.", "", ""}, + {"clearrowwidgets", "clearRowWidgets", "name", "Clear only the Grid row matching widgets.", "", ""}, + {"", "", "", "", "", ""} + }; +]] + + +--[[ security package + +Java skang could run as a stand alone applicion, as an applet in a web +page, or as a servlet on a web server. This was pretty much all +transparent to the user. The security system reflected that. Lua skang +wont run in web pages, but can still have client / server behaviour. +The general idea was, and still is, that the GUI is the client side (in +web page, in extantz GUI) that sends values back to the server side +(servlet, actual Lua package running as a separate process, or the world +server for in world scripts). Client side can request that server side +runs commands. Serevr side can send values and commands back to the +client. Mostly it all happenes automatically through the ACLs. + +Bouncer is the Java skang security manager, it extended the Java +SecurityManager. Lua has no such thing, though C code running stuff in +a sandbox does a similar job. Fascist is the Java security supervisor, +again should go into the C sandbox. + +Human is used for authenticating a human, Puter for authenticating a +computer, Suits for corporate style authentication, and they all +extended Who, the base authentication module. + +For now, I have no idea how this all translates into Lua, but putting +this here for a reminder to think about security during the design +stage. + + +This is the old Java ACL definition - + acl - access control list. +Owner is usually the person running the Thingspace. +RWX~,---,Rwxgroup1,r--group2,r-xgroup3,rw-group4,--X~user1 +rwx~ is for the owner. The second one is the default. The rest are per group or per user. +Capital letters mean that they get access from the network to. +--- No access at all. +RWX Full access. +R-- Read only access. +r-x Read and execute, but only locally. +rw- Read and write a field, but don't execute a method. +-w- A password. +-a- An append only log file. +-A- An append only log file on the server. +Ri- read, but only set from init (ei. skinURL not set from properties or skang files). +RI- As above, but applet.init() can set it too. +--x Thing is both method and field, only execution of the method is allowed. +--p Run as owner (Pretend). +--P Run across the network as owner (can run in applet triggered by server). +s-- Read only, but not even visible to applets. +sss Only visible to servlets and applications. +--S Send to servlet to execute if applet, otherwise execute normally. +S-- Read only, but ignore local version and get it from server. +ggg GUI Thing, only visible to Applets and applications. +GGG GUI Thing, but servlets can access them across the net. + +For servlet only modules from an applet, the applet only loads the skanglet class, using it for all +access to the module. + + +Lua Security best practices - + + From an email by Mike Pall - + +"The only reasonably safe way to run untrusted/malicious Lua scripts is +to sandbox it at the process level. Everything else has far too many +loopholes." + +http://lua-users.org/lists/lua-l/2011-02/msg01595.html +http://lua-users.org/lists/lua-l/2011-02/msg01609.html +http://lua-users.org/lists/lua-l/2011-02/msg01097.html +http://lua-users.org/lists/lua-l/2011-02/msg01106.html + +So that's processes, not threads like LuaProc does. B-( + +However, security in depth is good, so still worthwhile looking at it from other levels as well. + +General solution is to create a new environment, which we are doing +anyway, then whitelist the safe stuff into it, instead of blacklisting +unsafe stuff. Coz you never know if new unsafe stuff might exist. + +Different between 5.1 (setfenv) and 5.2 (_ENV) + +http://lua-users.org/wiki/SandBoxes - + +------------------------------------------------------ +-- make environment +local env = -- add functions you know are safe here +{ + ipairs = ipairs, + next = next, + pairs = pairs, + pcall = pcall, + tonumber = tonumber, + tostring = tostring, + type = type, + unpack = unpack, + coroutine = { create = coroutine.create, resume = coroutine.resume, + running = coroutine.running, status = coroutine.status, + wrap = coroutine.wrap }, + string = { byte = string.byte, char = string.char, find = string.find, + format = string.format, gmatch = string.gmatch, gsub = string.gsub, + len = string.len, lower = string.lower, match = string.match, + rep = string.rep, reverse = string.reverse, sub = string.sub, + upper = string.upper }, + table = { insert = table.insert, maxn = table.maxn, remove = table.remove, + sort = table.sort }, + math = { abs = math.abs, acos = math.acos, asin = math.asin, + atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos, + cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor, + fmod = math.fmod, frexp = math.frexp, huge = math.huge, + ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max, + min = math.min, modf = math.modf, pi = math.pi, pow = math.pow, + rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh, + sqrt = math.sqrt, tan = math.tan, tanh = math.tanh }, + os = { clock = os.clock, difftime = os.difftime, time = os.time }, +} + +-- run code under environment [Lua 5.1] +local function run(untrusted_code) + if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end + local untrusted_function, message = loadstring(untrusted_code) + if not untrusted_function then return nil, message end + setfenv(untrusted_function, env) + return pcall(untrusted_function) +end + +-- run code under environment [Lua 5.2] +local function run(untrusted_code) + local untrusted_function, message = load(untrusted_code, nil, 't', env) + if not untrusted_function then return nil, message end + return pcall(untrusted_function) +end +------------------------------------------------------ + +Also includes a table of safe / unsafe stuff. + + +While whitelisting stuff, could also wrap unsafe stuff to make them more safe. + +print() -> should reroute to our output widgets. +rawget/set() -> don't bypass the metatables, but gets tricky and recursive. +require -> don't allow bypassing the sandbox to get access to restricted modules +package.loaded -> ditto + + +Other things to do - + +debug.sethook() can be used to call a hook every X lines, which can help with endless loops and such. +Have a custom memory allocater, like edje_lua2 does. + +------------------------------------------------------ +------------------------------------------------------ + +The plan - + + Process level - + Have a Lua script runner C program / library. + It does the LuaProc thing, and the edje_lua2 memory allocater thing. + Other code feeds scripts to it via a pipe. + Unless they are using this as a library. + It can be chrooted, ulimited, LXC containered, etc. + It has an internal watchdog thread. + The truly paranoid can have a watchdog timer process watch it. + Watches for a "new Lua state pulled off the queue" signal. + This could be done from the App that spawned it. + + It runs a bunch of worker threads, with a queue of ready Lua states. + Each Lua state being run has lua_sethook() set to run each X lines, AND a watchdog timer set. + If either is hit, then the Lua state is put back on the queue. + (Currently LuaProc states go back an the queue when waiting for a "channel message", which does a lua_yeild().) + NOTE - apparently "compiled code" bypasses hooks, though there's an undocumented LuaJIT compile switch for that. http://lua-users.org/lists/lua-l/2011-02/msg01106.html + EFL is event based. + LSL is event based. + LuaSL is event based. + Java skang is event based, with anything that has long running stuff overriding runBit(). + Coz Java AWT is event based, the "events" are over ridden methods in each widget class. + Should all work if we keep this as event based. + An "event" is a bit of Lua script in a string, at Input trust level usually. + Configurable for this script runner is - + IP address & port, or pipe name. + Security level to run at, defaults to Network. + Number of worker threads, defaults to number of CPUs. + Memory limit per Lua state. + Lines & time per tick for Lua states. + + Different levels of script trust - + System - the local skang and similar stuff. + -> No security at all. + App - Lua scripts and C from the running application. + -> Current module level security. + Each has it's own environment, with no cross environment leakage. + Runs in the Apps process, though that might be the script runner as a library. + Or could be skang's main loop. + Local - Lua scripts and skang files sent from the client apps running on the same system. + -> As per App. + Runs in a script runner, maybe? Or just the Apps script runner. + Limit OS and file stuff, the client app can do those itself. + Network - Lua scripts and skang files sent from the network. + -> Runs in a script runner. + Option to chroot it, ulimit it, etc. + Heavily Lua sandboxed via environment. + It can access nails, but via network derived credentials. + Can have very limited local storage, not direct file access though, but via some sort of security layer. + Can have network access. + Can have GUI access, but only to it's own window. + Config - Lua scripts run as configuration files. + -> Almost empty local environment, can really only do math and set Things. + Input - Lua scripts run as a result of hitting skang widgets on the other end of a socket. + -> As per Config, but can include function calls. + Also would need sanitizing, as this can come from the network. + Microsoft - Not to be trusted at all. + Apple - Don't expect them to trust us. + + NOTE - edje_lua2 would be roughly Local trust level. + + So we need to be able to pass Lua between processes - + Have to be able to do it from C, from Lua, and from Lua embedded in C. + edje_lua2 - uses Edje messages / signals. + LuaSL - uses Ecore_Con, in this case a TCP port, even though it's only local. + LuaSL mainloop for it's scripts is to basically wait for these messages from LuaProc. + Which yield's until we get one. + Eventually gets Lua code as a string -> loadstring() -> setfenv() -> pcall() + The pcall returns a string that we send back as a LuaProc message. + Extantz - we want to use stdin/stdout for the pipe, but otherwise LuaSL style semantics. + + Hmm, Extantz could run external skang modules in two ways - + Run the module as a separate app, with stdin/stdout. + Require the module, and run it internally. + Stuff like the in world editor and radar would be better as module, since they are useless outside Extantz? + Radar is useless outside Extantz, editor could be used to edit a local file. + Stuff like woMan would be better off as a separate application, so it can start and stop extantz. + +]] + + +--[[ +The main loop. + A Lua skang module is basically a table, with skang managed fields. + Once it calls moduleEnd(), it's expected to have finished. + test.lua is currently an exception, it runs test stuff afterwards. + So their code just registers Things and local variables. + Some of those Things might be functions for manipulating internal module state. + A C module has it's variables managed outside of it by Lua. + So would have to go via LUA_GLOBALSINDEX to get to them. + Unless they are userdata, which are C things anyway. + Though it's functions are obviously inside the C module. + Normal Lua module semantics expect them to return to the require call after setting themselves up. + So test.lua is really an aberation. + + Soooo, where does the main loop go? + The skang module itself could define the main loop. + Should not actually call it though, coz skang is itself a module. + + In Java we had different entry points depending on how it was called. + If it was called as an applet or application, it got it's own thread with a mainloop. + That main loop just slept and called runBit() on all registered modules. + If it was loaded as a module, it bypassed that stuff. + I don't think Lua provides any such mechanism. + In theory, the first call to moduleBegin would be the one that was started as an application. + So we could capture that, and test against it in moduleEnd to find when THAT module finally got to the end. + THEN we can start the main loop (test.lua being the exception that fucks this up). + Though test.lua could include a runBits() that quits the main loop, then starts the main loop at the very end once more? + The problem with this is applications that require skang type modules. + The first one they require() isn't going to return. + Eventually skang itself will get loaded. It looks at the "arg" global, which SHOULD be the command line. + Including the name of the application, which we could use to double check. + Extantz / script runner would set this arg global itself. + + Skang applications have one main loop per app. + Skang modules use the main loop of what ever app called them. + Non skang apps have the option of calling skangs main loop. + Extantz starts many external skang apps, which each get their own main loop. + Extantz has it's own Ecore main loop. + LuaSL still has one main loop per script. + LSL scripts get their own main loop, but LSL is stupid and doesn't have any real "module" type stuff. + +What does skang main loop actually do? + In Java it just slept for a bit, then called runBit() from each module, and the only module that had one was watch. + Actually better off using Ecore timers for that sort of thing. + Skang main loop has nothing to do? lol + Well, except for the "wait for LuaProc channel messages" thing. + Widget main loop would be Ecore's main loop. + + Extantz loads a skang module. + Module runs in extantz script runner. + Module runs luaproc message main loop from skang. + Evas / Elm widgets send signals, call C callbacks. + Extantz sends Lua input scripts via luaproc messages to module. + Extantz runs separate skang module. + Module runs in it's own process. + Module runs it's own message loop on the end of stdin. + Evas / Elm widgets send signals, call C callbacks. + Extantz sends Lua Input scripts to module via stdout. + Module runs stand alone. + Module runs in it's own process. + Module has to have widget start Ecore's main loop. + Module runs it's own message loop, waiting for widget to send it messages. + Evas / Elm widgets send signals, call C callbacks. + Widget sends Lua Input scripts to module. + +Alternate plan - + Skang has no main loop, modules are just tables. + OK, so sometimes skang has to start up an Ecore main loop. + With either Ecore_Con, or Evas and Elm. + skang.message(string) + Call it to pass a bit of Lua to skang, which is likely Input. + Widget twiddles happen in Ecore's main loop, via signals and call backs. + Eventually these call backs hit the widgets action string. + Send that action string to skang.message(). + + Extantz loads a skang module. + Extantz has a skang Lua state. + Module is just another table in skang. + Widget -> callback -> action string -> skang.message() + Extantz runs separate skang module. + Skang module C code runs an Ecore main loop. + Module is just another table in skang. + Skang C uses Ecore_Con to get messages from Extantz' Ecore_Con. + Widget -> callback -> action string -> Ecore_Con -> skang.message() + OOORRRR .... + Skang runs a main loop reading stdin. + Widget -> callback -> action string -> stdout -> skang.message() + Module runs stand alone. + Skang module C code runs an Ecore main loop. + Module is just another table in skang. + Widget -> callback -> action string -> skang.message() + Extantz loads a skang module from the network. + Skang module runs on the server with it's own Ecore main loop somehow. + Module is just another table in skang. + Extantz uses Ecore_Con to get messages from Extantz' Ecore_Con, via TCP. + Widget -> callback -> action string -> Ecore_Con -> skang.message() + OOORRRR .... + Remember, these are untrusted, so that's what the script runner is for. + Skang module runs in the script runner, with some sort of luaproc interface. + Widget -> callback -> action string -> Ecore_Con -> luaproc -> skang.message() + + Skang running as a web server. + Skang runs an Ecore main loop. + Skang has an Ecore_Con server attached to port 80. + Skang loads modules as usual. + Skang responds to specific URLs with HTML forms generated from Skang files. + Skang gets post data back, which kinda looks like Input. B-) + + Extantz runs a LuaSL / LSL script from the network. + Send this one to the LuaSL script runner. + Coz LSL scripts tend to have lots of copies with the same name in different objects. + Could get too huge to deal with via "just another table". + In this case, compiling the LSL might be needed to. + +]] + + +--[[ TODO + NOTE that skang.thingasm{} doesn't care what other names you pass in, they all get assigned to the Thing. + + + Widget - + Should include functions for actually dealing with widgets, plus a way + of creating widgets via introspection. Should also allow access to + widget internals via table access. Lua code could look like this - + + foo = widget('label', 0, "0.1", 0.5, 0, 'Text goes here :") + -- Method style. + foo:colour(255, 255, 255, 0, 0, 100, 255, 0) + foo:hide() + foo:action("skang.load(some/skang/file.skang)") + -- Table style. + foo.action = "skang.load('some/skang/file.skang')" + foo.colour.r = 123 + foo.look('some/edje/file/somewhere.edj') + foo.help = 'This is a widget for labelling some foo.' + + Widgets get a type as well, which would be label, button, edit, grid, etc. + A grid could even have sub types - grid,number,string,button,date. + A required widget might mean that the window HAS to have one. + Default for a widget could be the default creation arguments - '"Press me", 1, 1, 10, 50'. + + skang.thingasm{'myButton', types='Button', rectangle={1, 1, 10, 50}, title='Press me', ...} + + skang.thingasm('foo,s,fooAlias', 'Foo is a bar, not the drinking type.', function () print('foo') end, '', '"button", "The foo :"' 1, 1, 10, 50') + myButton = skang.widget('foo') -- Gets the default widget creation arguments. + myButton:colour(1, 2, 3, 4) + -- Use generic positional / named arguments for widget to, then we can do - + myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, look='edit.edj', colour={blue=20}, action='...'} + -- Using the Thing alias stuff, maybe we can do the "first stage tokenise" step after all - + myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, l='edit.edj', c={b=20}, a='...'} + myEditor:colour(1, 2, 3, 4, 5, 6, 7, 8) + myButton = 'Not default' -- myEditor and foo change to. Though now foo is a command, not a parameter, so maybe don't change that. + -- Though the 'quit' Thing could have a function that does quitting, this is just an example of NOT linking to a Thing. + -- If we had linked to this theoretical 'quit' Thing, then pushing that Quit button would invoke it's Thing function. + quitter = skang.widget(nil, 'button', 'Quit', 0.5, 0.5, 0.5, 0.5) + quitter:action('quit') + + For widgets with "rows", which was handled by Stuff in skang, we could + maybe use the Lua concat operator via metatable. I think that works by + having the widget (a table) on one side of the concat or the other, and + the metatable function gets passed left and right sides, then must + return the result. Needs some experimentation, but it might look like + this - + + this.bar = this.bar .. 'new choice' + this.bar = 'new first choice' .. this.bar + + + coordinates and sizes - + + Originally skang differentiated between pixels and character cells, + using plain integers to represent pixels, and _123 to represent + character cells. The skang TODO wanted to expand that to percentages + and relative numbers. We can't use _123 in Lua, so some other method + needs to be used. Should include those TODO items in this new design. + + Specifying character cells should be done as strings - "123" + + Percentages can be done as small floating point numbers between 0 and 1, + which is similar to Edje. Since Lua only has a floating point number + type, both 0 and 1 should still represent pixels / character cells - + + 0.1, 0.5, "0.2", "0.9" + + Relative numbers could be done as strings, with the widget to be + relative to, a + or -, then the number. This still leaves the problem + of telling if the number is pixels or character cells. Also, relative + to what part of the other widget? Some more thought needs to be put + into this. + + Another idea for relative numbers could be to have a coord object with + various methods, so we could have something like - + + button:bottom(-10):right(5) -- 10 pixels below the bottom of button, 5 pixels to the right of the right edge of button. + button:width("12") -- 12 characters longer than the width of button. + + But then how do we store that so that things move when the thing they are + relative to moves? + + + Squeal - + Squeal was the database driver interface for SquealStuff, the database + version of Stuff. Maybe we could wrap esskyuehl? Not really in need of + database stuff for now, but should keep it in mind. + For SquealStuff, the metadata would be read from the SQL database automatically. + + squeal.database('db', 'host', 'someDb', 'user', 'password') -> Should return a Squeal Thing. + local db = require 'someDbThing' -> Same as above, only the database details are encodode in the someDbThing source, OR come from someDbThing.properties. + db:getTable('stuff', 'someTable') -> Grabs the metadata, but not the rows. + db:read('stuff', 'select * from someTable'} -> Fills stuff up with several rows, including setting up the metadata for the columns. + stuff[1].field1 -> Is a Thing, with a proper metatable, that was created automatically from the database meta data. + stuff:read('someIndex') -> Grabs a single row that has the key 'someIndex', or perhaps multiple rows since this might have SQL under it. + stuff = db:read('stuff', 'select * from someTable where key='someIndex') + + stuff:write() -> Write all rows to the database table. + stuff:write(1) -> Write one row to the database table. + stuff:stuff('field1').isValid = someFunction -- Should work, all stuff[key] share the same Thing description. + stuff:isValid(db) -> Validate the entire Thing against it's metadata at least. + window.stuff = stuff -> Window gets stuff as it's default 'Keyed' table, any widgets with same names as the table fields get linked. + grid.stuff = stuff -> Replace contents of this grid widget with data from all the rows in stuff. + choice.stuff = stuff -> As in grid, but only using the keys. + widget.stuff = stuff:stuff('field1') -> This widget gets a particular stufflet. + widget would have to look up getmetatable(window.stuff).parent. Or maybe this should work some other way? + + In all these cases above, stuff is a 'Keyed' table that has a Thing metatable, so it has sub Things. + Should include some way of specifyings details like key name, where string, etc. + getmetatable(stuff).__keyName + getmetatable(stuff).__squeal.where + And a way to link this database table to others, via the key of the other, as a field in this Stuff. + stuff:stuff('field0').__link = {parent, key, index} + In Java we had this - + +public class PersonStuff extends SquealStuff +{ + +... + + public final static String FULLNAME = "fullname"; + + public static final String keyField = "ID"; // Name of key field/s. + public static final String where = keyField + "='%k'"; + public static final String listName = "last"; + public static final String tables = "PEOPLE"; + public static final String select = null; + public static final String stufflets[] = + { + keyField, + "PASSWD_ID|net.matrix_rad.squeal.PasswdStuff|,OTHER", + "QUALIFICATION_IDS|net.matrix_rad.people.QualificationStuff|,OTHER", + "INTERESTING_IDS|net.matrix_rad.people.InterestingStuff|,OTHER", + "title", + "first", + "middle", + "last", + "suffix", + +... + + FULLNAME + "||,VARCHAR,512" + }; +} + +]] + + +-- Gotta check out this _ENV thing, 5.2 only. Seems to replace the need for setfenv(). Seems like setfenv should do what we want, and is more backward compatible. +-- "_ENV is not supported directly in 5.1, so its use can prevent a module from remaining compatible with 5.1. +-- Maybe you can simulate _ENV with setfenv and trapping gets/sets to it via __index/__newindex metamethods, or just avoid _ENV." +-- LuaJIT doesn't support _ENV anyway. diff --git a/libraries/LSL.lua b/libraries/LSL.lua deleted file mode 100644 index 662c880..0000000 --- a/libraries/LSL.lua +++ /dev/null @@ -1,870 +0,0 @@ --- A module of LSL stuffs. - --- Using a module means it gets compiled each time? Maybe not if I can use bytecode files. Perhaps LuaJIT caches these? --- Does not seem to be slowing it down noticably, but that might change once the stubs are filled out. - --- Use it like this - --- local _LSL = require 'LSL' - ---[[ From http://lua-users.org/wiki/LuaModuleFunctionCritiqued -A related note on C code: The luaL_register [9] function in C is -somewhat analogous to the module function in Lua, so luaL_register -shares similar problems, at least when a non-NULL libname is used. -Furthermore, the luaL_newmetatable/luaL_getmetatable/luaL_checkudata -functions use a C string as a key into the global registry. This poses -some potential for name conflicts--either because the modules were -written by different people or because they are different versions of -the same module loaded simultaneously. To address this, one may instead -use a lightuserdata (pointer to variable of static linkage to ensure -global uniqueness) for this key or store the metatable as an -upvalue--either way is a bit more efficient and less error prone. -]] - --- http://www.lua.org/pil/15.4.html looks useful. --- http://www.lua.org/pil/15.5.html the last part about autoloading functions might be useful. - - -local LSL = {}; -local SID = ""; -local scriptName = ""; -local running = true -local paused = false -local currentState = {} -local detectedGroups = {} -local detectedGrabs = {} -local detectedKeys = {} -local detectedLinkNumbers = {} -local detectedNames = {} -local detectedOwners = {} -local detectedPoss = {} -local detectedRots = {} -local detectedTouchBinormals = {} -local detectedTouchFaces = {} -local detectedTouchNormals = {} -local detectedTouchPoss = {} -local detectedTouchSTs = {} -local detectedTouchUVs = {} -local detectedTypes = {} -local detectedVels = {} -local waitAndProcess - - --- Debugging aids - --- Functions to print tables. -local print_table, print_table_start - -function print_table_start(table, space, name) - print(space .. name .. ": "); - print(space .. "{"); - print_table(table, space .. " "); - print(space .. "}"); -end - -function print_table(table, space) - for k, v in pairs(table) do - if type(v) == "table" then - print_table_start(v, space, k); - elseif type(v) == "string" then - print(space .. k .. ': "' .. v .. '";') - else - print(space .. k .. ": " .. v .. ";") - end - end -end - -function msg(...) - print(SID, ...) -- The comma adds a tab, fancy that. B-) -end - - --- Stuff called from the wire protocol has to be global, but I think this means just global to this file. - -events = {} - -function start() paused = false end -function stop() paused = true end -function quit() running = false end - -function events.detectedGroups(list) detectedGroups = list end -function events.detectedGrabs(list) detectedGrabs = list end -function events.detectedKeys(list) detectedKeys = list end -function events.detectedLinkNumbers(list) detectedLinkNumbers = list end -function events.detectedNames(list) detectedNames = list end -function events.detectedOwners(list) detectedOwners = list end -function events.detectedPoss(list) detectedPoss = list end -function events.detectedRots(list) detectedRots = list end -function events.detectedTouchBinormals(list) detectedTouchBinormals = list end -function events.detectedTouchFaces(list) detectedTouchFaces = list end -function events.detectedTouchNormals(list) detectedTouchNormals = list end -function events.detectedTouchPoss(list) detectedTouchPoss = list end -function events.detectedTouchSTs(list) detectedTouchSTs = list end -function events.detectedTouchUVs(list) detectedTouchUVs = list end -function events.detectedTypes(list) detectedTypes = list end -function events.detectedVels(list) detectedVels = list end - -function events.detectsClear() - detectedGroups = {} - detectedGrabs = {} - detectedKeys = {} - detectedLinkNumbers = {} - detectedNames = {} - detectedOwners = {} - detectedPoss = {} - detectedRots = {} - detectedTouchBinormals = {} - detectedTouchFaces = {} - detectedTouchNormals = {} - detectedTouchPoss = {} - detectedTouchSTs = {} - detectedTouchUVs = {} - detectedTypes = {} - detectedVels = {} -end - -function events.at_rot_target(tnum, targetrot, ourrot) if nil ~= currentState.at_rot_target then currentState.at_rot_target(tnum, targetrot, ourrot) end events.detectsClear() end -function events.at_target(tnum, targetpos, ourpos) if nil ~= currentState.at_target then currentState.at_target(tnum, targetpos, ourpos) end events.detectsClear() end -function events.attach(id) if nil ~= currentState.attach then currentState.attach(id) end events.detectsClear() end -function events.changed(change) if nil ~= currentState.changed then currentState.changed(change) end events.detectsClear() end -function events.collision_start(num_detected) if nil ~= currentState.collision_start then currentState.collision_start(num_detected) end events.detectsClear() end -function events.collision(num_detected) if nil ~= currentState.collision then currentState.collision(num_detected) end events.detectsClear() end -function events.collision_end(num_detected) if nil ~= currentState.collision_end then currentState.collision_end(num_detected) end events.detectsClear() end -function events.control(id, held, changed) if nil ~= currentState.control then currentState.control(id, held, changed) end events.detectsClear() end -function events.dataserver(queryid, data) if nil ~= currentState.dataserver then currentState.dataserver(queryid, data) end events.detectsClear() end -function events.email(Time, address, subj, message, num_left) if nil ~= currentState.email then currentState.email(Time, address, subj, message, num_left) end events.detectsClear() end -function events.http_request(request_id, status, metadata, body) if nil ~= currentState.http_request then currentState.http_request(request_id, status, metadata, body) end events.detectsClear() end -function events.http_response(request_id, status, metadata, body) if nil ~= currentState.http_response then currentState.http_response(request_id, status, metadata, body) end events.detectsClear() end -function events.land_collision_start(pos) if nil ~= currentState.land_collision_start then currentState.land_collision_start(pos) end events.detectsClear() end -function events.land_collision(pos) if nil ~= currentState.land_collision then currentState.land_collision(pos) end events.detectsClear() end -function events.land_collision_end(pos) if nil ~= currentState.land_collision_end then currentState.land_collision_end(pos) end events.detectsClear() end -function events.link_message(sender_num, num, str, id) if nil ~= currentState.link_message then currentState.link_message(sender_num, num, str, id) end events.detectsClear() end -function events.listen(channel, name, id, message) if nil ~= currentState.listen then currentState.listen(channel, name, id, message) end events.detectsClear() end -function events.money(id, amount) if nil ~= currentState.money then currentState.money(id, amount) end events.detectsClear() end -function events.moving_start() if nil ~= currentState.moving_start then currentState.moving_start() end events.detectsClear() end -function events.moving_end() if nil ~= currentState.moving_end then currentState.moving_end() end events.detectsClear() end -function events.no_sensor() if nil ~= currentState.no_sensor then currentState.no_sensor() end events.detectsClear() end -function events.not_at_rot_target() if nil ~= currentState.not_at_rot_target then currentState.not_at_rot_target() end events.detectsClear() end -function events.not_at_target() if nil ~= currentState.not_at_target then currentState.not_at_target() end events.detectsClear() end -function events.object_rez(id) if nil ~= currentState.object_rez then currentState.object_rez() end events.detectsClear() end -function events.on_rez(start_param) if nil ~= currentState.on_rezz then currentState.on_rez(start_param) end events.detectsClear() end -function events.remote_data(event_type, channel, message_id, sender, idata, sdata) if nil ~= currentState.remote_data then currentState.remote_data(event_type, channel, message_id, sender, idata, sdata) end events.detectsClear() end -function events.run_time_permissions(perm) if nil ~= currentState.run_time_permisions then currentState.run_time_permissions(perm) end events.detectsClear() end -function events.sensor(num_detected) if nil ~= currentState.sensor then currentState.sensor(num_detected) end events.detectsClear() end -function events.state_entry() if nil ~= currentState.state_entry then currentState.state_entry() end events.detectsClear() end -function events.state_exit() if nil ~= currentState.state_exit then currentState.state_exit() end events.detectsClear() end -function events.timer() if nil ~= currentState.timer then currentState.timer() end events.detectsClear() end -function events.touch_start(num_detected) if nil ~= currentState.touch_start then currentState.touch_start(num_detected) end events.detectsClear() end -function events.touch(num_detected) if nil ~= currentState.touch then currentState.touch(num_detected) end events.detectsClear() end -function events.touch_end(num_detected) if nil ~= currentState.touch_end then currentState.touch_end(num_detected) end events.detectsClear() end -function events.transaction_result(id, success, data) if nil ~= currentState.transaction_result then currentState.transaction_result(id, success, data) end events.detectsClear() end - - --- LSL function and constant creation stuff. - -local args2string -- Pre declare this. -local functions = {} -local mt = {} - -local function value2string(value, Type) - local temp = "" - - if "float" == Type then temp = temp .. value - elseif "integer" == Type then temp = temp .. value - elseif "key" == Type then temp = "\"" .. value .. "\"" - elseif "list" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]" - elseif "table" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]" - elseif "string" == Type then temp = "\"" .. value .. "\"" - elseif "rotation" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ", " .. value.s .. ">" - elseif "vector" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ">" - else - temp = temp .. value - end - return temp -end - -function args2string(doType, ...) - local temp = "" - local first = true - - for j,w in ipairs( {...} ) do - if first then first = false else temp = temp .. ", " end - if doType then - temp = temp .. value2string(w, type(w)) - else - temp = temp .. w - end - end - return temp -end - -function mt.callAndReturn(name, ...) - luaproc.sendback(name .. "(" .. args2string(true, ...) .. ")") -end - -function mt.callAndWait(name, ...) - mt.callAndReturn(name, ...); - -- Eventually a sendForth() is called, which should end up passing through SendToChannel(). - -- Wait for the result, which should be a Lua value as a string. - return waitAndProcess(true) -end - -local function newConst(Type, name, value) - LSL[name] = value - return { Type = Type, name = name } -end - -local function newFunc(Type, name, ... ) - functions[name] = { Type = Type, args = {...} } - if "" == Type then - LSL[name] = function(...) mt.callAndReturn(name, ... ) end - else - LSL[name] = function(...) return mt.callAndWait(name, ... ) end - end -end - - --- LSL constants. - -local constants = -{ - newConst("float", "PI", 3.14159265358979323846264338327950), - newConst("float", "PI_BY_TWO", LSL.PI / 2), -- 1.57079632679489661923132169163975 - newConst("float", "TWO_PI", LSL.PI * 2), -- 6.28318530717958647692528676655900 - newConst("float", "DEG_TO_RAD", LSL.PI / 180.0), -- 0.01745329252 - newConst("float", "RAD_TO_DEG", 180.0 / LSL.PI), -- 57.2957795131 - newConst("float", "SQRT2", 1.4142135623730950488016887242097), - - newConst("integer", "CHANGED_INVENTORY", 0x001), - newConst("integer", "CHANGED_COLOR", 0x002), - newConst("integer", "CHANGED_SHAPE", 0x004), - newConst("integer", "CHANGED_SCALE", 0x008), - newConst("integer", "CHANGED_TEXTURE", 0x010), - newConst("integer", "CHANGED_LINK", 0x020), - newConst("integer", "CHANGED_ALLOWED_DROP", 0x040), - newConst("integer", "CHANGED_OWNER", 0x080), - newConst("integer", "CHANGED_REGION", 0x100), - newConst("integer", "CHANGED_TELEPORT", 0x200), - newConst("integer", "CHANGED_REGION_START", 0x400), - newConst("integer", "CHANGED_MEDIA", 0x800), - - newConst("integer", "DEBUG_CHANNEL", 2147483647), - newConst("integer", "PUBLIC_CHANNEL", 0), - - newConst("integer", "INVENTORY_ALL", -1), - newConst("integer", "INVENTORY_NONE", -1), - newConst("integer", "INVENTORY_TEXTURE", 0), - newConst("integer", "INVENTORY_SOUND", 1), - newConst("integer", "INVENTORY_LANDMARK", 3), - newConst("integer", "INVENTORY_CLOTHING", 5), - newConst("integer", "INVENTORY_OBJECT", 6), - newConst("integer", "INVENTORY_NOTECARD", 7), - newConst("integer", "INVENTORY_SCRIPT", 10), - newConst("integer", "INVENTORY_BODYPART", 13), - newConst("integer", "INVENTORY_ANIMATION", 20), - newConst("integer", "INVENTORY_GESTURE", 21), - - newConst("integer", "ALL_SIDES", -1), - newConst("integer", "LINK_SET", -1), - newConst("integer", "LINK_ROOT", 1), - newConst("integer", "LINK_ALL_OTHERS", -2), - newConst("integer", "LINK_ALL_CHILDREN", -3), - newConst("integer", "LINK_THIS", -4), - - newConst("integer", "PERM_ALL", 0x7FFFFFFF), - newConst("integer", "PERM_COPY", 0x00008000), - newConst("integer", "PERM_MODIFY", 0x00004000), - newConst("integer", "PERM_MOVE", 0x00080000), - newConst("integer", "PERM_TRANSFER", 0x00002000), - newConst("integer", "MASK_BASE", 0), - newConst("integer", "MASK_OWNER", 1), - newConst("integer", "MASK_GROUP", 2), - newConst("integer", "MASK_EVERYONE", 3), - newConst("integer", "MASK_NEXT", 4), - newConst("integer", "PERMISSION_DEBIT", 0x0002), - newConst("integer", "PERMISSION_TAKE_CONTROLS", 0x0004), - newConst("integer", "PERMISSION_TRIGGER_ANIMATION", 0x0010), - newConst("integer", "PERMISSION_ATTACH", 0x0020), - newConst("integer", "PERMISSION_CHANGE_LINKS", 0x0080), - newConst("integer", "PERMISSION_TRACK_CAMERA", 0x0400), - newConst("integer", "PERMISSION_CONTRAL_CAMERA", 0x0800), - - newConst("integer", "AGENT", 0x01), - newConst("integer", "ACTIVE", 0x02), - newConst("integer", "PASSIVE", 0x04), - newConst("integer", "SCRIPTED", 0x08), - - newConst("integer", "OBJECT_UNKNOWN_DETAIL", -1), - - newConst("integer", "PRIM_BUMP_SHINY", 19), - newConst("integer", "PRIM_COLOR", 18), - newConst("integer", "PRIM_FLEXIBLE", 21), - newConst("integer", "PRIM_FULLBRIGHT", 20), - newConst("integer", "PRIM_GLOW", 25), - newConst("integer", "PRIM_MATERIAL", 2), - newConst("integer", "PRIM_PHANTOM", 5), - newConst("integer", "PRIM_PHYSICS", 3), - newConst("integer", "PRIM_POINT_LIGHT", 23), - newConst("integer", "PRIM_POSITION", 6), - newConst("integer", "PRIM_ROTATION", 8), - newConst("integer", "PRIM_SIZE", 7), - newConst("integer", "PRIM_TEMP_ON_REZ", 4), - newConst("integer", "PRIM_TYPE", 9), - newConst("integer", "PRIM_TYPE_OLD", 1), - newConst("integer", "PRIM_TEXGEN", 22), - newConst("integer", "PRIM_TEXTURE", 17), - newConst("integer", "PRIM_TEXT", 26), - - newConst("integer", "PRIM_BUMP_NONE", 0), - newConst("integer", "PRIM_BUMP_BRIGHT", 1), - newConst("integer", "PRIM_BUMP_DARK", 2), - newConst("integer", "PRIM_BUMP_WOOD", 3), - newConst("integer", "PRIM_BUMP_BARK", 4), - newConst("integer", "PRIM_BUMP_BRICKS", 5), - newConst("integer", "PRIM_BUMP_CHECKER", 6), - newConst("integer", "PRIM_BUMP_CONCRETE", 7), - newConst("integer", "PRIM_BUMP_TILE", 8), - newConst("integer", "PRIM_BUMP_STONE", 9), - newConst("integer", "PRIM_BUMP_DISKS", 10), - newConst("integer", "PRIM_BUMP_GRAVEL", 11), - newConst("integer", "PRIM_BUMP_BLOBS", 12), - newConst("integer", "PRIM_BUMP_SIDING", 13), - newConst("integer", "PRIM_BUMP_LARGETILE", 14), - newConst("integer", "PRIM_BUMP_STUCCO", 15), - newConst("integer", "PRIM_BUMP_SUCTION", 16), - newConst("integer", "PRIM_BUMP_WEAVE", 17), - - newConst("integer", "PRIM_HOLE_DEFAULT", 0), - newConst("integer", "PRIM_HOLE_CIRCLE", 16), - newConst("integer", "PRIM_HOLE_SQUARE", 32), - newConst("integer", "PRIM_HOLE_TRIANGLE", 48), - - newConst("integer", "PRIM_MATERIAL_STONE", 0), - newConst("integer", "PRIM_MATERIAL_METAL", 1), - newConst("integer", "PRIM_MATERIAL_GLASS", 2), - newConst("integer", "PRIM_MATERIAL_WOOD", 3), - newConst("integer", "PRIM_MATERIAL_FLESH", 4), - newConst("integer", "PRIM_MATERIAL_PLASTIC", 5), - newConst("integer", "PRIM_MATERIAL_RUBBER", 6), - newConst("integer", "PRIM_MATERIAL_LIGHT", 7), - - newConst("integer", "PRIM_SCULPT_TYPE_SPHERE", 1), - newConst("integer", "PRIM_SCULPT_TYPE_TORUS", 2), - newConst("integer", "PRIM_SCULPT_TYPE_PLANE", 3), - newConst("integer", "PRIM_SCULPT_TYPE_CYLINDER", 4), - newConst("integer", "PRIM_SCULPT_TYPE_MESH", 5), - newConst("integer", "PRIM_SCULPT_TYPE_MIMESH", 6), - - newConst("integer", "PRIM_SHINY_NONE", 0), - newConst("integer", "PRIM_SHINY_LOW", 1), - newConst("integer", "PRIM_SHINY_MEDIUM", 2), - newConst("integer", "PRIM_SHINY_HIGH", 3), - - newConst("integer", "PRIM_TYPE_BOX", 0), - newConst("integer", "PRIM_TYPE_CYLINDER", 1), - newConst("integer", "PRIM_TYPE_PRISM", 2), - newConst("integer", "PRIM_TYPE_SPHERE", 3), - newConst("integer", "PRIM_TYPE_TORUS", 4), - newConst("integer", "PRIM_TYPE_TUBE", 5), - newConst("integer", "PRIM_TYPE_RING", 6), - newConst("integer", "PRIM_TYPE_SCULPT", 7), - - newConst("integer", "STRING_TRIM", 3), - newConst("integer", "STRING_TRIM_HEAD", 1), - newConst("integer", "STRING_TRIM_TAIL", 2), - - newConst("integer", "TRUE", 1), - newConst("integer", "FALSE", 0), - - newConst("integer", "TOUCH_INVALID_FACE", 0x7FFFFFFF), - newConst("vector", "TOUCH_INVALID_TEXCOORD", {x=-1.0, y=-1.0, z=0.0}), - newConst("vector", "TOUCH_INVALID_VECTOR", {x=0.0, y=0.0, z=0.0}), - - newConst("integer", "TYPE_INTEGER", 1), - newConst("integer", "TYPE_FLOAT", 2), - newConst("integer", "TYPE_STRING", 3), - newConst("integer", "TYPE_KEY", 4), - newConst("integer", "TYPE_VECTOR", 5), - newConst("integer", "TYPE_ROTATION", 6), - newConst("integer", "TYPE_INVALID", 0), - - newConst("string", "NULL_KEY", "00000000-0000-0000-0000-000000000000"), - newConst("string", "EOF", "\\n\\n\\n"), -- Corner case, dealt with later. - - newConst("rotation", "ZERO_ROTATION", {x=0.0, y=0.0, z=0.0, s=1.0}), - newConst("vector", "ZERO_VECTOR", {x=0.0, y=0.0, z=0.0}), - --- TODO - Temporary dummy variables to get vector and rotation thingies to work for now. - - newConst("float", "s", 1.0), - newConst("float", "x", 0.0), - newConst("float", "y", 0.0), - newConst("float", "z", 0.0), -} - --- ll*() function definitions - --- LSL avatar functions -newFunc("key", "llAvatarOnSitTarget") -newFunc("list", "llGetAnimationList", "key id") -newFunc("integer", "llGetPermissions") -newFunc("key", "llGetPermissionsKey") -newFunc("string", "llKey2Name", "key avatar") -newFunc("", "llRequestPermissions", "key avatar", "integer perms") -newFunc("integer", "llSameGroup", "key avatar") -newFunc("", "llStartAnimation", "string anim") -newFunc("", "llStopAnimation", "string anim") -newFunc("", "llUnSit", "key avatar") - --- LSL collision / detect / sensor functions -newFunc("integer", "llDetectedGroup", "integer index") -newFunc("vector", "llDetectedGrab", "integer index") -newFunc("key", "llDetectedKey", "integer index") -newFunc("integer", "llDetectedLinkNumber", "integer index") -newFunc("string", "llDetectedName", "integer index") -newFunc("key", "llDetectedOwner", "integer index") -newFunc("vector", "llDetectedPos", "integer index") -newFunc("rotation", "llDetectedRot", "integer index") -newFunc("vector", "llDetectedTouchBinormal", "integer index") -newFunc("integer", "llDetectedTouchFace", "integer index") -newFunc("vector", "llDetectedTouchNormal", "integer index") -newFunc("vector", "llDetectedTouchPos", "integer index") -newFunc("vector", "llDetectedTouchST", "integer index") -newFunc("vector", "llDetectedTouchUV", "integer index") -newFunc("integer", "llDetectedType", "integer index") -newFunc("vector", "llDetectedVel", "integer index") - - --- LSL communications functions -newFunc("", "llDialog", "key avatar", "string caption", "list arseBackwardsMenu", "integer channel") -newFunc("integer", "llListen", "integer channel", "string name", "key id", "string msg") -newFunc("", "llListenRemove", "integer handle") -newFunc("", "llOwnerSay", "string text") -newFunc("", "llSay", "integer channel", "string text") -newFunc("", "llShout", "integer channel", "string text") -newFunc("", "llWhisper", "integer channel", "string text") -newFunc("", "llMessageLinked", "integer link", "integer num", "string text", "key aKey") - --- LSL inventory functions. -newFunc("string", "llGetInventoryName", "integer Type", "integer index") -newFunc("integer", "llGetInventoryNumber", "integer Type") -newFunc("integer", "llGetInventoryType", "string name") -newFunc("key", "llGetNotecardLine", "string name", "integer index") -newFunc("", "llRezAtRoot", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") -newFunc("", "llRezObject", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") - --- LSL list functions. -newFunc("list", "llCSV2List", "string text") -newFunc("list", "llDeleteSubList", "list l", "integer start", "integer End") -newFunc("string", "llDumpList2String", "list l", "string separator") -newFunc("integer", "llGetListLength", "list l") -newFunc("string", "llList2CSV", "list l") -newFunc("float", "llList2Float", "list l", "integer index") -newFunc("integer", "llList2Integer", "list l", "integer index") -newFunc("key", "llList2Key", "list l", "integer index") -newFunc("list", "llList2List", "list l", "integer start", "integer End") -newFunc("string", "llList2String", "list l", "integer index") -newFunc("rotation", "llList2Rotation", "list l", "integer index") -newFunc("vector", "llList2Vector", "list l", "integer index") -newFunc("integer", "llListFindList", "list l", "list l1") -newFunc("list", "llListInsertList", "list l", "list l1", "integer index") -newFunc("list", "llListReplaceList", "list l", "list part", "integer start", "integer End") -newFunc("list", "llListSort", "list l", "integer stride", "integer ascending") -newFunc("list", "llParseString2List", "string In", "list l", "list l1") -newFunc("list", "llParseStringKeepNulls", "string In", "list l", "list l1") - --- LSL math functions -newFunc("rotation", "llEuler2Rot", "vector vec") -newFunc("float", "llFrand", "float max") -newFunc("float", "llPow", "float number", "float places") -newFunc("vector", "llRot2Euler", "rotation rot") -newFunc("integer", "llRound", "float number") - --- LSL media functions -newFunc("", "llPlaySound", "string name", "float volume") - --- LSL object / prim functions -newFunc("", "llDie") -newFunc("key", "llGetKey") -newFunc("integer", "llGetLinkNumber") -newFunc("string", "llGetObjectDesc") -newFunc("string", "llGetObjectName") -newFunc("key", "llGetOwner") -newFunc("", "llSetObjectDesc", "string text") -newFunc("", "llSetObjectName", "string text") -newFunc("", "llSetPrimitiveParams", "list params") -newFunc("", "llSetSitText", "string text") -newFunc("", "llSetText", "string text", "vector colour", "float alpha") -newFunc("", "llSitTarget", "vector pos", "rotation rot") - --- LSL rotation / scaling / translation functions -newFunc("vector", "llGetPos") -newFunc("rotation", "llGetRot") -newFunc("", "llSetPos", "vector pos") -newFunc("", "llSetRot", "rotation rot") -newFunc("", "llSetScale", "vector scale") - --- LSL script functions -newFunc("integer", "llGetFreeMemory") -newFunc("string", "llGetScriptName") -newFunc("", "llResetOtherScript", "string name") -newFunc("", "llResetScript") -newFunc("", "llSetScriptState", "string name", "integer running") - --- LSL string functions -newFunc("string", "llGetSubString", "string text", "integer start", "integer End") -newFunc("integer", "llStringLength", "string text") -newFunc("string", "llStringTrim", "string text", "integer type") -newFunc("integer", "llSubStringIndex", "string text", "string sub") - --- LSL texture functions -newFunc("float", "llGetAlpha", "integer side") -newFunc("", "llSetAlpha", "float alpha", "integer side") -newFunc("", "llSetColor", "vector colour", "integer side") - --- LSL time functions -newFunc("float", "llGetTime") -newFunc("", "llResetTime") -newFunc("", "llSetTimerEvent", "float seconds") -newFunc("float", "llSleep", "float seconds") -- Faked return type, it actually does not return anything. This forces it to wait. Actually fully implements llSleep(). B-) - - --- TODO - fake this for now. -function --[[integer]] LSL.llGetInventoryType(--[[string]] name) return LSL.INVENTORY_SCRIPT end; - - --- LSL collision / detect / sensor functions -function --[[integer]] LSL.llDetectedGroup( --[[integer]] index) return detectedGroups [index + 1] or false end -function --[[vector]] LSL.llDetectedGrab( --[[integer]] index) return detectedGrabs [index + 1] or LSL.ZERO_VECTOR end -function --[[key]] LSL.llDetectedKey( --[[integer]] index) return detectedKeys [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY -function --[[integer]] LSL.llDetectedLinkNumber( --[[integer]] index) return detectedLinkNumbers [index + 1] or 0 end -function --[[string]] LSL.llDetectedName( --[[integer]] index) return detectedNames [index + 1] or "" end -- LL says it returns NULL_KEY, LSL Wiki says an empty string. Apparently there used to be an exploit for creating multi kb names, normally they are 255 characters. -function --[[key]] LSL.llDetectedOwner( --[[integer]] index) return detectedOwners [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY -function --[[vector]] LSL.llDetectedPos( --[[integer]] index) return detectedPoss [index + 1] or LSL.ZERO_VECTOR end -function --[[rotation]] LSL.llDetectedRot( --[[integer]] index) return detectedRots [index + 1] or LSL.ZERO_ROTATION end -function --[[vector]] LSL.llDetectedTouchBinormal( --[[integer]] index) return detectedTouchBinormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end -function --[[integer]] LSL.llDetectedTouchFace( --[[integer]] index) return detectedTouchFaces [index + 1] or LSL.TOUCH_INVALID_FACE end -function --[[vector]] LSL.llDetectedTouchNormal( --[[integer]] index) return detectedTouchNormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end -function --[[vector]] LSL.llDetectedTouchPos( --[[integer]] index) return detectedTouchPoss [index + 1] or LSL.TOUCH_INVALID_VECTOR end -function --[[vector]] LSL.llDetectedTouchST( --[[integer]] index) return detectedTouchSTs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end -function --[[vector]] LSL.llDetectedTouchUV( --[[integer]] index) return detectedTouchUVs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end -function --[[integer]] LSL.llDetectedType( --[[integer]] index) return detectedTypes [index + 1] or 0 end -function --[[vector]] LSL.llDetectedVel( --[[integer]] index) return detectedVels [index + 1] or LSL.ZERO_VECTOR end - - --- LSL list functions. - ---function --[[list]] LSL.llCSV2List(--[[string]] text) return {} end; -function --[[list]] LSL.llDeleteSubList(--[[list]] l,--[[integer]] start,--[[integer]] eNd) - local result = {} - local x = 1 - - -- Deal with the impedance mismatch. - start = start + 1 - eNd = eNd + 1 - for i = 1,#l do - if i < start then result[x] = l[i]; x = x + 1 - elseif i > eNd then result[x] = l[i]; x = x + 1 - end - end - - return result -end - -function --[[string]] LSL.llDumpList2String(--[[list]] l, --[[string]] separator) - local result = "" - for i = 1,#l do - if "" ~= result then result = result .. separator end - result = result .. l[i] - end - return result -end - -function --[[integer]] LSL.llGetListLength(--[[list]] l) - return #l -end - -function --[[string]] LSL.llList2CSV(--[[list]] l) - return LSL.llDumpList2String(l, ",") -end - -function --[[float]] LSL.llList2Float(--[[list]] l,--[[integer]] index) - local result = tonumber(l[index]) - if nil == result then result = 0.0 end - return result -end - -function --[[integer]] LSL.llList2Integer(--[[list]] l,--[[integer]] index) - local result = tonumber(l[index+1]) - if nil == result then result = 0 end - return result -end - -function --[[key]] LSL.llList2Key(--[[list]] l,--[[integer]] index) - local result = l[index+1] - if result then return "" .. result else return LSL.NULL_KEY end -end - -function --[[list]] LSL.llList2List(--[[list]] l,--[[integer]] start,--[[integer]] eNd) - local result = {} - local x = 1 - - --[[ TODO - -Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would capture the entire list. -If start is larger than end the list returned is the exclusion of the entries, so 6, 4 would give the entire list except for the 5th entry. - ]] - - -- Deal with the impedance mismatch. - start = start + 1 - eNd = eNd + 1 - for i = 1,#l do - if i >= start then result[x] = l[i]; x = x + 1 - elseif i <= eNd then result[x] = l[i]; x = x + 1 - end - end - - return result -end - -function --[[string]] LSL.llList2String(--[[list]] l,--[[integer]] index) - local result = l[index+1] - if result then return "" .. result else return "" end -end - -function --[[rotation]] LSL.llList2Rotation(--[[list]] l,--[[integer]] index) - local result = l[index+1] - if nil == result then result = LSL.ZERO_ROTATION end - -- TODO - check if it's not an actual rotation, then return LSS.ZERO_ROTATION - return result -end - -function --[[vector]] LSL.llList2Vector(--[[list]] l,--[[integer]] index) - local result = l[index+1] - if nil == result then result = LSL.ZERO_VECTOR end - -- TODO - check if it's not an actual rotation, then return LSS.ZERO_VECTOR - return result -end - ---function --[[integer]] LSL.llListFindList(--[[list]] l, --[[list]] l1) return 0 end; -function --[[list]] LSL.llListInsertList(--[[list]] l, --[[list]] l1,--[[integer]] index) - local result = {} - local x = 1 - local y - for i = 1,index do - result[x] = l[i] - x = x + 1 - end - y = x - for i = 1,#ll do - result[x] = ll[i] - x = x + 1 - end - for i = y,#l do - result[x] = l[i] - x = x + 1 - end - return result -end - -function --[[list]] LSL.llListReplaceList(--[[list]] l, --[[list]] part,--[[integer]] start,--[[integer]] eNd) - local result = {} - local x = 1 - local y - for i = 1,index do - result[x] = l[i] - x = x + 1 - end - for i = 1,#part do - result[x] = part[i] - x = x + 1 - end - for i = index,#l do - result[x] = l[i] - x = x + 1 - end - return result -end - -function --[[list]] LSL.llListSort(--[[list]] l,--[[integer]] stride,--[[integer]] ascending) - local result = {} - - -- TODO - Deal with stride and ascending. - for i = 1,#l do - result[x] = l[i]; x = x + 1 - end - table.sort(result) - - return result -end - ---function --[[list]] LSL.llParseString2List(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end; ---function --[[list]] LSL.llParseStringKeepNulls(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end; - - --- LSL script functions - -function --[[string]] LSL.llGetScriptName() - return scriptName -end - - --- LSL string functions - -function --[[string]] LSL.llGetSubString(--[[string]] text, --[[integer]] start, --[[integer]] End) - -- Deal with the impedance mismatch. - if 0 <= start then start = start + 1 end - if 0 <= End then End = End + 1 end --- TODO - If start is larger than end the substring is the exclusion of the entries, so 6,4 would give the entire string except for the 5th character. - return string.sub(text, start, End) -end - -function --[[integer]] LSL.llSubStringIndex(--[[string]] text, --[[string]] sub) - local start, End = string.find(text, sub, 1, true) - - if nil == start then return -1 else return start - 1 end -end - - --- Crements stuff. - -function LSL.preDecrement(name) _G[name] = _G[name] - 1; return _G[name]; end; -function LSL.preIncrement(name) _G[name] = _G[name] + 1; return _G[name]; end; -function LSL.postDecrement(name) local temp = _G[name]; _G[name] = _G[name] - 1; return temp; end; -function LSL.postIncrement(name) local temp = _G[name]; _G[name] = _G[name] + 1; return temp; end; - - --- State stuff - -function LSL.stateChange(x) - if currentState ~= x then -- Changing to the same state is a NOP. - -- TODO - Should clear out pending events, except timer() - -- Also forget about any event setup that needs setting up via some ll*() function, except timer(). - if nil ~= currentState.state_exit then - currentState.state_exit(); - end - currentState = x; - --[[ Never return to the current states event handler. In theory. lol - Notably, it's not actually legal to do a state change from a function, only from handlers. - There is a hack though, but it's unsupported, so I don't have to worry about it so much. - - Write out "state new;" as "return _LSL.stateChange(newState);", with stateChange() returning new.state_entry()) which will force two tail calls. - - The caller of stateChange() might be a function rather than an event handler. - Which will return to the event handler (possibly via other nested function calls) as if the state never changed. - http://lslwiki.net/lslwiki/wakka.php?wakka=FunctionStateChangeHack seems to imply that this is exactly what LSL does. - Soooo, this might actually work perfectly. - Except for one minor quirk, as that page shows - only the top level function's state sticks, unless the handler does one to, then the handlers one overrides things. - Which I can probably ignore anyway, as this entire calling states within functions thing is an unsupported hack. - ]] - if nil ~= currentState.state_entry then - return currentState.state_entry(); - end - end -end; - -function LSL.mainLoop(sid, name, x) - local status, errorMsg - local result - - SID = sid - scriptName = name - LSL.EOF = "\n\n\n" -- Fix this up now. - - LSL.stateChange(x); - waitAndProcess(false) - msg("Script quitting.") -end - -function waitAndProcess(returnWanted) - local Type = "event" - - if returnWanted then Type = "result" end - while running do - local message = luaproc.receive(SID) - if message then - -- TODO - should we be discarding return values while paused? I don't think so, so we need to process those, - if paused then - if "start()" == message then paused = false end - else - result, errorMsg = loadstring(message) -- "The environment of the returned function is the global environment." Though normally, a function inherits it's environment from the function creating it. Which is what we want. lol - if nil == result then - msg("Not a valid " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg) - else - -- Set the functions environment to ours, for the protection of the script, coz loadstring sets it to the global environment instead. - -- TODO - On the other hand, we will need the global environment when we call event handlers. So we should probably stash it around here somewhere. - -- Meh, seems to be working fine as it is. - setfenv(result, getfenv(1)) - status, result = pcall(result) - if not status then - msg("Error from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. result) - elseif result then - -- Check if we are waiting for a return, and got it. - if returnWanted and string.match(message, "^return ") then - return result - end - -- Otherwise, just run it and keep looping. - status, errorMsg = luaproc.send(sid, result) - if not status then - msg("Error sending results from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg) - end - end - end - end - end - end -end - --- Typecasting stuff. - -function LSL.floatTypecast(x) - local temp = tonumber(x) - if nil == temp then temp = 0 end - return temp; -end - -function LSL.integerTypecast(x) - local temp = tonumber(x) - if nil == temp then temp = 0 end - return temp; -end - -function LSL.keyTypecast(x) - return "" .. x; -end - -function LSL.listTypecast(x) - return {x}; -end - -function LSL.rotationTypecast(x) - return x; -end - -function LSL.stringTypecast(x) - return "" .. x; -end - -function LSL.vectorTypecast(x) - return x; -end - - --- Called at compiler set up time, to produce the constants.lsl file. -function LSL.gimmeLSL() - for i,v in ipairs(constants) do - local value = LSL[v.name] - - print(v.Type .. " " .. v.name .. " = " .. value2string(value, v.Type) .. ";") - end - - for k in pairs(functions) do - local v = functions[k] - if nil == v.args then - print(v.Type .. " " .. k .. "(){}") - else - print(v.Type .. " " .. k .. "(" .. args2string(false, unpack(v.args)) .. "){}") - end - end -end - - -return LSL; diff --git a/libraries/LumbrJack.c b/libraries/LumbrJack.c deleted file mode 100644 index 084d916..0000000 --- a/libraries/LumbrJack.c +++ /dev/null @@ -1,85 +0,0 @@ -/* LumbrJack - a logging library that wraps Eina logging. - -*/ - - -#include "LumbrJack.h" - - -static char dateTime[DATE_TIME_LEN]; - -static void _ggg_log_print_cb(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args) -{ - FILE *f = data; - char dt[DATE_TIME_LEN + 1]; - char fileTab[256], funcTab[256]; - - getDateTime(NULL, dt, NULL); - dt[19] = '\0'; - if (12 > strlen(file)) - snprintf(fileTab, sizeof(fileTab), "%s\t\t", file); - else - snprintf(fileTab, sizeof(fileTab), "%s\t", file); - snprintf(funcTab, sizeof(funcTab), "\t%s", fnc); - fprintf(f, "%s ", dt); - if (f == stderr) - eina_log_print_cb_stderr(d, level, fileTab, funcTab, line, fmt, data, args); - else if (f == stdout) - eina_log_print_cb_stdout(d, level, fileTab, funcTab, line, fmt, data, args); - fflush(f); -} - -int loggingStartup(char *name, int logDom) -{ - if (logDom < 0) - { - logDom = eina_log_domain_register(name, NULL); - if (logDom < 0) - { - EINA_LOG_CRIT("could not register log domain '%s'", name); - return logDom; - } - } - eina_log_level_set(EINA_LOG_LEVEL_DBG); - eina_log_domain_level_set(name, EINA_LOG_LEVEL_DBG); - eina_log_print_cb_set(_ggg_log_print_cb, stderr); - - // Shut up the excess debugging shit from EFL. - eina_log_domain_level_set("eo", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("eldbus", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("eet", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_audio", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_con", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_evas", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("ecore_system_upower", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("eio", EINA_LOG_LEVEL_WARN); - eina_log_domain_level_set("evas_main", EINA_LOG_LEVEL_WARN); - - return logDom; -} - -char *getDateTime(struct tm **nowOut, char *dateOut, time_t *timeOut) -{ - struct tm *newTime; - time_t szClock; - char *date = dateTime; - - // Get time in seconds - time(&szClock); - // Convert time to struct tm form - newTime = localtime(&szClock); - - if (nowOut) - *nowOut = newTime; - if (dateOut) - date = dateOut; - if (timeOut) - *timeOut = szClock; - - // format - strftime(date, DATE_TIME_LEN, "%d/%m/%Y %H:%M:%S\r", newTime); - return (dateTime); -} diff --git a/libraries/LumbrJack.h b/libraries/LumbrJack.h deleted file mode 100644 index 4a3290c..0000000 --- a/libraries/LumbrJack.h +++ /dev/null @@ -1,38 +0,0 @@ - -#include -#include - -#include - - -#define PC(...) EINA_LOG_DOM_CRIT(ourGlobals->logDom, __VA_ARGS__) -#define PE(...) EINA_LOG_DOM_ERR(ourGlobals->logDom, __VA_ARGS__) -#define PW(...) EINA_LOG_DOM_WARN(ourGlobals->logDom, __VA_ARGS__) -#define PD(...) EINA_LOG_DOM_DBG(ourGlobals->logDom, __VA_ARGS__) -#define PI(...) EINA_LOG_DOM_INFO(ourGlobals->logDom, __VA_ARGS__) - -#define PCm(...) EINA_LOG_DOM_CRIT(ourGlobals.logDom, __VA_ARGS__) -#define PEm(...) EINA_LOG_DOM_ERR(ourGlobals.logDom, __VA_ARGS__) -#define PWm(...) EINA_LOG_DOM_WARN(ourGlobals.logDom, __VA_ARGS__) -#define PDm(...) EINA_LOG_DOM_DBG(ourGlobals.logDom, __VA_ARGS__) -#define PIm(...) EINA_LOG_DOM_INFO(ourGlobals.logDom, __VA_ARGS__) - -#define D() PD("DEBUG") - - -// "01:03:52 01-01-1973\n\0" -#define DATE_TIME_LEN 21 - - -#ifndef FALSE -// NEVER change this -typedef enum -{ - FALSE = 0, - TRUE = 1 -} boolean; -#endif - - -int loggingStartup(char *name, int logDom); -char *getDateTime(struct tm **nowOut, char *dateOut, time_t *tiemOut); diff --git a/libraries/Runnr.c b/libraries/Runnr.c deleted file mode 100644 index a24e7f5..0000000 --- a/libraries/Runnr.c +++ /dev/null @@ -1,318 +0,0 @@ -/* Runnr - a library that deals with running Lua scripts. - -*/ - - -#include "Runnr.h" - - -void dumpStack(lua_State *L, int i) -{ - int type = lua_type(L, i); - - switch (type) - { - case LUA_TNONE : printf("Stack %d is empty\n", i); break; - case LUA_TNIL : printf("Stack %d is a nil\n", i); break; - case LUA_TBOOLEAN : printf("Stack %d is a boolean - %d\n", i, lua_toboolean(L, i)); break; - case LUA_TNUMBER : printf("Stack %d is a number\n - %f", i, lua_tonumber(L, i)); break; - case LUA_TSTRING : printf("Stack %d is a string - %s\n", i, lua_tostring(L, i)); break; - case LUA_TFUNCTION : printf("Stack %d is a function\n", i); break; - case LUA_TTHREAD : printf("Stack %d is a thread\n", i); break; - case LUA_TTABLE : - { - int j; - - printf("Stack %d is a table", i); - lua_getfield(L, i, "_NAME"); - j = lua_gettop(L); - if (lua_isstring(L, j)) - printf(" - %s", lua_tostring(L, j)); - lua_pop(L, 1); - printf("\n"); - break; - } - case LUA_TUSERDATA : printf("Stack %d is a userdata\n", i); break; - case LUA_TLIGHTUSERDATA : printf("Stack %d is a light userdata\n", i); break; - default : printf("Stack %d is unknown\n", i); break; - } -} - - -// These are what the various symbols are for each type - -// int % -// num # -// str $ -// bool ! -// C func & -// table.field @ Expects an integer and a string. -// nil ~ -// table {} Starts and stops filling up a new table. -// ( Just syntax sugar for call. -// call ) Expects an integer, the number of results left after the call. -// FIXME: Still to do, if we ever use them - -// stack = Get a value from the stack, expects a stack index. -// userdata + -// lightuserdata * -// thread ^ - -static char *_push_name(lua_State *L, char *q, int *idx) // Stack usage [-0, +1, e or m] -{ - char *p = q; - char temp = '\0'; - - // A simplistic scan through an identifier, it's wrong, but it's quick, - // and we don't mind that it's wrong, coz this is only internal. - while (isalnum((int)*q)) - q++; - temp = *q; - *q = '\0'; - if (*idx > 0) - lua_getfield(L, *idx, p); // Stack usage [-0, +1, e] - else - { - if (p != q) - lua_pushstring(L, p); // Stack usage [-0, +1, m] - else - { - lua_pushnumber(L, (lua_Number) (0 - (*idx))); - (*idx)--; - } - } - *q = temp; - - return q; -} - -int pull_lua(lua_State *L, int i, char *params, ...) // Stack usage - - // if i is a table - // [-n, +n, e] - // else - // [-0, +0, -] -{ - va_list vl; - char *f = strdup(params); - char *p = f; - int n = 0, j = i, count = 0; - Eina_Bool table = EINA_FALSE; - - if (!f) return -1; - va_start(vl, params); - - if (lua_istable(L, i)) // Stack usage [-0, +0, -] - { - j = -1; - table = EINA_TRUE; - } - - while (*p) - { - char *q; - Eina_Bool get = EINA_TRUE; - - while (isspace((int)*p)) - p++; - q = p + 1; - switch (*p) - { - case '%': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] - if (lua_isnumber(L, j)) // Stack usage [-0, +0, -] - { - int *v = va_arg(vl, int *); - *v = lua_tointeger(L, j); // Stack usage [-0, +0, -] - n++; - } - break; - } - case '#': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] - if (lua_isnumber(L, j)) // Stack usage [-0, +0, -] - { - double *v = va_arg(vl, double *); - *v = lua_tonumber(L, j); // Stack usage [-0, +0, -] - n++; - } - break; - } - case '$': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] - if (lua_isstring(L, j)) // Stack usage [-0, +0, -] - { - char **v = va_arg(vl, char **); - size_t len; - char *temp = (char *) lua_tolstring(L, j, &len); // Stack usage [-0, +0, m] - - len++; // Cater for the null at the end. - *v = malloc(len); - if (*v) - { - memcpy(*v, temp, len); - n++; - } - } - break; - } - case '!': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] - if (lua_isboolean(L, j)) // Stack usage [-0, +0, -] - { - int *v = va_arg(vl, int *); - *v = lua_toboolean(L, j); // Stack usage [-0, +0, -] - n++; - } - break; - } - default: - { - get = EINA_FALSE; - break; - } - } - - if (get) - { - if (table) - { - // If this is a table, then we pushed a value on the stack, pop it off. - lua_pop(L, 1); // Stack usage [-n, +0, -] - } - else - j++; - count++; - } - p = q; - } - - va_end(vl); - free(f); - if (count > n) - n = 0; - else if (table) - n = 1; - return n; -} - -int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em] -{ - va_list vl; - char *f = strdup(params); - char *p = f; - int n = 0, table = 0, i = -1; - - if (!f) return -1; - - va_start(vl, params); - - while (*p) - { - char *q; - Eina_Bool set = EINA_TRUE; - - while (isspace((int)*p)) - p++; - q = p + 1; - switch (*p) - { - case '%': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushinteger(L, va_arg(vl, int)); // Stack usage [-0, +1, -] - break; - } - case '#': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushnumber(L, va_arg(vl, double)); // Stack usage [-0, +1, -] - break; - } - case '$': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushstring(L, va_arg(vl, char *)); // Stack usage [-0, +1, m] - break; - } - case '!': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushboolean(L, va_arg(vl, int)); // Stack usage [-0, +1, -] - break; - } - case '=': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushvalue(L, va_arg(vl, int)); // Stack usage [-0, +1, -] - break; - } - case '@': - { - int tabl = va_arg(vl, int); - char *field = va_arg(vl, char *); - - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_getfield(L, tabl, field); // Stack usage [-0, +1, e] - break; - } - case '&': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushcfunction(L, va_arg(vl, void *)); // Stack usage [-0, +1, m] - break; - } - case '~': - { - if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] - lua_pushnil(L); // Stack usage [-0, +1, -] - break; - } - case '(': // Just syntax sugar. - { - set = EINA_FALSE; - break; - } - case ')': - { - lua_call(L, n - 1, va_arg(vl, int)); - n = 0; - set = EINA_FALSE; - break; - } - case '{': - { - lua_newtable(L); - table++; - n++; - set = EINA_FALSE; - break; - } - case '}': - { - table--; - set = EINA_FALSE; - break; - } - default: - { - set = EINA_FALSE; - break; - } - } - - if (set) - { - if (table > 0) - lua_settable(L, -3); // Stack usage [-2, +0, e] - else - n++; - } - p = q; - } - - va_end(vl); - free(f); - return n; -} diff --git a/libraries/Runnr.h b/libraries/Runnr.h deleted file mode 100644 index dc720ff..0000000 --- a/libraries/Runnr.h +++ /dev/null @@ -1,15 +0,0 @@ - -#include -#include - -#include - -#include -#include -#include -#include - - -void dumpStack(lua_State *L, int i); -int pull_lua(lua_State *L, int i, char *params, ...); -int push_lua(lua_State *L, char *params, ...); diff --git a/libraries/build.lua b/libraries/build.lua deleted file mode 100755 index 8b48b70..0000000 --- a/libraries/build.lua +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env lua - -local dir = ... - -if 'nil' == type(dir) then - local build, err = loadfile('../build.lua') - if build then - setfenv(build, getfenv(2)) - build('') - else - print("ERROR - " .. err) - end - dir = workingDir -end - -LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS - -removeFiles(dir, {'LumbrJack.o', 'libLumbrJack.so', 'Runnr.o', 'libRunnr.so'}) - -runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -fPIC -c LumbrJack.c') -runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libLumbrJack.so -o libLumbrJack.so LumbrJack.o') - -runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c Runnr.c') -runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libRunnr.so -o libRunnr.so Runnr.o') diff --git a/libraries/skang.lua b/libraries/skang.lua deleted file mode 100644 index 23549c3..0000000 --- a/libraries/skang.lua +++ /dev/null @@ -1,1686 +0,0 @@ ---[[ TODO - This should be in C, but so far development has been quite rapid doing it in Lua. - -C will let us - - Actually do the widget stuff. - Slap meta tables on all value types. - Which lets us put the meta table on the variable, instead of on the table, which I think is cleaner. - Figure out the directory separator. - Network stuff. No need to look at Lua socket stuff, we have Ecore_Con. - Database stuff. No need to look at Lua SQL stuff, we have esskyuehl. Maybe. - - Actually, we could have the best of both worlds, since it is currently a C / Lua hybrid. B-) -]] - - ---[[ Skang package - -In here should live all the internals of matrix-RAD that don't -specifically relate to widgets. This would include the "window" though. - -skang.module(Evas) -skang.module(Elementary) -skang.skang('some/skang/file.skang') - -This package is also what "apps" that use the system should "inherit" -from, in the same way matrix-RAD apps did. Skang "apps" could be Lua -modules. They could also be C code, like the extantz modules are likely -to be. Skang "apps" would automatically be associated with skang files -of the same name. - -For a .skang file, the skang command (written in C) would strip off the -first line, add the two implied lines, then run it as Lua. The -skang.load() command would do the same. So that skang C comand would -just pass the file name to skang.load() in this library. B-) - -The old skang argument types are - - -{"name", "java.lang.String"}, -{"action", "java.lang.String"}, -{"type", "java.lang.String"}, -{"data", "java.lang.String"}, -{"URL", "java.lang.String"}, -{"file", "java.lang.String"}, -{"method", "java.lang.String"}, -{"lx", "java.lang.String"}, -{"ly", "java.lang.String"}, -{"lw", "java.lang.String"}, -{"lh", "java.lang.String"}, -{"normal", "java.lang.String"}, -{"ghost", "java.lang.String"}, -{"active", "java.lang.String"}, -{"toggle", "java.lang.String"}, -{"boolean","java.lang.Boolean"}, -{"number", "java.lang.Integer"}, -{"int", "java.lang.Integer"}, -{"x", "java.lang.Integer"}, -{"y", "java.lang.Integer"}, -{"w", "java.lang.Integer"}, -{"h", "java.lang.Integer"}, -{"r", "java.lang.Integer"}, -{"g", "java.lang.Integer"}, -{"b", "java.lang.Integer"}, -{"alpha", "java.lang.Integer"}, -{"acl", "net.matrix_rad.security.ACL"}, -]] - - --- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular. --- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module". -do -- Only I'm not gonna indent this. - -mainSkin = {} - --- TODO - This needs to be expanded a bit to cover things like 1.42 -local versions = { - '0%.0', 'unwritten', 'Just a stub, no code at all, or completely non-existant.', - '0%.1', 'prototype', 'Stuff that has only been prototyped, or partly written. There is some code, and it may even work, but it is not even close to the finished product.', - '%d%.3', 'written', 'Stuff that has already been written. It may not be perfect, but it is considered an aproximation of the finished product.', - '%d%.5', 'alpha', 'Version being tested by official alpha testers.', - '%d%.9', 'beta', 'Version passed alpha testing, but not ready for final release.', - '1%.0', 'final', 'Version ready for final release, fully tested.', - '3%.0', 'poetry', 'Near perfection has been acheived.', - '5%.0', 'nirvana', 'Perfection has been acheived.', - '9%.0', 'bible', 'This is the Whord of Ghod.', -} - --- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. --- TODO - Should parse in license type to. -moduleBegin = function (name, author, copyright, version, timestamp, skin, isLua) - local _M = {} -- This is what we return to require(). - local level = 2 - - if 'nil' == type(isLua) then isLua = true end - - package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. - -- Returning it at the end does the same thing. - -- This is so that we can have all the module stuff at the top, in this function. - -- Should do this before any further require(), so that circular references don't blow out. - - -- Save the callers environment. - local savedEnvironment - if isLua then - savedEnvironment = getfenv(level) - else - -- While the above works fine for test_c, it doesn't for GuiLua. Odd. - savedEnvironment = getfenv(1) - end - - -- Clone the environment into _M, so the module can access everything as usual after the setfenv() below. - --[[ TODO - Check if this also clones _G or _ENV. And see if it leaks stuff in either direction. - local _G = _G -- Only sets a local _G for this function. - _M._G = _G -- This clone loop might do this, but we don't want to be able to access the old _G from outside via this leak. - In Lua 5.1 at least, _G was special. In 5.2, _ENV sorta replaces setfenv(), but no idea if this clone loop stomps on that. - ]] - for k, v in pairs(savedEnvironment) do - _M[k] = v - end - - _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. - _M._NAME = name - _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. - _M.isLua = isLua - - -- Parse in an entire copyright message, and strip that down into bits, to put back together. - local date, owner = string.match(copyright, '[Cc]opyright (%d%d%d%d) (.*)') - _M.AUTHOR = author or owner - _M.COPYRIGHT = 'Copyright ' .. date .. ' ' .. _M.AUTHOR - -- Translate the version number into a version string. - local versionName, versionDesc = ' ', '' - for i = 1, #versions / 3 do - if 1 == string.find(version, versions[i]) then - versionName = ' ' .. versions[i + 1] .. ' ' - versionDesc = versions[i + 2] - break - end - end - _M.VERSION = version .. versionName .. timestamp - _M.VERSION_DESC = versionDesc - -- If there is a .skang file, read that in and override the passed in skin. - local f = io.open(name .. '.skang') - if f then - skin = f:read('*l') - if '#' == string.sub(skin, 1, 1) then skin = '' end - skin = skin .. f:read('*a') - f:close() - end - if skin then - skin = "local skang = require 'skang'\nlocal " .. name .. " = require '" .. name .. "'\n" .. skin - if nil == mainSkin._NAME then mainSkin = _M end - end - _M.DEFAULT_SKANG = skin - - --_G[_M._NAME] = _M -- Stuff it into a global of the same name. - -- Not such a good idea to stomp on global name space. - -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway. - -- This is why module() is broken. - _M.savedEnvironment = savedEnvironment - -- NOTE - setfenv() wont work if the environment it refers to is a C function. Worse, getfenv() returns the global environment, so we can't tell. - if isLua then - -- setfenv() sets the environment for the FUNCTION, stack level deep. - -- The number is the stack level - - -- 0 running thread, 1 current function, 2 function that called this function, etc - setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. - -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). - -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. - end - - print('Loaded module ' .. _M._NAME .. ' version ' .. _M.VERSION .. ', ' .. _M.COPYRIGHT .. '.\n ' .. _M.VERSION_DESC) - - return _M -end - - ---[[ Parse command line parameters. - -This is done in two parts. Skang will do an initial scan and tokenise, -then each module gets a chance to pull it's own Things from the result. - -Make the command line parameter getting MUCH more intelligent, try to support the common -command line interfaces - - -arg value -a value -/arg value -/a value ---arg value ---a value --a value --ab ('a' and 'b' are both shortcuts.) -arg=value -a=value -arg1=value1&arg2=value2 -arg1=value1|arg2=value2 -a=value1&a=value2 -+arg/-arg (Can't support this generically.) - -Ignore /,-,--,& except as arg introducers. Use = as value introducer. Expect -arg or a. If type is String, expect a value. If type is integer, and next token is -not an integer, increment current value, otherwise expect integer value. If type is -boolean, value beginning with T, t, F, f, etc is true, otherwise value is false, unless -next token starts with an introducer, then value is true. - -TODO - Finish supporting all of the above. - These all need deeper parsing, but we dunno if they might have been inside quoted strings from the shell. - arg=value Shell. - arg1=value1&arg2=value2 For URLs. - arg1=value1|arg2=value2 Can't remember why, probably the old skang multivalue syntax. - Test it all. - Skang command line should have standardish stuff, like --version, --help, --help module.thing. - Lua does these already, might be no need to do them ourselves - - -e 'some code'. - -i go interactive after running the script. - -v version. - - read from stdin non interactively. - LuaJIT also has this - - -- stop processing options. -]] - -ARGS = {} -lua = '' -command = '' - - --- Do an initial scan and tokenise of the command line arguments. -scanArguments = function (args) - if args then - lua = args[-1] - command = args[0] - for i, v in ipairs(args) do - local pre = '' - if '--' == string.sub(v, 1, 2) then pre = '--'; v = string.sub(v, 3, -1) end - if '-' == string.sub(v, 1, 1) then pre = '-'; v = string.sub(v, 2, -1) end - if '+' == string.sub(v, 1, 1) then pre = '+'; v = string.sub(v, 2, -1) end - -- TODO - Make this the opposite of the directory separator for what ever platform we are running on. - -- Which Lua can't figure out I think. - if '/' == string.sub(v, 1, 1) then pre = '/'; v = string.sub(v, 2, -1) end - if '=' == string.sub(v, 1, 1) then pre = '='; v = string.sub(v, 2, -1) end - if '&' == string.sub(v, 1, 1) then pre = '&'; v = string.sub(v, 2, -1) end - if '|' == string.sub(v, 1, 1) then pre = '|'; v = string.sub(v, 2, -1) end - if '' ~= v then ARGS[i] = {pre, v} end - end - end -end - -parseType = function (module, thingy, v, value) - if 'string' == thingy.types[1] then - if value then - module[v[2] ] = value[2] - value[2] = nil -- Mark it as used. - else - print('ERROR - Expected a string value for ' .. thingy.names[1]) - end - end - - if 'number' == thingy.types[1] then - if value then - -- If the introducer is '-', then this should be a negative number. - if '-' == value[1] then value[1] = ''; value[2] = '-' .. value[2] end - -- Only parse the next value as a number if it doesn't have an introducer. - if ('' == value[1]) or ('=' == value[1]) then - value[2] = tonumber(value[2]) - if value[2] then - module[v[2] ] = value[2] - value[2] = nil -- Mark it as used. - else - print('ERROR - Expected a number value for ' .. thingy.names[1]) - end - else - module[v[2] ] = module[v[2] ] + 1 - end - else - print('ERROR - Expected a number value for ' .. thingy.names[1]) - end - end - - if 'function' == thingy.types[1] then - local args = {} - -- TODO - Should allow more than one argument, but would need to pass in ARGS and i. - if 2 == #thingy.types then - if value then - -- TODO - Should check the type of the arguments. - args[#args + 1] = value[2] - module[v[2] ](args[1]) - value[2] = nil -- Mark it as used. - else - print('ERROR - Expected an argument for ' .. thingy.names[1]) - end - else - module[v[2] ]() - end - end - - if 'boolean' == thingy.types[1] then - if value then - -- Only parse the next value as a boolean if it doesn't have an introducer. - if ('' == value[1]) or ('=' == value[1]) then - module[v[2] ] = isBoolean(value[2]) - value[2] = nil -- Mark it as used. - else - module[v[2] ] = true - end - else - print('ERROR - Expected a boolean value for ' .. thingy.names[1]) - end - end -end - -pullArguments = function (module) - -- Look for our command line arguments. - local metaMum = getmetatable(module) - if metaMum and metaMum.__self then - for i, v in ipairs(ARGS) do - if v[2] then - local thingy = metaMum.__self.stuff[v[2] ] - -- Did we find one of ours? - if thingy then - parseType(module, thingy, v, ARGS[i + 1]) - v[2] = nil -- Mark it as used. - else - -- Didn't find one directly, check for single letter matches in '-abc'. - for k, w in pairs(metaMum.__self.stuff) do - if 1 == #w.names[1] then - for j = 1, #v[2] do - if string.sub(v[2], j, 1) == w.names[1] then - if 1 == j then - v[2] = string.sub(v[2], 2, -1) - if 'boolean' == w.types[1] then module[v[2] ] = true end - elseif #v[2] == j then - v[2] = string.sub(v[2], 1, j - 1) - -- The one at the end is the only one that could have a following value. - parseType(module, w, v, ARGS[i + 1]) - else - v[2] = string.sub(v[2], 1, j - 1) .. string.sub(v[2], j + 1, -1) - if 'boolean' == w.types[1] then module[v[2] ] = true end - end - if '' == v[2] then v[2] = nil end -- Mark it as used. - end - end - end - end - end - end - end - end -end - --- Restore the environment, and grab paramateres from standard places. -moduleEnd = function (module) - -- See if there is a properties file, and run it in the modules environment. - local properties, err = loadfile(module._NAME .. '.properties') - if properties then - setfenv(properties, getfenv(2)) - properties() - elseif 'cannot open ' ~= string.sub(err, 1, 12) then - print("ERROR - " .. err) - end - - pullArguments(module) - - -- Run the main skin, which is the first skin that is defined. In theory, the skin from the main module. - if mainSkin == module then - print("RUNNING SKIN FOR " .. module._NAME) - local skin, err = loadstring(module.DEFAULT_SKANG) - if skin then - setfenv(skin, getfenv(2)) - skin() - else - print("ERROR - " .. err) - end - end - - if module.isLua then setfenv(2, module.savedEnvironment) end -end - - --- Call this now so that from now on, this is like any other module. -local _M = moduleBegin('skang', 'David Seikel', 'Copyright 2014 David Seikel', '0.1', '2014-03-27 02:57:00') - --- This works coz LuaJIT automatically loads the jit module. -if type(jit) == 'table' then - print('Skang is being run by ' .. jit.version .. ' under ' .. jit.os .. ' on a ' .. jit.arch) -else - print('Skang is being run by Lua version ' .. _VERSION) -end - -scanArguments(arg) - - -function printTableStart(table, space, name) - print(space .. name .. ": ") - print(space .. "{") - printTable(table, space .. " ") - print(space .. "}") - if '' == space then print('') end -end - -function printTable(table, space) - if nil == table then return end - for k, v in pairs(table) do - if type(v) == "table" then - if v._NAME then - print(space .. "SKANG module " .. v._NAME .. ";") - else - printTableStart(v, space, k) - end - elseif type(v) == "string" then - print(space .. k .. ': "' .. v .. '";') - elseif type(v) == "function" then - print(space .. "function " .. k .. "();") - elseif type(v) == "userdata" then - print(space .. "userdata " .. k .. ";") - elseif type(v) == "boolean" then - if (v) then - print(space .. "boolean " .. k .. " TRUE ;") - else - print(space .. "boolean " .. k .. " FALSE ;") - end - else - print(space .. k .. ": " .. v .. ";") - end - end -end - - -csv2table = function (csv) - local result = {} - local i = 1 - - for v in string.gmatch(csv, ' *([^,]+)') do - result[i] = v - i = i + 1 - end - return result -end - - -shiftLeft = function (tab) - local result = tab[1] - table.remove(tab, 1) - return result -end - - --- My clever boolean check, this is the third language I've written this in. B-) --- true 1 yes ack ok one positive absolutely affirmative 'ah ha' 'shit yeah' 'why not' -local isTrue = 't1aopswy' --- false 0 no nack nope zero negative nah 'no way' 'get real' 'uh uh' 'fuck off' 'bugger off' -local isFalse = 'f0bgnuz' -isBoolean = function (aBoolean) - local result = false - - if type(aBoolean) ~= 'nil' then - -- The default case, presence of a value means it's true. - result = true - if type(aBoolean) == 'boolean' then result = aBoolean - elseif type(aBoolean) == 'function' then result = aBoolean() - elseif type(aBoolean) == 'number' then result = (aBoolean ~= 0) - elseif type(aBoolean) == 'string' then - if '' == aBoolean then - result = false - else - if 1 == string.find(string.lower(aBoolean), '^[' .. isTrue .. ']') then result = true end - if 1 == string.find(string.lower(aBoolean), '^[' .. isFalse .. ']') then result = false end - end - end - end - return result -end - - ---[[ Thing Java package - -matrix-RAD had Thing as the base class of everything. - -Each "users session" (matrix-RAD term that came from Java -applets/servlets) has a ThingSpace, which is a tree that holds -everything else. It holds the class cache, commands, loaded modules, -variables and their values, widgets and their states. In matrix-RAD I -built BonsiaTree and LeafLike, for the old FDO system I built dumbtrees. - -Other Thing things are - - get/set The getter and setter. - number No idea how this was useful. - skang The owning object, a Skang (actually got this, called module for now). - owner The owning object, a String (module._NAME). - clas Class of the Thing, a Class. (pointless) - type Class of the Thing, a String. (pointless) - realType Real Class of the Thing, a String. (pointless) - myRoot ThingSpace we are in, a ThingSpace. - - Also various functions to wrap checking the security, like canDo, canRead, etc. -]] - - ---[[ Stuff Java package - -In matrix-RAD Stuff took care of multi value Things, like database rows. - -Stuff is an abstract class that gets extended by other classes, like -SquealStuff, which was the only thing extending it. It dealt with the -basic "collection of things" stuff. Each individual thing was called a -stufflet. A final fooStuff would extend SquealStuff, and include an -array of strings called "stufflets" that at least named the stufflets, -but could also include metadata and links to other Stuffs. - -There was various infrastructure for reading and writing Stuff, throwing -rows of Stuff into grids, having choices of Stuff, linking stufflets to -individual widgets, having default Stuffs for windows, validating -Stuffs, etc. - -In Lua, setting up stuff has been folded into the general Thing stuff. - -]] - - ---[[ Thing structure - - -In the following, meta(table) is short for getmetatable(table). - -In Lua everything is supposed to be a first class value, and variables are just places to store values. -All variables are in fact stored in tables, even if it's just the local environment table. -Any variable that has a value of nil, doesn't actually exist. That's the definition. -While non table things can have metatables, Lua can only set metatables on tables, C has no such restriction. -meta(table).__index and __newindex only work on table entries that don't exist. - __index(table, key) is called if table.key is nil. - Though if __index is a table, then try __index[key]. - __newindex(table, key, value) is called if table.key is nil. - Though if __newindex is a table, then try __newindex[key] = value. -Using both __index and __newindex, and keeping the actual values elsewhere, is called a proxy table. -meta(table).__call(table, ...) is called when trying to access table as a function - table(...). - -It's worth repeating - -All variables in Lua are in some table somewhere, even if it's just the global environment table. -Metatables are only associated vith values, not variables. -Lua can only attach metatables to values that are tables, but C can attach metatables to any value. - - -A Thing is a managed variable stored in a parent proxy table, which is usually empty. -So the values stored in this Thing are actually stored in meta(parent)__values[thing]. - parent[thing] -> __index (parent, thing) -> meta(parent).__values[thing] - parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value - - -Each Thing has a description table that includes - - names - An array of names, the first one is the "official" name. - types - An array of types, the first one is the "official" type. - help - A descriptive text for humans to read. - default - The default value. - widget - A default widget definition. - required - If the Thing is required. - isValid - A function that tests if the Thing is valid. - errors - Any errors related to the Thing. - isKeyed - Is this a parent for Things that are stored under an arbitrary key. - stuff - An array of descriptions for sub Things, so Things that are tables can have their own Things. - and other things that aren't actually used yet. -All things that a description doesn't have should be inherited from the Thing table. - setmetatable(aStuff, {__index = Thing}) -Descriptions should be able to be easily shared by various Things. - - -A parent's metatable has __self, which is it's own description. -A parent is free to use it's own name space for anything it wants. -Only the variables it specifies as managed Things are dealt with here. - - -Things that are tables are treated differently, in two different ways even. -Ordinary table Things are basically treated recursively, the table is a parent, and it gets it's own Things. -There is also 'Keyed' type table Things, where the keys to this table are arbitrary, but we still want to store Things in it. -In this case, when a table is assigned to this Keyed Thing, via a new key, a new table Thing is created by copying the parent Thing description. - - -TODO - - test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.stuff[foo], then return an empty table instead? - Do we still need a parent pointer? - Should be in __values I guess. - __values[key].value - __values[key].parent - Weak references might help in here somewhere. - Maybe try looking in the skang table for Things that are not found? - Maybe put Things in the skang table that are unique from modules? - I think this is what matrix-RAD Collisions was all about. -]] - --- There is no ThingSpace, or Stuff, now it's all just in this meta table. -local Thing = -{ --- Default Thing values. - names = {'unknown'}, - help = 'No description supplied.', -- help text describing this Thing. - default = '', -- The default value. This could be a funcion, making this a command. - types = {}, -- A list of types. The first is the type of the Thing itself, the rest are for multi value Things. Or argument types for commands. - required = false, -- Is this thing is required. TODO - Maybe fold this into types somehow, or acl? - widget = '', -- Default widget command arguments for creating this Thing as a widget. --- acl = '', -- Access Control List defining security restrains. --- boss = '', -- The Thing or person that owns this Thing, otherwise it is self owned. - - action = 'nada', -- An optional action to perform. - tell = '', -- The skang command that created this Thing. - pattern = '.*', -- A pattern to restrict values. - - isKeyed = false, -- Is this thing an arbitrarily Keyed table? - isReadOnly = false, -- Is this Thing read only? - isServer = false, -- Is this Thing server side? - isStub = false, -- Is this Thing a stub? - isStubbed = false, -- Is this Thing stubbed elsewhere? - - hasCrashed = 0, -- How many times this Thing has crashed. - - append = function (self,data) -- Append to the value of this Thing. - end, - - stuff = {}, -- The sub things this Thing has, for modules, tables, and Keyed tables. - errors = {}, -- A list of errors returned by isValid(). - - isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors. - -- Anything that overrides this method, should call this super method first. - local name = self.names[1] - local metaMum = getmetatable(parent) - local value = metaMum.__values[name] - local mum = metaMum.__self.names[1] - - local t = type(value) or 'nil' - self.errors = {} - -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions. - if 'nil' == t then - if self.required then table.insert(self.errors, mum .. '.' .. name .. ' is required!') end - else - if 'widget' == self.types[1] then - -- TODO - Should validate any attached Thing. - elseif self.types[1] ~= t then table.insert(self.errors, mum .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!') - else - if 'number' == t then value = '' .. value end - if ('number' == t) or ('string' == t) then - if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mum .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end - end - end - end - - for k, v in pairs(self.stuff) do - if not v:isValid(value) then - for i, w in ipairs(v.errors) do - table.insert(self.errors, w) - end - end - end - - return #(self.errors) == 0 - end, - - remove = function (self) -- Delete this Thing. - end, -} - - -getStuffed = function (parent, key) - local metaMum = getmetatable(parent) - local thingy - - if metaMum and metaMum.__self then - thingy = metaMum.__self.stuff[key] - - if not thingy then - -- Deal with getting a table entry. - if metaMum.__values[key] then - thingy = getmetatable(metaMum.__values[key]).__self - end - end - end - return metaMum, thingy -end - -local Mum = -{ - -__index = function (parent, key) - -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. - - -- First see if this is a Thing. - local metaMum, thingy = getStuffed(parent, key) - - if thingy then - return metaMum.__values[thingy.names[1] ] or thingy.default - end - - -- Then see if we can inherit it from Thing. - return Thing[key] -end, - -__newindex = function (parent, key, value) - -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. - - -- First see if this is a Thing. - local metaMum, thingy = getStuffed(parent, key) - - if not thingy then - if metaMum.__self.isKeyed then - -- Deal with setting a new Keyed table entry. - local newThing = copy(parent, key) - local newSelf = getmetatable(newThing).__self - rawset(metaMum.__values, key, newThing) - thingy = {} - for k, v in pairs(newSelf) do - thingy[k] = v - end - thingy.names={key} - thingy.types={'table'} - setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. - end - end - - if thingy then - local name = thingy.names[1] - local oldMum - - if 'table' == type(value) then - -- Coz setting it via metaMum screws with the __index stuff somehow. - local oldValue = metaMum.__values[name] - if 'table' == type(oldValue) then - oldMum = getmetatable(oldValue) - if oldMum then - -- TODO - This SHOULD work, but doesn't - - --setmetatable(value, oldMum) - -- Instead we do this - - -- Clear out any values in the old table. - for k, v in pairs(oldMum.__values) do - oldMum.__values[k] = nil - end - for k, v in pairs(value) do - local newK = oldMum.__self.stuff[k] - if newK then newK = newK.names[1] else newK = k end - oldMum.__values[newK] = v - end - end - end - end - if nil == oldMum then metaMum.__values[name] = value end - -- NOTE - invalid values are still stored, this is by design. - if not thingy:isValid(parent) then - for i, v in ipairs(thingy.errors) do - print('ERROR - ' .. v) - end - end - -- TODO - Go through it's linked things and set them to. - -- Done, don't fall through to the rawset() - return - end - - rawset(parent, key, value) -- Stuff it normally. -end, - -__call = function (func, ...) - return thingasm(func, ...) -end, - -} - -newMum = function () - local result = {} ---[[ From an email by Mike Pall - -"Important: create the metatable and its metamethods once and reuse -the _same_ metatable for _every_ instance." - -This is for LuaJIT, he's the author, and concerns performance. - -TODO - Which is the exact opposite of what we are doing. Perhaps we can fix that? -]] - for k, v in pairs(Mum) do - result[k] = v - end - result.__self = {stuff={}} - result.__values = {} - return result -end - - --- skang.thingasm() Creates a new Thing, or changes an existing one. ---[[ It can be called in many different ways - - -It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss) -Or it can be called with a table - {names, help, pattern='.*', acl='rwx'} - -The first argument can be another Thing (the parent), or a string list of names (see below). - -It can be called by itself, with no parent specified - - thingasm('foo', 'help text) -In this case the surrounding Lua environment becomes the parent of foo. - If the first argument (or first in the table) is a string, then it's this form. -All others include the parent as the first argument, which would be a table. - -It can be called by calling the parent as a function - - foo('bar', 'some help', types='table') -- ___call(foo, 'bar', ...) And now foo is the parent. - foo.bar{'baz', types='Keyed'} -- thingasm({foo.bar, 'baz', ...}) - foo.bar.baz{'field0'} -- thingasm({foo.bar.baz, 'field0'}) - foo.bar.baz{'field1'} -]] - --- names - a comma seperated list of names, aliases, and shortcuts. The first one is the official name. --- If this is not a new thing, then only the first one is used to look it up. --- So to change names, use skang.thingasm{'oldName', names='newName,otherNewName'} -thingasm = function (names, ...) - local params = {...} - local new = false - local parent - local set = true - - -- Check how we were called, and re arrange stuff to match. - if 0 == #params then - if ('table' == type(names)) then -- thingasm{...} - params = names - names = shiftLeft(params) - if 'table' == type(names) then -- thingasm{parent, 'foo', ...} - parent = names - names = shiftLeft(params) - end - end -- thingasm("foo") otherwise - else - if 'table' == type(names) then - parent = names - if 'string' == type(...) then params = {...} -- C or __call(table, string, ..) - elseif 'table' == type(...) then params = ... -- __call(table, table) - end - names = shiftLeft(params) - end -- thingasm('foo', ...) otherwise - end - - -- Break out the names. - names = csv2table(names) - local name = names[1] - local oldNames = {} - - -- TODO - Double check this comment - No need to bitch and return if no names, this will crash for us. - - -- Grab the environment of the calling function if no parent was passed in. - parent = parent or getfenv(2) - local metaMum = getmetatable(parent) - -- Coz at module creation time, Thing is an empty table, or in case this is for a new parent. - if nil == metaMum then - metaMum = newMum() - metaMum.__self.names = {parent._NAME or 'NoName'} - if parent._NAME then metaMum.__self.types = {'Module'} end - setmetatable(parent, metaMum) - end - - local thingy = metaMum.__self.stuff[name] - if not thingy then -- This is a new Thing. - new = true - thingy = {} - thingy.names = names - thingy.stuff = {} - setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. - end - - -- Pull out positional arguments. - thingy.help = params[1] or thingy.help - thingy.default = params[2] or thingy.default - local types = params[3] or table.concat(thingy.types or {}, ',') - - -- Pull out named arguments. - for k, v in pairs(params) do - if 'string' == type(k) then - if 'types' == k then types = v - elseif 'names' == k then - oldNames = thingy.names - thingy.names = cvs2table(v) - elseif 'required' == k then - if isBoolean(v) then thingy.required = true end - else thingy[k] = v - end - end - end - - -- Find type, default to string, then break out the other types. - local typ = type(thingy.default) - if 'nil' == typ then typ = 'string' end - if 'function' == typ then types = typ .. ',' .. types end - if '' == types then types = typ end - thingy.types = csv2table(types) - - if 'widget' == thingy.types[1] then - set = false - local args, err = loadstring('return ' .. thingy.widget) - if args then - setfenv(args, parent) - thingy.Cwidget = widget(args()) -print('\nNO IDEA WHY this does isValid() three times on the action, and the first one being a string.') - parent.W[name] = thingy - else - print("ERROR - " .. err) - end - end - - -- Deal with Keyed and tables. - if 'Keyed' == thingy.types[1] then - set = false - thingy.types[1] = 'table' - thingy.isKeyed = true - end - if 'table' == thingy.types[1] then - -- Default in this case becomes a parent. - if '' == thingy.default then thingy.default = {} end - local thisMum = newMum() - thisMum.__self = thingy - setmetatable(thingy.default, thisMum) - end - - if 'userdata' == thingy.types[1] then - set = false - end - - -- Remove old names, then stash the Thing under all of it's new names. - for i, v in ipairs(oldNames) do - metaMum.__self.stuff[v] = nil - end - for i, v in ipairs(thingy.names) do - metaMum.__self.stuff[v] = thingy - end - - -- This triggers the Mum.__newindex metamethod above. If nothing else, it triggers thingy.isValid() - if new and set then parent[name] = thingy.default end -end - - -fixNames = function (module, name) - local stuff = getmetatable(module) - if stuff then - stuff.__self.names[1] = name - for k, v in pairs(stuff.__self.stuff) do - if 'table' == v.types[1] then - local name = v.names[1] - print(name .. ' -> ' .. name) - fixNames(stuff.__values[name], name) - end - end - end -end - - -copy = function (parent, name) - local result = {} - local stuff = getmetatable(parent).__self.stuff - - for k, v in pairs(stuff) do - local temp = {} - for l, w in pairs(v) do - temp[l] = w - end - temp[1] = table.concat(temp.names, ',') - temp.names = nil - temp.types = table.concat(temp.types, ',') - temp.errors = nil - thingasm(result, temp) - end - getmetatable(result).__self.names = {name} - --- TODO - Should we copy values to? - - return result -end - -module = function (name) - _G[name] = require(name) - return _G[name] -end - -stuff = function (aThingy, aStuff) - return getmetatable(aThingy).__self.stuff[aStuff] -end - - -get = function (stuff, key, name) - local result - if name then - local thingy = getmetatable(stuff) - if thingy then - local this = thingy.__self.stuff[key] - if this then result = this[name] end - end - else - result = stuff[key] - end - return result -end - - -reset = function (stuff, key, name) - if name then - local thingy = getmetatable(stuff) - if thingy then - local this = thingy.__self.stuff[key] - if this then this[name] = nil end - end - else - stuff[key] = nil - end -end - - -set = function (stuff, key, name, value) - if 'nil' ~= type(value) then - local thingy = getmetatable(stuff) - if thingy then - local this = thingy.__self.stuff[key] - if this then this[name] = value end - end - else - -- In this case, value isn't there, so we are reusing the third argument as the value. - stuff[key] = name - end -end - - --- Get our C functions installed into skang. --- This has to be after thingasm is defined. -package.cpath = package.cpath .. ';../../libraries/lib?.so' -local GuiLua = require 'GuiLua' - - -thingasm('module,l', 'Load a module.', module, 'file') -thingasm('get', 'Get the current value of an existing Thing or metadata.', get, 'thing,key,name') -thingasm('reset', 'Reset the current value of an existing Thing or metadata.', reset, 'thing,key,name') -thingasm('set', 'Set the current value of an existing Thing or metadata.', set, 'thing,key,name,data') - -thingasm('nada', 'Do nothing.', function () --[[ This function intentionally left blank. ]] end) - - - --- Widget wrappers. --- TODO - Fix this up so we don't need .W -local widgets = {} ---thingasm{widgets, 'window', 'The window.', types='userdata'} -thingasm{widgets, 'W', 'Holds all the widgets', types='Keyed'} -widgets.W{'Cwidget', 'The widget.', types='userdata'} -widgets.W{'action,a', 'The action for the widget.', 'nada', types='string'} -local aIsValid = function (self, parent) - local result = Thing.isValid(self, parent) - - if result then - local value = parent[self.names[1] ] -print('NEW ACTION - ' .. self.names[1] .. ' = ' .. value .. ' ' .. type(parent.Cwidget)) - action(parent.Cwidget, value) - end - return result -end - -widgets.W{'look,l', 'The look of the widget.', types='string'} ---[[ -widgets.W{'colour,color,c', 'The colours of the widget.', types='table'} -widgets.W.c{'red,r', 'The red colour of the widget.', 255, types='number'} -widgets.W.c{'green,g', 'The green colour of the widget.', 255, types='number'} -widgets.W.c{'blue,b', 'The blue colour of the widget.', 255, types='number'} -widgets.W.c{'alpha,a', 'The alpha colour of the widget.', 255, types='number'} --- At this point we want to change widgets.W.c() to go to a different __call, coz right now it's going to the Mum.__call, which wraps thingasm. --- TODO - Keep an eye on this if we change to a single Mum, instead of the shallow copy we use now. -local wMum = getmetatable(widgets.W.c) -wMum.__call = function (func, ...) - return Colour(func, ...) -end -]] - -window = function(w, h, title, name) - name = name or 'myWindow' - local win = {} - win = copy(widgets, name) - local wMum, wThingy = getStuffed(win.W, 'a') - wThingy.isValid = aIsValid - win.window = Cwindow(w, h, title, name) - return win -end - -thingasm{'window', 'Specifies the size and title of the application Frame.', window, 'number,number,string', acl="GGG"} - - --- TODO - Some function stubs, for now. Fill them up later. -skang = function (name) -end - -thingasm('skang', 'Parse the contents of a skang file or URL.', skang, 'URL') - - -moduleEnd(_M) - -end - --- Boss is the person that owns a Thing. - ---[[ The original Skang parameters and commands. - public final static String MY_USAGE[][] = - { - {"skinURL", "skinURL", "Y", "s", null, "URL of skin file.", "", "RI-"}, - {"debug", "debug", "N", "", "0", "Set debugging level to :\n\t-1 - errors and warnings only (-q)\n\t0 - basic information\n\t1 - advanced information (-v)\n\t2 - trace functions\n\t3 - trace protocol\n\t4 - dump packets + stuff\n\t5 - detail", "", ""}, - {"browser", "browser", "N", "", "mozilla %f", "Browser to run.", "", ""}, - {"downloaddir", "downloadDir", "N", "", "download", "Download directory.", "", ""}, - {"sessionID", "sessionID", "N", "", null, "SessionID from servlet.", "", ""}, - {"JSESSIONID", "JSESSIONID", "N", "", null, "JSESSIONID from servlet engine.", "", ""}, - {"servletpath", "servletPath", "N", "", "matrix_rad", "Servlet path.", "", ""}, - {"servletport", "servletPort", "N", "", null, "Servlet port.", "", ""}, - {"servletsslport", "servletSSLPort", "N", "", null, "Servlet SSL port.", "", ""}, - {"HTML", "HTML", "N", "", "false", "Output to HTML?", "", ""}, - {"PHP", "PHP", "N", "", "false", "Output though the PHP wrapper", "", ""}, - {"inbrowser", "inBrowser", "N", "", "true", "Run in browser window?", "", ""}, - {"SSL", "SSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""}, - {"NOSSL", "NOSSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""}, - {"corporate", "corporate", "N", "", null, "Are we doing corporate shit?", "", ""}, - {"", "", "", "", "", "", "", ""} - }; - public final static String MY_SKANG[][] = - { --- {"module", "addModule", "file,data", "Load a module.", "", ""}, - {"append", "appendThing", "name,data", "Append to the current value of a Thing.", "", ""}, - {"#!java", "bash", "name,name,name,name,name,name,name", "A not so clever unix script compatability hack.", "", ""}, - {"pending", "pendingDoThing", "action", "Do an action when you are ready.", "", ""}, - {"applet", "doIfApplet", "action", "Only do this if we are an applet.", "", ""}, - {"application", "doIfApplication", "action", "Only do this if we are an application.", "", ""}, - {"corporateshit", "doIfCorporateShit", "action", "Only do this if we are doing corporate shit.", "", ""}, - {"realworld", "doIfRealWorld", "action", "Only do this if we are in the real world.", "", ""}, - {"servlet", "doIfServlet", "action", "Only do this if we are a servlet.", "", ""}, - {"do", "doThing", "action", "Do this action.", "", ""}, - {"grab", "getFile", "URL", "Grab a file from a URL.", "", ""}, --- {"get", "getThing", "name", "Get the current value of an existing thing.", "", ""}, - {"gimmeskin", "gimmeSkin", "", "Returns the modules default skin.", "", ""}, - {"help", "helpThing", "file", "Show help page.", "", ""}, --- {"nada", "nothing", "data", "Does nothing B-).", "", ""}, - {"postshow", "postShowThings", "URL,name", "POST the values of all Things to the URL, show the returned content.", "", ""}, - {"post", "postThings", "URL", "POST the values of all Things to the URL, return the content.", "", ""}, - {"postparse", "postParseThings", "URL", "POST the values of all Things to the URL, parse the returned content.", "", ""}, - {"quiet", "quiet", "", "Output errors and warnings only.", "", ""}, - {"remove", "removeThing", "name", "Remove an existing thing.", "", ""}, - {"sethelp", "setHelp", "name,data", "Change the help for something.", "", ""}, --- {"set", "setThing", "name,data", "Set the current value of an existing Thing.", "", ""}, --- {"skang", "skangRead", "URL", "Parse the contents of a skang file or URL.", "", ""}, --- {"quit", "startQuit", "", "Quit, exit, remove thyself.", "", ""}, - {"stopwhinging", "stopWhinging", "", "Clear all messages.", "", ""}, - {"tell", "tellThing", "name", "Returns details of an existing Thing.", "", ""}, - {"togglebug", "toggleIgnoreBrowserBug", "", "Toggle ignorance of a certain browser bug.", "", ""}, - {"verbose", "verbose", "", "Output advanced information.", "", ""}, - {"", "", "", "", "", ""} -]] - ---[[ The original SkangAWT parameters and commands. - public final static String MY_USAGE[][] = - { - {"", "", "", "", "", "", "", ""} - }; - public final static String MY_SKANG[][] = - { - {"taction", "tactionWidget", "name,action", "Set the alternative action for a widget.", "", ""}, - {"action", "actionWidget", "name,action", "Set the action for a widget.", "", ""}, - {"pane", "addPane", "name,x,y,w,h,data", "Add a pane to the current module.", "", ""}, - {"widget", "addWidget", "name,type,lx,ly,lw,lh,data,data", "Add a widget to the current skin.", "", ""}, - {"checkboxgroup", "checkBoxGroup", "number", "Make the next 'number' Checkboxes part of a check box group.", "", ""}, --- {"clear", "clearWidgets", "", "The current skin is cleared of all widgets.", "", ""}, - {"colour", "colourWidget", "name,r,g,b,alpha,r,g,b,alpha", "Set widget's background and foreground colour.", "", "GGG"}, - {"doaction", "doWidget", "name", "Do a widgets action.", "", "GGG"}, - {"disable", "disableWidget", "name", "Disable a widget.", "", "GGG"}, - {"enable", "enableWidget", "name", "Enable a widget.", "", "GGG"}, - {"hide", "hideWidget", "name", "Hide a widget.", "", "GGG"}, - {"hideall", "hideAllWidgets", "name,lx,ly,lw,lh", "Hide all widgets.", "", "GGG"}, - {"look", "lookWidget", "name,normal,ghost,active,toggle", "Set the current look of an existing widget.", "", "GGG"}, - {"mask", "maskWidget", "name,data", "Set the mask for a widget.", "", ""}, - {"onmouse", "onMouse", "name,data", "Do something on mouse hover.", "", ""}, - {"offmouse", "offMouse", "name,data", "Do something off mouse hover.", "", ""}, - {"popup", "popupWidget", "name,data,data,data,data", "Create a popup.", "", "GGG"}, - {"readonly", "readOnlyWidget", "name", "Make a widget read only.", "", "GGG"}, - {"writeonly", "writeOnlyWidget", "name", "Make a widget write only.", "", "GGG"}, - {"satori", "satori", "x,y", "Give me the developers menu.", "", "GGG"}, - {"showloginwindow", "showLoginWindow", "", "Show user login window.", "", "GGG"}, - {"show", "showWidget", "name", "Show a widget.", "", "GGG"}, --- {"window", "setSkangFrame", "x,y,name", "Specifies the size and title of the application Frame.", "", "GGG"}, - {"stuff", "stuffWidget", "name,data", "Set the stuff for a widget's pane.", "", ""}, - {"stufflet", "stuffWidget", "name,data,data", "Set the stufflet for a widget.", "", ""}, - {"stufflist", "stuffListWidget", "name,data", "List the stuff in this widget.", "", ""}, - {"stuffload", "stuffLoadWidget", "name,data,data", "Load the stuff for a widget.", "", ""}, - {"stuffsave", "stuffSaveWidget", "name,data,data", "Save the stuff for a widget.", "", ""}, - {"stuffdelete", "stuffDeleteWidget", "name,data,data", "Delete the stuff for a widget.", "", "SSS"}, - {"stuffclear", "stuffClearWidget", "name,data", "Clear the stuff for a widget.", "", "SSS"}, - {"rowtowidgets", "rowToWidgets", "name", "Copy Grid row to matching widgets.", "", ""}, - {"widgetstorow", "widgetsToRow", "name,data", "Copy matching widgets to Grid row.", "", ""}, - {"clearrow", "clearRow", "name", "Clear Grid row and matching widgets.", "", ""}, - {"clearrowwidgets", "clearRowWidgets", "name", "Clear only the Grid row matching widgets.", "", ""}, - {"", "", "", "", "", ""} - }; -]] - - ---[[ security package - -Java skang could run as a stand alone applicion, as an applet in a web -page, or as a servlet on a web server. This was pretty much all -transparent to the user. The security system reflected that. Lua skang -wont run in web pages, but can still have client / server behaviour. -The general idea was, and still is, that the GUI is the client side (in -web page, in extantz GUI) that sends values back to the server side -(servlet, actual Lua package running as a separate process, or the world -server for in world scripts). Client side can request that server side -runs commands. Serevr side can send values and commands back to the -client. Mostly it all happenes automatically through the ACLs. - -Bouncer is the Java skang security manager, it extended the Java -SecurityManager. Lua has no such thing, though C code running stuff in -a sandbox does a similar job. Fascist is the Java security supervisor, -again should go into the C sandbox. - -Human is used for authenticating a human, Puter for authenticating a -computer, Suits for corporate style authentication, and they all -extended Who, the base authentication module. - -For now, I have no idea how this all translates into Lua, but putting -this here for a reminder to think about security during the design -stage. - - -This is the old Java ACL definition - - acl - access control list. -Owner is usually the person running the Thingspace. -RWX~,---,Rwxgroup1,r--group2,r-xgroup3,rw-group4,--X~user1 -rwx~ is for the owner. The second one is the default. The rest are per group or per user. -Capital letters mean that they get access from the network to. ---- No access at all. -RWX Full access. -R-- Read only access. -r-x Read and execute, but only locally. -rw- Read and write a field, but don't execute a method. --w- A password. --a- An append only log file. --A- An append only log file on the server. -Ri- read, but only set from init (ei. skinURL not set from properties or skang files). -RI- As above, but applet.init() can set it too. ---x Thing is both method and field, only execution of the method is allowed. ---p Run as owner (Pretend). ---P Run across the network as owner (can run in applet triggered by server). -s-- Read only, but not even visible to applets. -sss Only visible to servlets and applications. ---S Send to servlet to execute if applet, otherwise execute normally. -S-- Read only, but ignore local version and get it from server. -ggg GUI Thing, only visible to Applets and applications. -GGG GUI Thing, but servlets can access them across the net. - -For servlet only modules from an applet, the applet only loads the skanglet class, using it for all -access to the module. - - -Lua Security best practices - - - From an email by Mike Pall - - -"The only reasonably safe way to run untrusted/malicious Lua scripts is -to sandbox it at the process level. Everything else has far too many -loopholes." - -http://lua-users.org/lists/lua-l/2011-02/msg01595.html -http://lua-users.org/lists/lua-l/2011-02/msg01609.html -http://lua-users.org/lists/lua-l/2011-02/msg01097.html -http://lua-users.org/lists/lua-l/2011-02/msg01106.html - -So that's processes, not threads like LuaProc does. B-( - -However, security in depth is good, so still worthwhile looking at it from other levels as well. - -General solution is to create a new environment, which we are doing -anyway, then whitelist the safe stuff into it, instead of blacklisting -unsafe stuff. Coz you never know if new unsafe stuff might exist. - -Different between 5.1 (setfenv) and 5.2 (_ENV) - -http://lua-users.org/wiki/SandBoxes - - ------------------------------------------------------- --- make environment -local env = -- add functions you know are safe here -{ - ipairs = ipairs, - next = next, - pairs = pairs, - pcall = pcall, - tonumber = tonumber, - tostring = tostring, - type = type, - unpack = unpack, - coroutine = { create = coroutine.create, resume = coroutine.resume, - running = coroutine.running, status = coroutine.status, - wrap = coroutine.wrap }, - string = { byte = string.byte, char = string.char, find = string.find, - format = string.format, gmatch = string.gmatch, gsub = string.gsub, - len = string.len, lower = string.lower, match = string.match, - rep = string.rep, reverse = string.reverse, sub = string.sub, - upper = string.upper }, - table = { insert = table.insert, maxn = table.maxn, remove = table.remove, - sort = table.sort }, - math = { abs = math.abs, acos = math.acos, asin = math.asin, - atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos, - cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor, - fmod = math.fmod, frexp = math.frexp, huge = math.huge, - ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max, - min = math.min, modf = math.modf, pi = math.pi, pow = math.pow, - rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh, - sqrt = math.sqrt, tan = math.tan, tanh = math.tanh }, - os = { clock = os.clock, difftime = os.difftime, time = os.time }, -} - --- run code under environment [Lua 5.1] -local function run(untrusted_code) - if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end - local untrusted_function, message = loadstring(untrusted_code) - if not untrusted_function then return nil, message end - setfenv(untrusted_function, env) - return pcall(untrusted_function) -end - --- run code under environment [Lua 5.2] -local function run(untrusted_code) - local untrusted_function, message = load(untrusted_code, nil, 't', env) - if not untrusted_function then return nil, message end - return pcall(untrusted_function) -end ------------------------------------------------------- - -Also includes a table of safe / unsafe stuff. - - -While whitelisting stuff, could also wrap unsafe stuff to make them more safe. - -print() -> should reroute to our output widgets. -rawget/set() -> don't bypass the metatables, but gets tricky and recursive. -require -> don't allow bypassing the sandbox to get access to restricted modules -package.loaded -> ditto - - -Other things to do - - -debug.sethook() can be used to call a hook every X lines, which can help with endless loops and such. -Have a custom memory allocater, like edje_lua2 does. - ------------------------------------------------------- ------------------------------------------------------- - -The plan - - - Process level - - Have a Lua script runner C program / library. - It does the LuaProc thing, and the edje_lua2 memory allocater thing. - Other code feeds scripts to it via a pipe. - Unless they are using this as a library. - It can be chrooted, ulimited, LXC containered, etc. - It has an internal watchdog thread. - The truly paranoid can have a watchdog timer process watch it. - Watches for a "new Lua state pulled off the queue" signal. - This could be done from the App that spawned it. - - It runs a bunch of worker threads, with a queue of ready Lua states. - Each Lua state being run has lua_sethook() set to run each X lines, AND a watchdog timer set. - If either is hit, then the Lua state is put back on the queue. - (Currently LuaProc states go back an the queue when waiting for a "channel message", which does a lua_yeild().) - NOTE - apparently "compiled code" bypasses hooks, though there's an undocumented LuaJIT compile switch for that. http://lua-users.org/lists/lua-l/2011-02/msg01106.html - EFL is event based. - LSL is event based. - LuaSL is event based. - Java skang is event based, with anything that has long running stuff overriding runBit(). - Coz Java AWT is event based, the "events" are over ridden methods in each widget class. - Should all work if we keep this as event based. - An "event" is a bit of Lua script in a string, at Input trust level usually. - Configurable for this script runner is - - IP address & port, or pipe name. - Security level to run at, defaults to Network. - Number of worker threads, defaults to number of CPUs. - Memory limit per Lua state. - Lines & time per tick for Lua states. - - Different levels of script trust - - System - the local skang and similar stuff. - -> No security at all. - App - Lua scripts and C from the running application. - -> Current module level security. - Each has it's own environment, with no cross environment leakage. - Runs in the Apps process, though that might be the script runner as a library. - Or could be skang's main loop. - Local - Lua scripts and skang files sent from the client apps running on the same system. - -> As per App. - Runs in a script runner, maybe? Or just the Apps script runner. - Limit OS and file stuff, the client app can do those itself. - Network - Lua scripts and skang files sent from the network. - -> Runs in a script runner. - Option to chroot it, ulimit it, etc. - Heavily Lua sandboxed via environment. - It can access nails, but via network derived credentials. - Can have very limited local storage, not direct file access though, but via some sort of security layer. - Can have network access. - Can have GUI access, but only to it's own window. - Config - Lua scripts run as configuration files. - -> Almost empty local environment, can really only do math and set Things. - Input - Lua scripts run as a result of hitting skang widgets on the other end of a socket. - -> As per Config, but can include function calls. - Also would need sanitizing, as this can come from the network. - Microsoft - Not to be trusted at all. - Apple - Don't expect them to trust us. - - NOTE - edje_lua2 would be roughly Local trust level. - - So we need to be able to pass Lua between processes - - Have to be able to do it from C, from Lua, and from Lua embedded in C. - edje_lua2 - uses Edje messages / signals. - LuaSL - uses Ecore_Con, in this case a TCP port, even though it's only local. - LuaSL mainloop for it's scripts is to basically wait for these messages from LuaProc. - Which yield's until we get one. - Eventually gets Lua code as a string -> loadstring() -> setfenv() -> pcall() - The pcall returns a string that we send back as a LuaProc message. - Extantz - we want to use stdin/stdout for the pipe, but otherwise LuaSL style semantics. - - Hmm, Extantz could run external skang modules in two ways - - Run the module as a separate app, with stdin/stdout. - Require the module, and run it internally. - Stuff like the in world editor and radar would be better as module, since they are useless outside Extantz? - Radar is useless outside Extantz, editor could be used to edit a local file. - Stuff like woMan would be better off as a separate application, so it can start and stop extantz. - -]] - - ---[[ -The main loop. - A Lua skang module is basically a table, with skang managed fields. - Once it calls moduleEnd(), it's expected to have finished. - test.lua is currently an exception, it runs test stuff afterwards. - So their code just registers Things and local variables. - Some of those Things might be functions for manipulating internal module state. - A C module has it's variables managed outside of it by Lua. - So would have to go via LUA_GLOBALSINDEX to get to them. - Unless they are userdata, which are C things anyway. - Though it's functions are obviously inside the C module. - Normal Lua module semantics expect them to return to the require call after setting themselves up. - So test.lua is really an aberation. - - Soooo, where does the main loop go? - The skang module itself could define the main loop. - Should not actually call it though, coz skang is itself a module. - - In Java we had different entry points depending on how it was called. - If it was called as an applet or application, it got it's own thread with a mainloop. - That main loop just slept and called runBit() on all registered modules. - If it was loaded as a module, it bypassed that stuff. - I don't think Lua provides any such mechanism. - In theory, the first call to moduleBegin would be the one that was started as an application. - So we could capture that, and test against it in moduleEnd to find when THAT module finally got to the end. - THEN we can start the main loop (test.lua being the exception that fucks this up). - Though test.lua could include a runBits() that quits the main loop, then starts the main loop at the very end once more? - The problem with this is applications that require skang type modules. - The first one they require() isn't going to return. - Eventually skang itself will get loaded. It looks at the "arg" global, which SHOULD be the command line. - Including the name of the application, which we could use to double check. - Extantz / script runner would set this arg global itself. - - Skang applications have one main loop per app. - Skang modules use the main loop of what ever app called them. - Non skang apps have the option of calling skangs main loop. - Extantz starts many external skang apps, which each get their own main loop. - Extantz has it's own Ecore main loop. - LuaSL still has one main loop per script. - LSL scripts get their own main loop, but LSL is stupid and doesn't have any real "module" type stuff. - -What does skang main loop actually do? - In Java it just slept for a bit, then called runBit() from each module, and the only module that had one was watch. - Actually better off using Ecore timers for that sort of thing. - Skang main loop has nothing to do? lol - Well, except for the "wait for LuaProc channel messages" thing. - Widget main loop would be Ecore's main loop. - - Extantz loads a skang module. - Module runs in extantz script runner. - Module runs luaproc message main loop from skang. - Evas / Elm widgets send signals, call C callbacks. - Extantz sends Lua input scripts via luaproc messages to module. - Extantz runs separate skang module. - Module runs in it's own process. - Module runs it's own message loop on the end of stdin. - Evas / Elm widgets send signals, call C callbacks. - Extantz sends Lua Input scripts to module via stdout. - Module runs stand alone. - Module runs in it's own process. - Module has to have widget start Ecore's main loop. - Module runs it's own message loop, waiting for widget to send it messages. - Evas / Elm widgets send signals, call C callbacks. - Widget sends Lua Input scripts to module. - -Alternate plan - - Skang has no main loop, modules are just tables. - OK, so sometimes skang has to start up an Ecore main loop. - With either Ecore_Con, or Evas and Elm. - skang.message(string) - Call it to pass a bit of Lua to skang, which is likely Input. - Widget twiddles happen in Ecore's main loop, via signals and call backs. - Eventually these call backs hit the widgets action string. - Send that action string to skang.message(). - - Extantz loads a skang module. - Extantz has a skang Lua state. - Module is just another table in skang. - Widget -> callback -> action string -> skang.message() - Extantz runs separate skang module. - Skang module C code runs an Ecore main loop. - Module is just another table in skang. - Skang C uses Ecore_Con to get messages from Extantz' Ecore_Con. - Widget -> callback -> action string -> Ecore_Con -> skang.message() - OOORRRR .... - Skang runs a main loop reading stdin. - Widget -> callback -> action string -> stdout -> skang.message() - Module runs stand alone. - Skang module C code runs an Ecore main loop. - Module is just another table in skang. - Widget -> callback -> action string -> skang.message() - Extantz loads a skang module from the network. - Skang module runs on the server with it's own Ecore main loop somehow. - Module is just another table in skang. - Extantz uses Ecore_Con to get messages from Extantz' Ecore_Con, via TCP. - Widget -> callback -> action string -> Ecore_Con -> skang.message() - OOORRRR .... - Remember, these are untrusted, so that's what the script runner is for. - Skang module runs in the script runner, with some sort of luaproc interface. - Widget -> callback -> action string -> Ecore_Con -> luaproc -> skang.message() - - Skang running as a web server. - Skang runs an Ecore main loop. - Skang has an Ecore_Con server attached to port 80. - Skang loads modules as usual. - Skang responds to specific URLs with HTML forms generated from Skang files. - Skang gets post data back, which kinda looks like Input. B-) - - Extantz runs a LuaSL / LSL script from the network. - Send this one to the LuaSL script runner. - Coz LSL scripts tend to have lots of copies with the same name in different objects. - Could get too huge to deal with via "just another table". - In this case, compiling the LSL might be needed to. - -]] - - ---[[ TODO - NOTE that skang.thingasm{} doesn't care what other names you pass in, they all get assigned to the Thing. - - - Widget - - Should include functions for actually dealing with widgets, plus a way - of creating widgets via introspection. Should also allow access to - widget internals via table access. Lua code could look like this - - - foo = widget('label', 0, "0.1", 0.5, 0, 'Text goes here :") - -- Method style. - foo:colour(255, 255, 255, 0, 0, 100, 255, 0) - foo:hide() - foo:action("skang.load(some/skang/file.skang)") - -- Table style. - foo.action = "skang.load('some/skang/file.skang')" - foo.colour.r = 123 - foo.look('some/edje/file/somewhere.edj') - foo.help = 'This is a widget for labelling some foo.' - - Widgets get a type as well, which would be label, button, edit, grid, etc. - A grid could even have sub types - grid,number,string,button,date. - A required widget might mean that the window HAS to have one. - Default for a widget could be the default creation arguments - '"Press me", 1, 1, 10, 50'. - - skang.thingasm{'myButton', types='Button', rectangle={1, 1, 10, 50}, title='Press me', ...} - - skang.thingasm('foo,s,fooAlias', 'Foo is a bar, not the drinking type.', function () print('foo') end, '', '"button", "The foo :"' 1, 1, 10, 50') - myButton = skang.widget('foo') -- Gets the default widget creation arguments. - myButton:colour(1, 2, 3, 4) - -- Use generic positional / named arguments for widget to, then we can do - - myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, look='edit.edj', colour={blue=20}, action='...'} - -- Using the Thing alias stuff, maybe we can do the "first stage tokenise" step after all - - myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, l='edit.edj', c={b=20}, a='...'} - myEditor:colour(1, 2, 3, 4, 5, 6, 7, 8) - myButton = 'Not default' -- myEditor and foo change to. Though now foo is a command, not a parameter, so maybe don't change that. - -- Though the 'quit' Thing could have a function that does quitting, this is just an example of NOT linking to a Thing. - -- If we had linked to this theoretical 'quit' Thing, then pushing that Quit button would invoke it's Thing function. - quitter = skang.widget(nil, 'button', 'Quit', 0.5, 0.5, 0.5, 0.5) - quitter:action('quit') - - For widgets with "rows", which was handled by Stuff in skang, we could - maybe use the Lua concat operator via metatable. I think that works by - having the widget (a table) on one side of the concat or the other, and - the metatable function gets passed left and right sides, then must - return the result. Needs some experimentation, but it might look like - this - - - this.bar = this.bar .. 'new choice' - this.bar = 'new first choice' .. this.bar - - - coordinates and sizes - - - Originally skang differentiated between pixels and character cells, - using plain integers to represent pixels, and _123 to represent - character cells. The skang TODO wanted to expand that to percentages - and relative numbers. We can't use _123 in Lua, so some other method - needs to be used. Should include those TODO items in this new design. - - Specifying character cells should be done as strings - "123" - - Percentages can be done as small floating point numbers between 0 and 1, - which is similar to Edje. Since Lua only has a floating point number - type, both 0 and 1 should still represent pixels / character cells - - - 0.1, 0.5, "0.2", "0.9" - - Relative numbers could be done as strings, with the widget to be - relative to, a + or -, then the number. This still leaves the problem - of telling if the number is pixels or character cells. Also, relative - to what part of the other widget? Some more thought needs to be put - into this. - - Another idea for relative numbers could be to have a coord object with - various methods, so we could have something like - - - button:bottom(-10):right(5) -- 10 pixels below the bottom of button, 5 pixels to the right of the right edge of button. - button:width("12") -- 12 characters longer than the width of button. - - But then how do we store that so that things move when the thing they are - relative to moves? - - - Squeal - - Squeal was the database driver interface for SquealStuff, the database - version of Stuff. Maybe we could wrap esskyuehl? Not really in need of - database stuff for now, but should keep it in mind. - For SquealStuff, the metadata would be read from the SQL database automatically. - - squeal.database('db', 'host', 'someDb', 'user', 'password') -> Should return a Squeal Thing. - local db = require 'someDbThing' -> Same as above, only the database details are encodode in the someDbThing source, OR come from someDbThing.properties. - db:getTable('stuff', 'someTable') -> Grabs the metadata, but not the rows. - db:read('stuff', 'select * from someTable'} -> Fills stuff up with several rows, including setting up the metadata for the columns. - stuff[1].field1 -> Is a Thing, with a proper metatable, that was created automatically from the database meta data. - stuff:read('someIndex') -> Grabs a single row that has the key 'someIndex', or perhaps multiple rows since this might have SQL under it. - stuff = db:read('stuff', 'select * from someTable where key='someIndex') - - stuff:write() -> Write all rows to the database table. - stuff:write(1) -> Write one row to the database table. - stuff:stuff('field1').isValid = someFunction -- Should work, all stuff[key] share the same Thing description. - stuff:isValid(db) -> Validate the entire Thing against it's metadata at least. - window.stuff = stuff -> Window gets stuff as it's default 'Keyed' table, any widgets with same names as the table fields get linked. - grid.stuff = stuff -> Replace contents of this grid widget with data from all the rows in stuff. - choice.stuff = stuff -> As in grid, but only using the keys. - widget.stuff = stuff:stuff('field1') -> This widget gets a particular stufflet. - widget would have to look up getmetatable(window.stuff).parent. Or maybe this should work some other way? - - In all these cases above, stuff is a 'Keyed' table that has a Thing metatable, so it has sub Things. - Should include some way of specifyings details like key name, where string, etc. - getmetatable(stuff).__keyName - getmetatable(stuff).__squeal.where - And a way to link this database table to others, via the key of the other, as a field in this Stuff. - stuff:stuff('field0').__link = {parent, key, index} - In Java we had this - - -public class PersonStuff extends SquealStuff -{ - -... - - public final static String FULLNAME = "fullname"; - - public static final String keyField = "ID"; // Name of key field/s. - public static final String where = keyField + "='%k'"; - public static final String listName = "last"; - public static final String tables = "PEOPLE"; - public static final String select = null; - public static final String stufflets[] = - { - keyField, - "PASSWD_ID|net.matrix_rad.squeal.PasswdStuff|,OTHER", - "QUALIFICATION_IDS|net.matrix_rad.people.QualificationStuff|,OTHER", - "INTERESTING_IDS|net.matrix_rad.people.InterestingStuff|,OTHER", - "title", - "first", - "middle", - "last", - "suffix", - -... - - FULLNAME + "||,VARCHAR,512" - }; -} - -]] - - --- Gotta check out this _ENV thing, 5.2 only. Seems to replace the need for setfenv(). Seems like setfenv should do what we want, and is more backward compatible. --- "_ENV is not supported directly in 5.1, so its use can prevent a module from remaining compatible with 5.1. --- Maybe you can simulate _ENV with setfenv and trapping gets/sets to it via __index/__newindex metamethods, or just avoid _ENV." --- LuaJIT doesn't support _ENV anyway. diff --git a/media/checkme.txt b/media/checkme.txt new file mode 100644 index 0000000..957099c --- /dev/null +++ b/media/checkme.txt @@ -0,0 +1,2 @@ +A file for elm_app_info_set() to find, to make sure it found the correct +data directory. diff --git a/src/GuiLua/GuiLua.h b/src/GuiLua/GuiLua.h index 95353fb..2dd77ba 100644 --- a/src/GuiLua/GuiLua.h +++ b/src/GuiLua/GuiLua.h @@ -1,27 +1,8 @@ - -#define EFL_API_OVERRIDE 1 -/* Enable access to unstable EFL API that are still in beta */ -#define EFL_BETA_API_SUPPORT 1 -/* Enable access to unstable EFL EO API. */ -#define EFL_EO_API_SUPPORT 1 - -#include -#include - -#include - -// This got left out. -//EAPI Evas_3D_Scene *evas_3d_scene_add(Evas *e); - - -#include -#include -#include -#include - +#include "SledjHamr.h" #include "LumbrJack.h" #include "Runnr.h" + typedef struct _globals globals; diff --git a/src/GuiLua/build.lua b/src/GuiLua/build.lua index 16f4153..169e198 100755 --- a/src/GuiLua/build.lua +++ b/src/GuiLua/build.lua @@ -15,9 +15,9 @@ end LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS -removeFiles(dir, {'test_c.so', 'GuiLua.o', '../../libraries/libGuiLua.so', '../../skang'}) +removeFiles(dir, {'test_c.so', 'GuiLua.o', lib_d .. '/libGuiLua.so', '../../skang'}) runCommand('C modules', dir, 'gcc ' .. CFLAGS .. ' -fPIC -shared -o test_c.so test_c.c') runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c GuiLua.c') -runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libGuiLua.so -o ../../libraries/libGuiLua.so GuiLua.o') +runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libGuiLua.so -o ' .. lib_d .. '/libGuiLua.so GuiLua.o') runCommand('C apps', dir, 'gcc ' .. CFLAGS .. ' -Wl,-export-dynamic -o ../../skang skang.c ' .. LDFLAGS .. ' -lGuiLua ' .. libs) diff --git a/src/GuiLua/skang.c b/src/GuiLua/skang.c index facc239..f78c1c5 100644 --- a/src/GuiLua/skang.c +++ b/src/GuiLua/skang.c @@ -3,11 +3,7 @@ EAPI_MAIN int elm_main(int argc, char **argv) { - elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR); - elm_app_compile_data_dir_set(PACKAGE_DATA_DIR); - elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR); - elm_app_info_set(elm_main, "GuiLua", "skang.lua"); - + HamrTime(elm_main, "GuiLua"); GuiLuaDo(argc, argv); return 0; diff --git a/src/GuiLua/test.sh b/src/GuiLua/test.sh index bea2fc3..0cf39d9 100755 --- a/src/GuiLua/test.sh +++ b/src/GuiLua/test.sh @@ -2,5 +2,5 @@ wd=$(pwd) -export LUA_PATH="$wd/../../libraries/?.lua;./?.lua" +export LUA_PATH="$wd/../../lib/?.lua;./?.lua" ../../skang -l test -foo "argy bargy" diff --git a/src/LuaSL/LuaSL_compile.c b/src/LuaSL/LuaSL_compile.c index 771888e..f32e0c0 100644 --- a/src/LuaSL/LuaSL_compile.c +++ b/src/LuaSL/LuaSL_compile.c @@ -2119,9 +2119,9 @@ boolean compilerSetup(gameGlobals *ourGlobals) } // Compile the constants. - snprintf(buf, sizeof(buf), "lua -e 'require(\"LSL\").gimmeLSL()' > %s/libraries/constants.lsl", PACKAGE_DATA_DIR); + snprintf(buf, sizeof(buf), "lua -e 'require(\"LSL\").gimmeLSL()' > %s/constants.lsl", PACKAGE_LIB_DIR); system(buf); - snprintf(buf, sizeof(buf), "%s/libraries/constants.lsl", PACKAGE_DATA_DIR); + snprintf(buf, sizeof(buf), "%s/constants.lsl", PACKAGE_LIB_DIR); compileLSL(ourGlobals, NULL, "FAKE_SID", buf, TRUE); return TRUE; diff --git a/src/LuaSL/LuaSL_test.c b/src/LuaSL/LuaSL_test.c index 58d6225..27b9626 100644 --- a/src/LuaSL/LuaSL_test.c +++ b/src/LuaSL/LuaSL_test.c @@ -147,7 +147,7 @@ static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Ad ourGlobals->server = ev->server; gettimeofday(&startTime, NULL); - snprintf(buf, sizeof(buf), "%s/media/Test sim/objects", PACKAGE_DATA_DIR); + snprintf(buf, sizeof(buf), "%s/Test sim/objects", PACKAGE_DATA_DIR); eina_file_dir_list(buf, EINA_TRUE, dirList_compile, ourGlobals); // Wait awhile, then start sending events for testing. ecore_timer_add(0.5, _timer_cb, ourGlobals); @@ -392,7 +392,7 @@ int main(int argc, char **argv) evas_object_focus_set(ourGlobals.bg, EINA_TRUE); ourGlobals.edje = edje_object_add(ourGlobals.canvas); - snprintf(buf, sizeof(buf), "%s/media/%s.edj", PACKAGE_DATA_DIR, "LuaSL"); + snprintf(buf, sizeof(buf), "%s/%s.edj", PACKAGE_DATA_DIR, "LuaSL"); if (!edje_object_file_set(ourGlobals.edje, buf, group)) { int err = edje_object_load_error_get(ourGlobals.edje); @@ -409,7 +409,7 @@ int main(int argc, char **argv) evas_object_resize(ourGlobals.edje, WIDTH, HEIGHT); evas_object_show(ourGlobals.edje); - snprintf(buf, sizeof(buf), "%s/media/bubble_sh.png", PACKAGE_DATA_DIR); + snprintf(buf, sizeof(buf), "%s/bubble_sh.png", PACKAGE_DATA_DIR); for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++) { sh = evas_object_image_filled_add(ourGlobals.canvas); @@ -419,7 +419,7 @@ int main(int argc, char **argv) evas_object_data_set(ourGlobals.bg, names[(i * 2) + 1], sh); } - snprintf(buf, sizeof(buf), "%s/media/bubble.png", PACKAGE_DATA_DIR); + snprintf(buf, sizeof(buf), "%s/bubble.png", PACKAGE_DATA_DIR); for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++) { bub = evas_object_image_filled_add(ourGlobals.canvas); diff --git a/src/LuaSL/build.lua b/src/LuaSL/build.lua index f86715c..6b34145 100755 --- a/src/LuaSL/build.lua +++ b/src/LuaSL/build.lua @@ -13,11 +13,20 @@ if 'nil' == type(dir) then dir = workingDir end + removeFiles(dir, {'../../LuaSL', '*.o', '*.output', '*.backup', '../../media/LuaSL.edj', 'LuaSL_lexer.h', 'LuaSL_lexer.c', 'LuaSL_lemon_yaccer.h', 'LuaSL_lemon_yaccer.c', 'LuaSL_lemon_yaccer.out'}) -- Run lemon first, flex depends on it to define the symbol values. runCommand('lemon', dir, '../../libraries/lemon/lemon -s -T../../libraries/lemon/lempar.c LuaSL_lemon_yaccer.y') runCommand('flex', dir, 'flex -C --outfile=LuaSL_lexer.c --header-file=LuaSL_lexer.h LuaSL_lexer.l') runCommand('edje_cc', dir, 'edje_cc ' .. EDJE_FLAGS .. ' LuaSL.edc ../../media/LuaSL.edj') + +-- While SledHamr.c does this, we can't use that here, coz LuaSL is not an Elm app. +-- Neither is LuaSL_test actually. +CFLAGS = CFLAGS .. ' -DPACKAGE_BIN_DIR=\\"' .. bin_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_LIB_DIR=\\"' .. lib_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_DATA_DIR=\\"' .. data_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_LOCALE_DIR=\\"' .. locale_d .. '\\"' + compileFiles('../../LuaSL', dir, {'LuaSL_main', 'LuaSL_compile', 'LuaSL_threads', 'LuaSL_utilities', 'LuaSL_lexer', 'LuaSL_lemon_yaccer'}) compileFiles('LuaSL_test', dir, {'LuaSL_test', 'LuaSL_utilities'}) diff --git a/src/LuaSL/test.sh b/src/LuaSL/test.sh index 1c26ade..435c5e2 100755 --- a/src/LuaSL/test.sh +++ b/src/LuaSL/test.sh @@ -4,7 +4,7 @@ wd=$(pwd) # Kill any left overs. killall -KILL LuaSL -export LUA_PATH="$wd/../../libraries/?.lua" +export LUA_PATH="$wd/../../lib/?.lua" case $@ in diff --git a/src/extantz/extantz.c b/src/extantz/extantz.c index 09b5196..ab09fd0 100644 --- a/src/extantz/extantz.c +++ b/src/extantz/extantz.c @@ -39,7 +39,7 @@ static Elm_Genlist_Item_Class *viewer_gic = NULL; //static const char *img1 = PACKAGE_DATA_DIR "/media/plant_01.jpg"; //static const char *img2 = PACKAGE_DATA_DIR "/media/sky_01.jpg"; -static const char *img3 = PACKAGE_DATA_DIR "/media/rock_01.jpg"; +static const char *img3 = "rock_01.jpg"; #define EPHYSICS_TEST_THEME "extantz" @@ -1240,7 +1240,7 @@ static void woMan_add(GLData *gld) // Evas_Object *win, *bg, *bx, *ic, *bb, *av, *en, *bt, *nf, *tab, *tb, *gridList, *viewerList, *menu; Evas_Object *win, *bx, *bt, *nf, *tab, *tb, *gridList, *viewerList, *menu; Elm_Object_Item *tb_it, *menu_it, *tab_it; -// char buf[PATH_MAX]; + char buf[PATH_MAX]; int i; win = fang_win_add(gld); @@ -1351,9 +1351,10 @@ static void woMan_add(GLData *gld) evas_object_size_hint_weight_set(nf, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(nf, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(nf); - + + sprintf(buf, "%s/%s", elm_app_data_dir_get(), img3); tab = viewerList; tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Viewers", _promote, tab_it); - tab = _content_image_new(win, img3); tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Landmarks", _promote, tab_it); + tab = _content_image_new(win, strdup(buf)); tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Landmarks", _promote, tab_it); tab = gridList; tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Grids", _promote, tab_it); elm_box_pack_end(bx, nf); @@ -1393,7 +1394,7 @@ EAPI_MAIN int elm_main(int argc, char **argv) EPhysics_Body *box_body1, *box_body2; Evas_Object *box1, *box2; GLData *gld = NULL; -// char buf[PATH_MAX]; + char buf[PATH_MAX]; // int i; // Eina_Bool gotWebKit = elm_need_web(); // Initialise ewebkit if it exists, or return EINA_FALSE if it don't. @@ -1401,10 +1402,7 @@ EAPI_MAIN int elm_main(int argc, char **argv) // Don't do this, we need to clean up other stuff to, so set a clean up function below. //elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); - // If you want efl to handle finding your bin/lib/data dirs, you must do this below. - elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR); - elm_app_compile_data_dir_set(PACKAGE_DATA_DIR); - elm_app_info_set(elm_main, "datadir", "media/sky_03.jpg"); + HamrTime(elm_main, "extantz"); fprintf(stdout, "prefix was set to: %s\n", elm_app_prefix_dir_get()); fprintf(stdout, "data directory is: %s\n", elm_app_data_dir_get()); fprintf(stdout, "library directory is: %s\n", elm_app_lib_dir_get()); @@ -1538,7 +1536,8 @@ EAPI_MAIN int elm_main(int argc, char **argv) ephysics_body_friction_set(boundary, 0); box1 = elm_image_add(gld->win); - elm_image_file_set(box1, PACKAGE_DATA_DIR "/media/" EPHYSICS_TEST_THEME ".edj", "blue-cube"); + sprintf(buf, "%s/%s.edj", elm_app_data_dir_get(), EPHYSICS_TEST_THEME); + elm_image_file_set(box1, strdup(buf), "blue-cube"); evas_object_move(box1, gld->win_w / 2 - 80, gld->win_h - 200); evas_object_resize(box1, 70, 70); evas_object_show(box1); @@ -1552,7 +1551,7 @@ EAPI_MAIN int elm_main(int argc, char **argv) ephysics_body_sleeping_threshold_set(box_body1, 0.1, 0.1); box2 = elm_image_add(gld->win); - elm_image_file_set(box2, PACKAGE_DATA_DIR "/media/" EPHYSICS_TEST_THEME ".edj", "purple-cube"); + elm_image_file_set(box2, strdup(buf), "purple-cube"); evas_object_move(box2, gld->win_w / 2 + 10, gld->win_h - 200); evas_object_resize(box2, 70, 70); evas_object_show(box2); diff --git a/src/extantz/extantz.h b/src/extantz/extantz.h index afb94af..25b78cf 100644 --- a/src/extantz/extantz.h +++ b/src/extantz/extantz.h @@ -6,14 +6,8 @@ #define USE_DEMO 1 #define DO_GEARS 0 -#if USE_EO - /* Enable access to unstable EFL API that are still in beta */ - #define EFL_BETA_API_SUPPORT 1 - /* Enable access to unstable EFL EO API. */ - #define EFL_EO_API_SUPPORT 1 -#endif -#include +#include "SledjHamr.h" #include #include #include diff --git a/src/libraries/LumbrJack.c b/src/libraries/LumbrJack.c new file mode 100644 index 0000000..084d916 --- /dev/null +++ b/src/libraries/LumbrJack.c @@ -0,0 +1,85 @@ +/* LumbrJack - a logging library that wraps Eina logging. + +*/ + + +#include "LumbrJack.h" + + +static char dateTime[DATE_TIME_LEN]; + +static void _ggg_log_print_cb(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args) +{ + FILE *f = data; + char dt[DATE_TIME_LEN + 1]; + char fileTab[256], funcTab[256]; + + getDateTime(NULL, dt, NULL); + dt[19] = '\0'; + if (12 > strlen(file)) + snprintf(fileTab, sizeof(fileTab), "%s\t\t", file); + else + snprintf(fileTab, sizeof(fileTab), "%s\t", file); + snprintf(funcTab, sizeof(funcTab), "\t%s", fnc); + fprintf(f, "%s ", dt); + if (f == stderr) + eina_log_print_cb_stderr(d, level, fileTab, funcTab, line, fmt, data, args); + else if (f == stdout) + eina_log_print_cb_stdout(d, level, fileTab, funcTab, line, fmt, data, args); + fflush(f); +} + +int loggingStartup(char *name, int logDom) +{ + if (logDom < 0) + { + logDom = eina_log_domain_register(name, NULL); + if (logDom < 0) + { + EINA_LOG_CRIT("could not register log domain '%s'", name); + return logDom; + } + } + eina_log_level_set(EINA_LOG_LEVEL_DBG); + eina_log_domain_level_set(name, EINA_LOG_LEVEL_DBG); + eina_log_print_cb_set(_ggg_log_print_cb, stderr); + + // Shut up the excess debugging shit from EFL. + eina_log_domain_level_set("eo", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("eldbus", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("eet", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_audio", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_con", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_evas", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("ecore_system_upower", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("eio", EINA_LOG_LEVEL_WARN); + eina_log_domain_level_set("evas_main", EINA_LOG_LEVEL_WARN); + + return logDom; +} + +char *getDateTime(struct tm **nowOut, char *dateOut, time_t *timeOut) +{ + struct tm *newTime; + time_t szClock; + char *date = dateTime; + + // Get time in seconds + time(&szClock); + // Convert time to struct tm form + newTime = localtime(&szClock); + + if (nowOut) + *nowOut = newTime; + if (dateOut) + date = dateOut; + if (timeOut) + *timeOut = szClock; + + // format + strftime(date, DATE_TIME_LEN, "%d/%m/%Y %H:%M:%S\r", newTime); + return (dateTime); +} diff --git a/src/libraries/LumbrJack.h b/src/libraries/LumbrJack.h new file mode 100644 index 0000000..4a3290c --- /dev/null +++ b/src/libraries/LumbrJack.h @@ -0,0 +1,38 @@ + +#include +#include + +#include + + +#define PC(...) EINA_LOG_DOM_CRIT(ourGlobals->logDom, __VA_ARGS__) +#define PE(...) EINA_LOG_DOM_ERR(ourGlobals->logDom, __VA_ARGS__) +#define PW(...) EINA_LOG_DOM_WARN(ourGlobals->logDom, __VA_ARGS__) +#define PD(...) EINA_LOG_DOM_DBG(ourGlobals->logDom, __VA_ARGS__) +#define PI(...) EINA_LOG_DOM_INFO(ourGlobals->logDom, __VA_ARGS__) + +#define PCm(...) EINA_LOG_DOM_CRIT(ourGlobals.logDom, __VA_ARGS__) +#define PEm(...) EINA_LOG_DOM_ERR(ourGlobals.logDom, __VA_ARGS__) +#define PWm(...) EINA_LOG_DOM_WARN(ourGlobals.logDom, __VA_ARGS__) +#define PDm(...) EINA_LOG_DOM_DBG(ourGlobals.logDom, __VA_ARGS__) +#define PIm(...) EINA_LOG_DOM_INFO(ourGlobals.logDom, __VA_ARGS__) + +#define D() PD("DEBUG") + + +// "01:03:52 01-01-1973\n\0" +#define DATE_TIME_LEN 21 + + +#ifndef FALSE +// NEVER change this +typedef enum +{ + FALSE = 0, + TRUE = 1 +} boolean; +#endif + + +int loggingStartup(char *name, int logDom); +char *getDateTime(struct tm **nowOut, char *dateOut, time_t *tiemOut); diff --git a/src/libraries/Runnr.c b/src/libraries/Runnr.c new file mode 100644 index 0000000..a24e7f5 --- /dev/null +++ b/src/libraries/Runnr.c @@ -0,0 +1,318 @@ +/* Runnr - a library that deals with running Lua scripts. + +*/ + + +#include "Runnr.h" + + +void dumpStack(lua_State *L, int i) +{ + int type = lua_type(L, i); + + switch (type) + { + case LUA_TNONE : printf("Stack %d is empty\n", i); break; + case LUA_TNIL : printf("Stack %d is a nil\n", i); break; + case LUA_TBOOLEAN : printf("Stack %d is a boolean - %d\n", i, lua_toboolean(L, i)); break; + case LUA_TNUMBER : printf("Stack %d is a number\n - %f", i, lua_tonumber(L, i)); break; + case LUA_TSTRING : printf("Stack %d is a string - %s\n", i, lua_tostring(L, i)); break; + case LUA_TFUNCTION : printf("Stack %d is a function\n", i); break; + case LUA_TTHREAD : printf("Stack %d is a thread\n", i); break; + case LUA_TTABLE : + { + int j; + + printf("Stack %d is a table", i); + lua_getfield(L, i, "_NAME"); + j = lua_gettop(L); + if (lua_isstring(L, j)) + printf(" - %s", lua_tostring(L, j)); + lua_pop(L, 1); + printf("\n"); + break; + } + case LUA_TUSERDATA : printf("Stack %d is a userdata\n", i); break; + case LUA_TLIGHTUSERDATA : printf("Stack %d is a light userdata\n", i); break; + default : printf("Stack %d is unknown\n", i); break; + } +} + + +// These are what the various symbols are for each type - +// int % +// num # +// str $ +// bool ! +// C func & +// table.field @ Expects an integer and a string. +// nil ~ +// table {} Starts and stops filling up a new table. +// ( Just syntax sugar for call. +// call ) Expects an integer, the number of results left after the call. +// FIXME: Still to do, if we ever use them - +// stack = Get a value from the stack, expects a stack index. +// userdata + +// lightuserdata * +// thread ^ + +static char *_push_name(lua_State *L, char *q, int *idx) // Stack usage [-0, +1, e or m] +{ + char *p = q; + char temp = '\0'; + + // A simplistic scan through an identifier, it's wrong, but it's quick, + // and we don't mind that it's wrong, coz this is only internal. + while (isalnum((int)*q)) + q++; + temp = *q; + *q = '\0'; + if (*idx > 0) + lua_getfield(L, *idx, p); // Stack usage [-0, +1, e] + else + { + if (p != q) + lua_pushstring(L, p); // Stack usage [-0, +1, m] + else + { + lua_pushnumber(L, (lua_Number) (0 - (*idx))); + (*idx)--; + } + } + *q = temp; + + return q; +} + +int pull_lua(lua_State *L, int i, char *params, ...) // Stack usage - + // if i is a table + // [-n, +n, e] + // else + // [-0, +0, -] +{ + va_list vl; + char *f = strdup(params); + char *p = f; + int n = 0, j = i, count = 0; + Eina_Bool table = EINA_FALSE; + + if (!f) return -1; + va_start(vl, params); + + if (lua_istable(L, i)) // Stack usage [-0, +0, -] + { + j = -1; + table = EINA_TRUE; + } + + while (*p) + { + char *q; + Eina_Bool get = EINA_TRUE; + + while (isspace((int)*p)) + p++; + q = p + 1; + switch (*p) + { + case '%': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] + if (lua_isnumber(L, j)) // Stack usage [-0, +0, -] + { + int *v = va_arg(vl, int *); + *v = lua_tointeger(L, j); // Stack usage [-0, +0, -] + n++; + } + break; + } + case '#': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] + if (lua_isnumber(L, j)) // Stack usage [-0, +0, -] + { + double *v = va_arg(vl, double *); + *v = lua_tonumber(L, j); // Stack usage [-0, +0, -] + n++; + } + break; + } + case '$': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] + if (lua_isstring(L, j)) // Stack usage [-0, +0, -] + { + char **v = va_arg(vl, char **); + size_t len; + char *temp = (char *) lua_tolstring(L, j, &len); // Stack usage [-0, +0, m] + + len++; // Cater for the null at the end. + *v = malloc(len); + if (*v) + { + memcpy(*v, temp, len); + n++; + } + } + break; + } + case '!': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e] + if (lua_isboolean(L, j)) // Stack usage [-0, +0, -] + { + int *v = va_arg(vl, int *); + *v = lua_toboolean(L, j); // Stack usage [-0, +0, -] + n++; + } + break; + } + default: + { + get = EINA_FALSE; + break; + } + } + + if (get) + { + if (table) + { + // If this is a table, then we pushed a value on the stack, pop it off. + lua_pop(L, 1); // Stack usage [-n, +0, -] + } + else + j++; + count++; + } + p = q; + } + + va_end(vl); + free(f); + if (count > n) + n = 0; + else if (table) + n = 1; + return n; +} + +int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em] +{ + va_list vl; + char *f = strdup(params); + char *p = f; + int n = 0, table = 0, i = -1; + + if (!f) return -1; + + va_start(vl, params); + + while (*p) + { + char *q; + Eina_Bool set = EINA_TRUE; + + while (isspace((int)*p)) + p++; + q = p + 1; + switch (*p) + { + case '%': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushinteger(L, va_arg(vl, int)); // Stack usage [-0, +1, -] + break; + } + case '#': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushnumber(L, va_arg(vl, double)); // Stack usage [-0, +1, -] + break; + } + case '$': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushstring(L, va_arg(vl, char *)); // Stack usage [-0, +1, m] + break; + } + case '!': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushboolean(L, va_arg(vl, int)); // Stack usage [-0, +1, -] + break; + } + case '=': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushvalue(L, va_arg(vl, int)); // Stack usage [-0, +1, -] + break; + } + case '@': + { + int tabl = va_arg(vl, int); + char *field = va_arg(vl, char *); + + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_getfield(L, tabl, field); // Stack usage [-0, +1, e] + break; + } + case '&': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushcfunction(L, va_arg(vl, void *)); // Stack usage [-0, +1, m] + break; + } + case '~': + { + if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m] + lua_pushnil(L); // Stack usage [-0, +1, -] + break; + } + case '(': // Just syntax sugar. + { + set = EINA_FALSE; + break; + } + case ')': + { + lua_call(L, n - 1, va_arg(vl, int)); + n = 0; + set = EINA_FALSE; + break; + } + case '{': + { + lua_newtable(L); + table++; + n++; + set = EINA_FALSE; + break; + } + case '}': + { + table--; + set = EINA_FALSE; + break; + } + default: + { + set = EINA_FALSE; + break; + } + } + + if (set) + { + if (table > 0) + lua_settable(L, -3); // Stack usage [-2, +0, e] + else + n++; + } + p = q; + } + + va_end(vl); + free(f); + return n; +} diff --git a/src/libraries/Runnr.h b/src/libraries/Runnr.h new file mode 100644 index 0000000..dc720ff --- /dev/null +++ b/src/libraries/Runnr.h @@ -0,0 +1,15 @@ + +#include +#include + +#include + +#include +#include +#include +#include + + +void dumpStack(lua_State *L, int i); +int pull_lua(lua_State *L, int i, char *params, ...); +int push_lua(lua_State *L, char *params, ...); diff --git a/src/libraries/SledjHamr.c b/src/libraries/SledjHamr.c new file mode 100644 index 0000000..aa68774 --- /dev/null +++ b/src/libraries/SledjHamr.c @@ -0,0 +1,23 @@ +#include "SledjHamr.h" + +void HamrTime(void *elm_main, char *domain) +{ + elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR); + elm_app_compile_data_dir_set(PACKAGE_DATA_DIR); + elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR); + elm_app_compile_locale_set(PACKAGE_LOCALE_DIR); + // Do this after the above calls, but before changing the working directory, or screwing with argv[0]. + // It tries to set up the package paths depending on where the executable is, so things are relocatable. + // First argument is the elm_main() function that Elementary starts us from. + // Second argument should be a lower case string used as the "domain", which is different from the log domain. + // It's used lower case as part of the data directory path. + // So, if prefix is /usr/local, then the system data dir is /usr/local/share, + // and this apps data dir is /usr/local/share/"domain". + // It's used upper case as part of environment variables to override directory paths at run time. + // So "DOMAIN"_PREFIX, "DOMAIN"_BIN_DIR, "DOMAIN"_LIB_DIR, "DOMAIN"_DATA_DIR, and "DOMAIN"_LOCALE_DIR + // Third argument is the name of a file it can check for to make sure it found the correct path. + // This file is looked for in the data dir. + elm_app_info_set(elm_main, domain, "checkme.txt"); + // Once this is all setup, the code can do - + // elm_app_prefix_dir_get(); // or bin, lib, data, locale. +} diff --git a/src/libraries/SledjHamr.h b/src/libraries/SledjHamr.h new file mode 100644 index 0000000..893d90e --- /dev/null +++ b/src/libraries/SledjHamr.h @@ -0,0 +1,14 @@ + +#define EFL_API_OVERRIDE 1 +/* Enable access to unstable EFL API that are still in beta */ +#define EFL_BETA_API_SUPPORT 1 +/* Enable access to unstable EFL EO API. */ +#define EFL_EO_API_SUPPORT 1 + +#include +#include + +#include + + +void HamrTime(void *elm_main, char *domain); diff --git a/src/libraries/build.lua b/src/libraries/build.lua new file mode 100755 index 0000000..6566e66 --- /dev/null +++ b/src/libraries/build.lua @@ -0,0 +1,33 @@ +#!/usr/bin/env lua + +local dir = ... + +if 'nil' == type(dir) then + local build, err = loadfile('../../build.lua') + if build then + setfenv(build, getfenv(2)) + build(2) + else + print("ERROR - " .. err) + end + dir = workingDir +end + +LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS + +removeFiles(dir, {'LumbrJack.o', lib_d .. '/libLumbrJack.so', 'Runnr.o', lib_d .. '/libRunnr.so', 'SledjHamr.o', lib_d .. '/libSledjHamr.so'}) + +runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -fPIC -c LumbrJack.c') +runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libLumbrJack.so -o ' .. lib_d .. '/libLumbrJack.so LumbrJack.o') + +runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c Runnr.c') +runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libRunnr.so -o ' .. lib_d .. '/libRunnr.so Runnr.o') + +-- For Elm apps, these are all centrally controlled in libSledjHamr. +CFLAGS = CFLAGS .. ' -DPACKAGE_BIN_DIR=\\"' .. bin_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_LIB_DIR=\\"' .. lib_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_DATA_DIR=\\"' .. data_d .. '\\"' +CFLAGS = CFLAGS .. ' -DPACKAGE_LOCALE_DIR=\\"' .. locale_d .. '\\"' + +runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c SledjHamr.c') +runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libSledjHamr.so -o ' .. lib_d .. '/libSledjHamr.so SledjHamr.o') -- cgit v1.1