e227fc7306aad92cf1d75ed7d3d914fc490a194b
[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 = copcall(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) .. tostring(k), tostring(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 -- Returns an error message to stdout
156 function perror(obj)
157         io.stderr:write(tostring(obj) .. "\n")
158 end
159
160
161 -- Resets the scope of f doing a shallow copy of its scope into a new table
162 function resfenv(f)
163         setfenv(f, clone(getfenv(f)))
164 end 
165
166
167 -- Splits a string into an array
168 function split(str, pat, max, regex)
169         pat = pat or "\n"
170         max = max or #str
171         
172         local t = {}
173         local c = 1
174         
175         if #str == 0 then
176                 return {""}
177         end
178         
179         if #pat == 0 then
180                 return nil
181         end
182         
183         if max == 0 then
184                 return str
185         end
186         
187         repeat
188                 local s, e = str:find(pat, c, not regex)
189                 table.insert(t, str:sub(c, s and s - 1))
190                 max = max - 1
191                 c = e and e + 1 or #str + 1
192         until not s or max < 0
193         
194         return t
195 end
196
197
198 -- Strips lua bytecode
199 -- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
200 function strip_bytecode(dump)
201         local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
202         local subint
203         if endian == 1 then
204                 subint = function(dump, i, l)
205                         local val = 0
206                         for n = l, 1, -1 do
207                                 val = val * 256 + dump:byte(i + n - 1)
208                         end
209                         return val, i + l
210                 end
211         else
212                 subint = function(dump, i, l)
213                         local val = 0
214                         for n = 1, l, 1 do
215                                 val = val * 256 + dump:byte(i + n - 1)
216                         end
217                         return val, i + l
218                 end
219         end
220     
221         local strip_function
222         strip_function = function(dump)
223                 local count, offset = subint(dump, 1, size)
224                 local stripped, dirty = string.rep("\0", size), offset + count
225                 offset = offset + count + int * 2 + 4
226                 offset = offset + int + subint(dump, offset, int) * ins
227                 count, offset = subint(dump, offset, int)
228                 for n = 1, count do
229                         local t
230                         t, offset = subint(dump, offset, 1)
231                         if t == 1 then
232                                 offset = offset + 1
233                         elseif t == 4 then
234                                 offset = offset + size + subint(dump, offset, size)
235                         elseif t == 3 then
236                                 offset = offset + num
237                         elseif t == 254 then
238                                 offset = offset + lnum
239                         end
240                 end
241                 count, offset = subint(dump, offset, int)
242                 stripped = stripped .. dump:sub(dirty, offset - 1)
243                 for n = 1, count do
244                         local proto, off = strip_function(dump:sub(offset, -1))
245                         stripped, offset = stripped .. proto, offset + off - 1
246                 end
247                 offset = offset + subint(dump, offset, int) * int + int
248                 count, offset = subint(dump, offset, int)
249                 for n = 1, count do
250                         offset = offset + subint(dump, offset, size) + size + int * 2
251                 end
252                 count, offset = subint(dump, offset, int)
253                 for n = 1, count do
254                         offset = offset + subint(dump, offset, size) + size
255                 end
256                 stripped = stripped .. string.rep("\0", int * 3)
257                 return stripped, offset
258         end
259         
260         return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
261 end
262
263
264 -- Creates a new threadlocal store
265 function threadlocal()
266         local tbl = {}
267         
268         local function get(self, key)
269                 local c = coroutine.running()
270                 local thread = coxpt[c] or c or 0
271                 if not rawget(self, thread) then
272                         return nil
273                 end
274                 return rawget(self, thread)[key]
275         end
276                 
277         local function set(self, key, value)
278                 local c = coroutine.running()
279                 local thread = coxpt[c] or c or 0
280                 if not rawget(self, thread) then
281                         rawset(self, thread, {})
282                 end
283                 rawget(self, thread)[key] = value
284         end
285         
286         setmetatable(tbl, {__index = get, __newindex = set, __mode = "k"})
287         
288         return tbl
289 end
290
291
292 -- Removes whitespace from beginning and end of a string
293 function trim(str)
294         local s = str:gsub("^%s*(.-)%s*$", "%1")
295         return s
296 end
297
298
299 -- Updates given table with new values
300 function update(t, updates)
301         for k, v in pairs(updates) do
302                 t[k] = v
303         end     
304 end
305
306
307 -- Updates the scope of f with "extscope"
308 function updfenv(f, extscope)
309         update(getfenv(f), extscope)
310 end
311
312
313 -- Parse units from a string and return integer value
314 function parse_units(ustr)
315
316         local val = 0
317
318         -- unit map
319         local map = {
320                 -- date stuff
321                 y   = 60 * 60 * 24 * 366,
322                 m   = 60 * 60 * 24 * 31,
323                 w   = 60 * 60 * 24 * 7,
324                 d   = 60 * 60 * 24,
325                 h   = 60 * 60,
326                 min = 60,
327
328                 -- storage sizes
329                 kb  = 1024,
330                 mb  = 1024 * 1024,
331                 gb  = 1024 * 1024 * 1024,
332
333                 -- storage sizes (si)
334                 kib = 1000,
335                 mib = 1000 * 1000,
336                 gib = 1000 * 1000 * 1000
337         }
338
339         -- parse input string
340         for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
341
342                 local num = spec:gsub("[^0-9%.]+$","")
343                 local spn = spec:gsub("^[0-9%.]+", "")
344
345                 if map[spn] or map[spn:sub(1,1)] then
346                         val = val + num * ( map[spn] or map[spn:sub(1,1)] )
347                 else
348                         val = val + num
349                 end
350         end
351
352
353         return val
354 end
355
356
357 -- Provide various sorting iterators
358 function _sortiter( t, f )
359         local keys = { }
360
361         for k, v in pairs(t) do
362                 table.insert( keys, k )
363         end
364
365         local _pos = 0
366         local _len = table.getn( keys )
367
368         table.sort( keys, f )
369
370         return function()
371                 _pos = _pos + 1
372                 if _pos <= _len then
373                         return keys[_pos], t[keys[_pos]]
374                 end
375         end
376 end
377
378 -- Return key, value pairs sorted by provided callback function
379 function spairs(t,f)
380         return _sortiter( t, f )                                        
381 end
382
383 -- Return key, value pairs sorted by keys
384 function kspairs(t)
385         return _sortiter( t )
386 end
387
388 -- Return key, value pairs sorted by values
389 function vspairs(t)
390         return _sortiter( t, function (a,b) return t[a] < t[b] end )
391 end
392
393
394 -- Coroutine safe xpcall and pcall versions modified for Luci
395 -- original version:
396 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
397 local performResume, handleReturnValue
398 local oldpcall, oldxpcall = pcall, xpcall
399 coxpt = {}
400 setmetatable(coxpt, {__mode = "kv"})
401
402 function handleReturnValue(err, co, status, ...)
403     if not status then
404         return false, err(debug.traceback(co, (...)), ...)
405     end
406     if coroutine.status(co) == 'suspended' then
407         return performResume(err, co, coroutine.yield(...))
408     else
409         return true, ...
410     end
411 end
412
413 function performResume(err, co, ...)
414     return handleReturnValue(err, co, coroutine.resume(co, ...))
415 end    
416
417 function coxpcall(f, err, ...)
418     local res, co = oldpcall(coroutine.create, f)
419     if not res then
420         local params = {...}
421         local newf = function() return f(unpack(params)) end
422         co = coroutine.create(newf)
423     end
424     local c = coroutine.running()
425     coxpt[co] = coxpt[c] or c or 0
426     
427     return performResume(err, co, ...)
428 end
429
430 local function id(trace, ...)
431   return ...
432 end
433
434 function copcall(f, ...)
435     return coxpcall(f, id, ...)
436 end