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