* libs/core: Made luci.util.strip_bytecode forward-compatible to upcoming OpenWRT...
[project/luci.git] / libs / core / luasrc / util.lua
index 0559fff..76e382b 100644 (file)
@@ -36,7 +36,7 @@ function class(base)
                setmetatable(inst, {__index = class})
                
                if inst.__init__ then
-                       local stat, err = pcall(inst.__init__, inst, ...)
+                       local stat, err = copcall(inst.__init__, inst, ...)
                        if not stat then
                                error(err)
                        end
@@ -87,20 +87,27 @@ end
 
 -- Checks whether a table has an object "value" in it
 function contains(table, value)
-       for k,v in pairs(table) do
+       for k, v in pairs(table) do
                if value == v then
-                       return true
+                       return k
                end
        end
        return false
 end
 
 
+-- Dumps and strips a Lua-Function
+function dump(f)
+       local d = string.dump(f)
+       return d and strip_bytecode(d)
+end
+
+
 -- Dumps a table to stdout (useful for testing and debugging)
 function dumptable(t, i)
        i = i or 0
        for k,v in pairs(t) do
-               print(string.rep("\t", i) .. k, v)
+               print(string.rep("\t", i) .. tostring(k), tostring(v))
                if type(v) == "table" then
                        dumptable(v, i+1)
                end
@@ -145,6 +152,12 @@ function pcdata(value)
 end
 
 
+-- Returns an error message to stdout
+function perror(obj)
+       io.stderr:write(tostring(obj) .. "\n")
+end
+
+
 -- Resets the scope of f doing a shallow copy of its scope into a new table
 function resfenv(f)
        setfenv(f, clone(getfenv(f)))
@@ -181,12 +194,108 @@ function split(str, pat, max, regex)
        return t
 end
 
