Several escaping fixes
[project/luci.git] / libs / core / luasrc / util.lua
index e4e99b9..ef1b8fe 100644 (file)
@@ -24,15 +24,15 @@ limitations under the License.
 
 ]]--
 
 
 ]]--
 
--- LuCI utility functions.
+--- LuCI utility functions.
 module("luci.util", package.seeall)
 
 --
 -- Class helper routines
 --
 
 module("luci.util", package.seeall)
 
 --
 -- Class helper routines
 --
 
---- Creates a Class object (Python-style object model)
--- Creates a new class object which can be instantiated by calling itself.
+--- Create a Class object (Python-style object model).
+-- The class object can be instantiated by calling itself.
 -- Any class functions or shared parameters can be attached to this object.
 -- Attaching a table to the class object makes this table shared between
 -- all instances of this class. For object parameters use the __init__ function.
 -- Any class functions or shared parameters can be attached to this object.
 -- Attaching a table to the class object makes this table shared between
 -- all instances of this class. For object parameters use the __init__ function.
@@ -75,7 +75,7 @@ end
 --- Test whether the given object is an instance of the given class.
 -- @param object       Object instance
 -- @param class                Class object to test against
 --- Test whether the given object is an instance of the given class.
 -- @param object       Object instance
 -- @param class                Class object to test against
--- @return                     Boolean indicating wheather the object is an instance
+-- @return                     Boolean indicating whether the object is an instance
 -- @see                                class
 -- @see                                clone
 function instanceof(object, class)
 -- @see                                class
 -- @see                                clone
 function instanceof(object, class)
@@ -162,7 +162,7 @@ end
 
 --- Write given object to stderr.
 -- @param obj  Value to write to stderr
 
 --- Write given object to stderr.
 -- @param obj  Value to write to stderr
--- @return             Boolean indicating wheather the write operation was successful
+-- @return             Boolean indicating whether the write operation was successful
 function perror(obj)
        return io.stderr:write(tostring(obj) .. "\n")
 end
 function perror(obj)
        return io.stderr:write(tostring(obj) .. "\n")
 end
@@ -186,10 +186,10 @@ end
 -- String and data manipulation routines
 --
 
 -- String and data manipulation routines
 --
 
