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