+
+-- Strips lua bytecode
+-- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+function strip_bytecode(dump)
+       local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
+       local subint
+       if endian == 1 then
+               subint = function(dump, i, l)
+                       local val = 0
+                       for n = l, 1, -1 do
+                               val = val * 256 + dump:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       else
+               subint = function(dump, i, l)
+                       local val = 0
+                       for n = 1, l, 1 do
+                               val = val * 256 + dump:byte(i + n - 1)
+                       end
+                       return val, i + l
+               end
+       end
+    
+       local strip_function
+       strip_function = function(dump)
+               local count, offset = subint(dump, 1, size)
+               local stripped, dirty = string.rep("\0", size), offset + count
+               offset = offset + count + int * 2 + 4
+               offset = offset + int + subint(dump, offset, int) * ins
+               count, offset = subint(dump, offset, int)
+               for n = 1, count do
+                       local t
+                       t, offset = subint(dump, offset, 1)
+                       if t == 1 then
+                               offset = offset + 1
+                       elseif t == 4 then
+                               offset = offset + size + subint(dump, offset, size)
+                       elseif t == 3 then
+                               offset = offset + num
+                       elseif t == 254 or t == 9 then
+                               offset = offset + lnum
+                       end
+               end
+               count, offset = subint(dump, offset, int)
+               stripped = stripped .. dump:sub(dirty, offset - 1)
+               for n = 1, count do
+                       local proto, off = strip_function(dump:sub(offset, -1))
+                       stripped, offset = stripped .. proto, offset + off - 1
+               end
+               offset = offset + subint(dump, offset, int) * int + int
+               count, offset = subint(dump, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(dump, offset, size) + size + int * 2
+               end
+               count, offset = subint(dump, offset, int)
+               for n = 1, count do
+                       offset = offset + subint(dump, offset, size) + size
+               end
+               stripped = stripped .. string.rep("\0", int * 3)
+               return stripped, offset
+       end
+       
+       return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
+end
+
+
+-- Creates a new threadlocal store
+function threadlocal()
+       local tbl = {}
+       
+       local function get(self, key)
+               local c = coroutine.running()
+               local thread = coxpt[c] or c or 0
+               if not rawget(self, thread) then
+                       return nil
+               end
+               return rawget(self, thread)[key]
+       end
+               
+       local function set(self, key, value)
+               local c = coroutine.running()
+               local thread = coxpt[c] or c or 0
+               if not rawget(self, thread) then
+                       rawset(self, thread, {})
+               end
+               rawget(self, thread)[key] = value
+       end
+       
+       setmetatable(tbl, {__index = get, __newindex = set, __mode = "k"})
+       
+       return tbl
+end
+
+
 -- Removes whitespace from beginning and end of a string
 function trim(str)
        local s = str:gsub("^%s*(.-)%s*$", "%1")
        return s
 end
 
+
 -- Updates given table with new values
 function update(t, updates)
        for k, v in pairs(updates) do
@@ -201,15 +310,127 @@ function updfenv(f, extscope)
 end
 
 
--- Validates a variable
-function validate(value, cast_number, cast_int)
-       if cast_number or cast_int then
-               value = tonumber(value)
+-- Parse units from a string and return integer value
+function parse_units(ustr)
+
+        local val = 0
+
+        -- unit map
+        local map = {
+                -- date stuff
+                y   = 60 * 60 * 24 * 366,
+                m   = 60 * 60 * 24 * 31,
+                w   = 60 * 60 * 24 * 7,
+                d   = 60 * 60 * 24,
+                h   = 60 * 60,
+               min = 60,
+
+                -- storage sizes
+                kb  = 1024,
+                mb  = 1024 * 1024,
+                gb  = 1024 * 1024 * 1024,
+
+                -- storage sizes (si)
+                kib = 1000,
+                mib = 1000 * 1000,
+                gib = 1000 * 1000 * 1000
+        }
+
+        -- parse input string
+        for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
+
+                local num = spec:gsub("[^0-9%.]+$","")
+                local spn = spec:gsub("^[0-9%.]+", "")
+
+                if map[spn] or map[spn:sub(1,1)] then
+                        val = val + num * ( map[spn] or map[spn:sub(1,1)] )
+                else
+                        val = val + num
+                end
+        end
+
+
+       return val
+end
+
+
+-- Provide various sorting iterators
+function _sortiter( t, f )
+       local keys = { }
+
+       for k, v in pairs(t) do
+               table.insert( keys, k )
        end
-       
-       if cast_int and value and not(value % 1 == 0) then
-               value = nil
+
+       local _pos = 0
+       local _len = table.getn( keys )
+
+       table.sort( keys, f )
+
+       return function()
+               _pos = _pos + 1
+               if _pos <= _len then
+                       return keys[_pos], t[keys[_pos]]
+               end
        end
-       
-       return value
-end
\ No newline at end of file
+end
+
+-- Return key, value pairs sorted by provided callback function
+function spairs(t,f)
+       return _sortiter( t, f )                                        
+end
+
+-- Return key, value pairs sorted by keys
+function kspairs(t)
+       return _sortiter( t )
+end
+
+-- Return key, value pairs sorted by values
+function vspairs(t)
+       return _sortiter( t, function (a,b) return t[a] < t[b] end )
+end
+
+
+-- Coroutine safe xpcall and pcall versions modified for Luci
+-- original version:
+-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
+local performResume, handleReturnValue
+local oldpcall, oldxpcall = pcall, xpcall
+coxpt = {}
+setmetatable(coxpt, {__mode = "kv"})
+
+function handleReturnValue(err, co, status, ...)
+    if not status then
+        return false, err(debug.traceback(co, (...)), ...)
+    end
+    if coroutine.status(co) == 'suspended' then
+        return performResume(err, co, coroutine.yield(...))
+    else
+        return true, ...
+    end
+end
+
+function performResume(err, co, ...)
+    return handleReturnValue(err, co, coroutine.resume(co, ...))
+end    
+
+function coxpcall(f, err, ...)
+    local res, co = oldpcall(coroutine.create, f)
+    if not res then
+        local params = {...}
+        local newf = function() return f(unpack(params)) end
+        co = coroutine.create(newf)
+    end
+    local c = coroutine.running()
+    coxpt[co] = coxpt[c] or c or 0
+    
+    return performResume(err, co, ...)
+end
+
+local function id(trace, ...)
+  return ...
+end
+
+function copcall(f, ...)
+    return coxpcall(f, id, ...)
+end