* Performance optimizations
[project/luci.git] / libs / core / luasrc / util.lua
1 --[[
2 LuCI - Utility library
3
4 Description:
5 Several common useful Lua functions
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at 
16
17         http://www.apache.org/licenses/LICENSE-2.0 
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26
27 module("luci.util", package.seeall)
28
29
30 -- Lua simplified Python-style OO class support emulation
31 function class(base)
32         local class = {}
33         
34         local create = function(class, ...)
35                 local inst = {}
36                 setmetatable(inst, {__index = class})
37                 
38                 if inst.__init__ then
39                         local stat, err = pcall(inst.__init__, inst, ...)
40                         if not stat then
41                                 error(err)
42                         end
43                 end
44                 
45                 return inst
46         end
47         
48         local classmeta = {__call = create}
49         
50         if base then
51                 classmeta.__index = base
52         end
53         
54         setmetatable(class, classmeta)
55         return class
56 end
57
58
59 -- Clones an object (deep on-demand)
60 function clone(object, deep)
61         local copy = {}
62         
63         for k, v in pairs(object) do
64                 if deep and type(v) == "table" then
65                         v = clone(v, deep)
66                 end
67                 copy[k] = v
68         end
69         
70         setmetatable(copy, getmetatable(object))
71         
72         return copy
73 end
74
75
76 -- Combines two or more numerically indexed tables into one
77 function combine(...)
78         local result = {}
79         for i, a in ipairs(arg) do
80                 for j, v in ipairs(a) do
81                         table.insert(result, v)
82                 end
83         end
84         return result
85 end
86
87
88 -- Checks whether a table has an object "value" in it
89 function contains(table, value)
90         for k,v in pairs(table) do
91                 if value == v then
92                         return true
93                 end
94         end
95         return false
96 end
97
98
99 -- Dumps and strips a Lua-Function
100 function dump(f)
101         local d = string.dump(f)
102         return d and strip_bytecode(d)
103 end
104
105
106 -- Dumps a table to stdout (useful for testing and debugging)
107 function dumptable(t, i)
108         i = i or 0
109         for k,v in pairs(t) do
110                 print(string.rep("\t", i) .. k, v)
111                 if type(v) == "table" then
112                         dumptable(v, i+1)
113                 end
114         end
115 end
116
117
118 -- Escapes all occurences of c in s
119 function escape(s, c)
120         c = c or "\\"
121         return s:gsub(c, "\\" .. c)
122 end
123
124
125 -- Populate obj in the scope of f as key 
126 function extfenv(f, key, obj)
127         local scope = getfenv(f)
128         scope[key] = obj
129 end
130
131
132 -- Checks whether an object is an instanceof class
133 function instanceof(object, class)
134         local meta = getmetatable(object)
135     while meta and meta.__index do 
136         if meta.__index == class then
137                 return true
138         end
139         meta = getmetatable(meta.__index)
140     end
141     return false        
142 end
143
144
145 -- Creates valid XML PCDATA from a string
146 function pcdata(value)
147         value = value:gsub("&", "&amp;")        
148         value = value:gsub('"', "&quot;")
149         value = value:gsub("'", "&apos;")
150         value = value:gsub("<", "&lt;") 
151         return value:gsub(">", "&gt;")
152 end
153
154
155 -- Resets the scope of f doing a shallow copy of its scope into a new table
156 function resfenv(f)
157         setfenv(f, clone(getfenv(f)))
158 end 
159
160
161 -- Splits a string into an array
162 function split(str, pat, max, regex)
163         pat = pat or "\n"
164         max = max or #str
165         
166         local t = {}
167         local c = 1
168         
169         if #str == 0 then
170                 return {""}
171         end
172         
173         if #pat == 0 then
174                 return nil
175         end
176         
177         if max == 0 then
178                 return str
179         end
180         
181         repeat
182                 local s, e = str:find(pat, c, not regex)
183                 table.insert(t, str:sub(c, s and s - 1))
184                 max = max - 1
185                 c = e and e + 1 or #str + 1
186         until not s or max < 0
187         
188         return t
189 end
190
191
192 -- Bytecode stripping function by Peter Cawley from http://lua-users.org/lists/lua-l/2008-02/msg01158.html
193 function strip_bytecode(dump)
194         local version, format, endian, int, size, ins, num = dump:byte(5, 11)
195         local subint
196         if endian == 1 then
197                 subint = function(dump, i, l)
198                         local val = 0
199                         for n = l, 1, -1 do
200                                 val = val * 256 + dump:byte(i + n - 1)
201                         end
202                         return val, i + l
203                 end
204         else
205         subint = function(dump, i, l)
206                 local val = 0
207             for n = 1, l, 1 do
208                 val = val * 256 + dump:byte(i + n - 1)
209             end
210             return val, i + l
211         end
212     end
213     
214         local strip_function
215         strip_function = function(dump)
216                 local count, offset = subint(dump, 1, size)
217                 local stripped, dirty = string.rep("\0", size), offset + count
218                 offset = offset + count + int * 2 + 4
219                 offset = offset + int + subint(dump, offset, int) * ins
220                 count, offset = subint(dump, offset, int)
221                 for n = 1, count do
222                         local t
223                         t, offset = subint(dump, offset, 1)
224                         if t == 1 then
225                                 offset = offset + 1
226                         elseif t == 4 then
227                                 offset = offset + size + subint(dump, offset, size)
228                         elseif t == 3 then
229                                 offset = offset + num
230                         end
231                 end
232                 count, offset = subint(dump, offset, int)
233                 stripped = stripped .. dump:sub(dirty, offset - 1)
234                 for n = 1, count do
235                         local proto, off = strip_function(dump:sub(offset, -1))
236                         stripped, offset = stripped .. proto, offset + off - 1
237                 end
238                 offset = offset + subint(dump, offset, int) * int + int
239                 count, offset = subint(dump, offset, int)
240                 for n = 1, count do
241                         offset = offset + subint(dump, offset, size) + size + int * 2
242                 end
243                 count, offset = subint(dump, offset, int)
244                 for n = 1, count do
245                         offset = offset + subint(dump, offset, size) + size
246                 end
247                 stripped = stripped .. string.rep("\0", int * 3)
248                 return stripped, offset
249         end
250         
251         return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
252 end
253
254
255 -- Removes whitespace from beginning and end of a string
256 function trim(str)
257         local s = str:gsub("^%s*(.-)%s*$", "%1")
258         return s
259 end
260
261
262 -- Updates given table with new values
263 function update(t, updates)
264         for k, v in pairs(updates) do
265                 t[k] = v
266         end     
267 end
268
269
270 -- Updates the scope of f with "extscope"
271 function updfenv(f, extscope)
272         update(getfenv(f), extscope)
273 end
274
275
276 -- Validates a variable
277 function validate(value, cast_number, cast_int)
278         if cast_number or cast_int then
279                 value = tonumber(value)
280         end
281         
282         if cast_int and value and not(value % 1 == 0) then
283                 value = nil
284         end
285         
286         return value
287 end