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