51685605721767ffa1fd4f362a7067551c446642
[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
285 -- Avoid memory leaks by removing abandoned stores
286 for k, v in pairs(self) do
287 if type(k) == "thread" and coroutine.status(k) == "dead" then
288 rawset(self, k, nil)
289 end
290 end
291 end
292
293 setmetatable(tbl, {__index = get, __newindex = set})
294
295 return tbl
296 end
297
298
299 -- Removes whitespace from beginning and end of a string
300 function trim(str)
301 local s = str:gsub("^%s*(.-)%s*$", "%1")
302 return s
303 end
304
305
306 -- Updates given table with new values
307 function update(t, updates)
308 for k, v in pairs(updates) do
309 t[k] = v
310 end
311 end
312
313
314 -- Updates the scope of f with "extscope"
315 function updfenv(f, extscope)
316 update(getfenv(f), extscope)
317 end
318
319
320 -- Parse units from a string and return integer value
321 function parse_units(ustr)
322
323 local val = 0
324
325 -- unit map
326 local map = {
327 -- date stuff
328 y = 60 * 60 * 24 * 366,
329 m = 60 * 60 * 24 * 31,
330 w = 60 * 60 * 24 * 7,
331 d = 60 * 60 * 24,
332 h = 60 * 60,
333 min = 60,
334
335 -- storage sizes
336 kb = 1024,
337 mb = 1024 * 1024,
338 gb = 1024 * 1024 * 1024,
339
340 -- storage sizes (si)
341 kib = 1000,
342 mib = 1000 * 1000,
343 gib = 1000 * 1000 * 1000
344 }
345
346 -- parse input string
347 for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
348
349 local num = spec:gsub("[^0-9%.]+$","")
350 local spn = spec:gsub("^[0-9%.]+", "")
351
352 if map[spn] or map[spn:sub(1,1)] then
353 val = val + num * ( map[spn] or map[spn:sub(1,1)] )
354 else
355 val = val + num
356 end
357 end
358
359
360 return val
361 end
362
363
364 -- Provide various sorting iterators
365 function _sortiter( t, f )
366 local keys = { }
367
368 for k, v in pairs(t) do
369 table.insert( keys, k )
370 end
371
372 local _pos = 0
373 local _len = table.getn( keys )
374
375 table.sort( keys, f )
376
377 return function()
378 _pos = _pos + 1
379 if _pos <= _len then
380 return keys[_pos], t[keys[_pos]]
381 end
382 end
383 end
384
385 -- Return key, value pairs sorted by provided callback function
386 function spairs(t,f)
387 return _sortiter( t, f )
388 end
389
390 -- Return key, value pairs sorted by keys
391 function kspairs(t)
392 return _sortiter( t )
393 end
394
395 -- Return key, value pairs sorted by values
396 function vspairs(t)
397 return _sortiter( t, function (a,b) return t[a] < t[b] end )
398 end
399
400
401 -- Coroutine safe xpcall and pcall versions modified for Luci
402 -- original version:
403 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
404 local performResume, handleReturnValue
405 local oldpcall, oldxpcall = pcall, xpcall
406 coxpt = {}
407
408 function handleReturnValue(err, co, status, ...)
409 if not status then
410 return false, err(debug.traceback(co, (...)), ...)
411 end
412 if coroutine.status(co) == 'suspended' then
413 return performResume(err, co, coroutine.yield(...))
414 else
415 return true, ...
416 end
417 end
418
419 function performResume(err, co, ...)
420 return handleReturnValue(err, co, coroutine.resume(co, ...))
421 end
422
423 function coxpcall(f, err, ...)
424 local res, co = oldpcall(coroutine.create, f)
425 if not res then
426 local params = {...}
427 local newf = function() return f(unpack(params)) end
428 co = coroutine.create(newf)
429 end
430 local c = coroutine.running()
431 coxpt[co] = coxpt[c] or c or 0
432 return performResume(err, co, ...)
433 end
434
435 local function id(trace, ...)
436 return ...
437 end
438
439 function copcall(f, ...)
440 return coxpcall(f, id, ...)
441 end