---- Escapes all occurences of the given character in given string.
+--- Escapes all occurrences of the given character in given string.
 -- @param s    String value containing unescaped characters
 -- @param c    String value with character to escape (optional, defaults to "\")
 -- @param s    String value containing unescaped characters
 -- @param c    String value with character to escape (optional, defaults to "\")
--- @return     String value with each occurence of character escaped with "\"
+-- @return     String value with each occurrence of character escaped with "\"
 function escape(s, c)
        c = c or "\\"
        return s:gsub(c, "\\" .. c)
 function escape(s, c)
        c = c or "\\"
        return s:gsub(c, "\\" .. c)
@@ -199,6 +199,8 @@ end
 -- @param value        String value containing the data to escape
 -- @return             String value containing the escaped data
 function pcdata(value)
 -- @param value        String value containing the data to escape
 -- @return             String value containing the escaped data
 function pcdata(value)
+       if not value then return end
+       value = tostring(value)
        value = value:gsub("&", "&")
        value = value:gsub('"', """)
        value = value:gsub("'", "'")
        value = value:gsub("&", "&")
        value = value:gsub('"', """)
        value = value:gsub("'", "'")
@@ -206,15 +208,15 @@ function pcdata(value)
        return value:gsub(">", ">")
 end
 
        return value:gsub(">", ">")
 end
 
---- Splits given string on a defined seperator sequence and return a table
+--- Splits given string on a defined separator sequence and return a table
 -- containing the resulting substrings. The optional max parameter specifies
 -- the number of bytes to process, regardless of the actual length of the given
 -- containing the resulting substrings. The optional max parameter specifies
 -- the number of bytes to process, regardless of the actual length of the given
--- string. The optional last parameter, regex, sepcifies wheather the separator
+-- string. The optional last parameter, regex, specifies whether the separator
 -- sequence is interpreted as regular expression.
 -- @param str          String value containing the data to split up
 -- @param pat          String with separator pattern (optional, defaults to "\n")
 -- sequence is interpreted as regular expression.
 -- @param str          String value containing the data to split up
 -- @param pat          String with separator pattern (optional, defaults to "\n")
--- @param max          Num of bytes to process (optional, default is string length)
--- @param regexp       Boolean indicating wheather to interprete the separator
+-- @param max          Maximum times to split (optional)
+-- @param regex        Boolean indicating whether to interpret the separator
 --                                     pattern as regular expression (optional, default is false)
 -- @return                     Table containing the resulting substrings
 function split(str, pat, max, regex)
 --                                     pattern as regular expression (optional, default is false)
 -- @return                     Table containing the resulting substrings
 function split(str, pat, max, regex)
@@ -259,7 +261,7 @@ function trim(str)
 end
 
 --- Parse certain units from the given string and return the canonical integer
 end
 
 --- Parse certain units from the given string and return the canonical integer
--- value or 0 if the unit is unknown. Upper- or lowercase is irrelevant.
+-- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
 -- Recognized units are:
 --     o "y"   - one year   (60*60*24*366)
 --  o "m"      - one month  (60*60*24*31)
 -- Recognized units are:
 --     o "y"   - one year   (60*60*24*366)
 --  o "m"      - one month  (60*60*24*31)
@@ -320,7 +322,7 @@ end
 --- Combines two or more numerically indexed tables into one.
 -- @param tbl1 Table value to combine
 -- @param tbl2 Table value to combine
 --- Combines two or more numerically indexed tables into one.
 -- @param tbl1 Table value to combine
 -- @param tbl2 Table value to combine
--- @param tblN More values to combine
+-- @param ...  More tables to combine
 -- @return             Table value containing all values of given tables
 function combine(...)
        local result = {}
 -- @return             Table value containing all values of given tables
 function combine(...)
        local result = {}
@@ -335,7 +337,7 @@ end
 --- Checks whether the given table contains the given value.
 -- @param table        Table value
 -- @param value        Value to search within the given table
 --- Checks whether the given table contains the given value.
 -- @param table        Table value
 -- @param value        Value to search within the given table
--- @return             Boolean indicating wheather the given value occurs within table
+-- @return             Boolean indicating whether the given value occurs within table
 function contains(table, value)
        for k, v in pairs(table) do
                if value == v then
 function contains(table, value)
        for k, v in pairs(table) do
                if value == v then
@@ -358,7 +360,7 @@ end
 
 --- Clones the given object and return it's copy.
 -- @param object       Table value to clone
 
 --- Clones the given object and return it's copy.
 -- @param object       Table value to clone
--- @param deep         Boolean indicating wheather to do recursive cloning
+-- @param deep         Boolean indicating whether to do recursive cloning
 -- @return                     Cloned table value
 function clone(object, deep)
        local copy = {}
 -- @return                     Cloned table value
 function clone(object, deep)
        local copy = {}
@@ -376,17 +378,82 @@ function clone(object, deep)
 end
 
 
 end
 
 
+--- Create a dynamic table which automatically creates subtables.
+-- @return     Dynamic Table
+function dtable()
+        return setmetatable({}, { __index =
+                function(tbl, key)
+                        return rawget(tbl, key)
+                         or rawget(rawset(tbl, key, dtable()), key)
+                end
+        })
+end
+
+
+-- Serialize the contents of a table value.
+function _serialize_table(t)
+       local data = ""
+       for k, v in pairs(t) do
+               k = serialize_data(k)
+               v = serialize_data(v)
+               data = data .. ( #data > 0 and ", " or "" ) ..
+                       '[' .. k .. '] = ' .. v
+       end
+       return data
+end
+
+--- Recursively serialize given data to lua code, suitable for restoring
+-- with loadstring().
+-- @param val  Value containing the data to serialize
+-- @return             String value containing the serialized code
+-- @see                        restore_data
+-- @see                        get_bytecode
+function serialize_data(val)
+       if val == nil then
+               return "nil"
+       elseif type(val) == "number" then
+               return val
+       elseif type(val) == "string" then
+               return string.format("%q", val)
+       elseif type(val) == "boolean" then
+               return val and "true" or "false"
+       elseif type(val) == "function" then
+               return string.format("loadstring(%q)", get_bytecode(val))
+       elseif type(val) == "table" then
+               return "{ " .. _serialize_table(val) .. " }"
+       else
+               return '"[unhandled data type:' .. type(val) .. ']"'
+       end
+end
+
+--- Restore data previously serialized with serialize_data().
+-- @param str  String containing the data to restore
+-- @return             Value containing the restored data structure
+-- @see                        serialize_data
+-- @see                        get_bytecode
+function restore_data(str)
+       return loadstring("return " .. str)()
+end
+
+
 --
 -- Byte code manipulation routines
 --
 
 --
 -- Byte code manipulation routines
 --
 
---- Return the current runtime bytecode of the given function. The byte code
+--- Return the current runtime bytecode of the given data. The byte code
 -- will be stripped before it is returned.
 -- will be stripped before it is returned.
--- @param f    Function value to return as bytecode
--- @return     String value containing the bytecode of the given function
-function get_bytecode(f)
-       local d = string.dump(f)
-       return d and strip_bytecode(d)
+-- @param val  Value to return as bytecode
+-- @return             String value containing the bytecode of the given data
+function get_bytecode(val)
+       local code
+
+       if type(val) == "function" then
+               code = string.dump(val)
+       else
+               code = string.dump( loadstring( "return " .. serialize_data(val) ) )
+       end
+
+       return code and strip_bytecode(code)
 end
 
 --- Strips unnescessary lua bytecode from given string. Information like line
 end
 
 --- Strips unnescessary lua bytecode from given string. Information like line
@@ -509,17 +576,96 @@ end
 
 
 --
 
 
 --
+-- System utility functions
+--
+
+--- Test whether the current system is operating in big endian mode.
+-- @return     Boolean value indicating whether system is big endian
+function bigendian()
+       return string.byte(string.dump(function() end), 7) == 0
+end
+
+--- Execute given commandline and gather stdout.
+-- @param command      String containing command to execute
+-- @return                     String containing the command's stdout
+function exec(command)
+       local pp   = io.popen(command)
+       local data = pp:read("*a")
+       pp:close()
+
+       return data
+end
+
+--- Return a line-buffered iterator over the output of given command.
+-- @param command      String containing the command to execute
+-- @return                     Iterator
+function execi(command)
+       local pp = io.popen(command)
+
+       return pp and function()
+               local line = pp:read()
+               
+               if not line then
+                       pp:close()
+               end
+               
+               return line
+       end
+end
+
+-- Deprecated
+function execl(command)
+       local pp   = io.popen(command)
+       local line = ""
+       local data = {}
+
+       while true do
+               line = pp:read()
+               if (line == nil) then break end
+               table.insert(data, line)
+       end
+       pp:close()
+
+       return data
+end
+
+--- Returns the absolute path to LuCI base directory.
+-- @return             String containing the directory path
+function libpath()
+       return luci.fs.dirname(require("luci.debug").__file__)
+end
+
+
+--
 -- Coroutine safe xpcall and pcall versions modified for Luci
 -- original version:
 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
 --
 -- Coroutine safe xpcall and pcall versions modified for Luci
 -- original version:
 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
 --
+-- Copyright © 2005 Kepler Project.
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the "Software"),
+-- to deal in the Software without restriction, including without limitation
+-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
+-- and/or sell copies of the Software, and to permit persons to whom the
+-- Software is furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+-- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+-- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 local performResume, handleReturnValue
 local oldpcall, oldxpcall = pcall, xpcall
 coxpt = {}
 setmetatable(coxpt, {__mode = "kv"})
 
 
 local performResume, handleReturnValue
 local oldpcall, oldxpcall = pcall, xpcall
 coxpt = {}
 setmetatable(coxpt, {__mode = "kv"})
 
---- Identity function for copcall
+-- Identity function for copcall
 local function copcall_id(trace, ...)
   return ...
 end
 local function copcall_id(trace, ...)
   return ...
 end
@@ -552,7 +698,7 @@ function copcall(f, ...)
        return coxpcall(f, copcall_id, ...)
 end
 
        return coxpcall(f, copcall_id, ...)
 end
 
---- Handle return value of protected call
+-- Handle return value of protected call
 function handleReturnValue(err, co, status, ...)
        if not status then
                return false, err(debug.traceback(co, (...)), ...)
 function handleReturnValue(err, co, status, ...)
        if not status then
                return false, err(debug.traceback(co, (...)), ...)
@@ -564,7 +710,7 @@ function handleReturnValue(err, co, status, ...)
        end
 end
 
        end
 end
 
---- Resume execution of protected function call
+-- Resume execution of protected function call
 function performResume(err, co, ...)
        return handleReturnValue(err, co, coroutine.resume(co, ...))
 end
 function performResume(err, co, ...)
        return handleReturnValue(err, co, coroutine.resume(co, ...))
 end