ad53138e60ffeaf451fda2608b16b24ad5d85079
[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 = pcall(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 -- Resets the scope of f doing a shallow copy of its scope into a new table
156 function resfenv(f)
157         setfenv(f, clone(getfenv(f)))
158 end 
159
160
161 -- Splits a string into an array
162 function split(str, pat, max, regex)
163         pat = pat or "\n"
164         max = max or #str
165         
166         local t = {}
167         local c = 1
168         
169         if #str == 0 then
170                 return {""}
171         end
172         
173         if #pat == 0 then
174                 return nil
175         end
176         
177         if max == 0 then
178                 return str
179         end
180         
181         repeat
182                 local s, e = str:find(pat, c, not regex)
183                 table.insert(t, str:sub(c, s and s - 1))
184                 max = max - 1
185                 c = e and e + 1 or #str + 1
186         until not s or max < 0
187         
188         return t
189 end
190
191
192 -- Strips lua bytecode
193 -- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
194 function strip_bytecode(dump)
195         local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
196         local subint
197         if endian == 1 then
198                 subint = function(dump, i, l)
199                         local val = 0
200                         for n = l, 1, -1 do
201                                 val = val * 256 + dump:byte(i + n - 1)
202                         end
203                         return val, i + l
204                 end
205         else
206                 subint = function(dump, i, l)
207                         local val = 0
208                         for n = 1, l, 1 do
209                                 val = val * 256 + dump:byte(i + n - 1)
210                         end
211                         return val, i + l
212                 end
213         end
214     
215         local strip_function
216         strip_function = function(dump)
217                 local count, offset = subint(dump, 1, size)
218                 local stripped, dirty = string.rep("\0", size), offset + count
219                 offset = offset + count + int * 2 + 4
220                 offset = offset + int + subint(dump, offset, int) * ins
221                 count, offset = subint(dump, offset, int)
222                 for n = 1, count do
223                         local t
224                         t, offset = subint(dump, offset, 1)
225                         if t == 1 then
226                                 offset = offset + 1
227                         elseif t == 4 then
228                                 offset = offset + size + subint(dump, offset, size)
229                         elseif t == 3 then
230                                 offset = offset + num
231                         elseif t == 254 then
232                                 offset = offset + lnum
233                         end
234                 end
235                 count, offset = subint(dump, offset, int)
236                 stripped = stripped .. dump:sub(dirty, offset - 1)
237                 for n = 1, count do
238                         local proto, off = strip_function(dump:sub(offset, -1))
239                         stripped, offset = stripped .. proto, offset + off - 1
240                 end
241                 offset = offset + subint(dump, offset, int) * int + int
242                 count, offset = subint(dump, offset, int)
243                 for n = 1, count do
244                         offset = offset + subint(dump, offset, size) + size + int * 2
245                 end
246                 count, offset = subint(dump, offset, int)
247                 for n = 1, count do
248                         offset = offset + subint(dump, offset, size) + size
249                 end
250                 stripped = stripped .. string.rep("\0", int * 3)
251                 return stripped, offset
252         end
253         
254         return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
255 end
256
257
258 -- Removes whitespace from beginning and end of a string
259 function trim(str)
260         local s = str:gsub("^%s*(.-)%s*$", "%1")
261         return s
262 end
263
264
265 -- Updates given table with new values
266 function update(t, updates)
267         for k, v in pairs(updates) do
268                 t[k] = v
269         end     
270 end
271
272
273 -- Updates the scope of f with "extscope"
274 function updfenv(f, extscope)
275         update(getfenv(f), extscope)
276 end
277
278
279 -- Parse units from a string and return integer value
280 function parse_units(ustr)
281
282         local val = 0
283
284         -- unit map
285         local map = {
286                 -- date stuff
287                 y   = 60 * 60 * 24 * 366,
288                 m   = 60 * 60 * 24 * 31,
289                 w   = 60 * 60 * 24 * 7,
290                 d   = 60 * 60 * 24,
291                 h   = 60 * 60,
292                 min = 60,
293
294                 -- storage sizes
295                 kb  = 1024,
296                 mb  = 1024 * 1024,
297                 gb  = 1024 * 1024 * 1024,
298
299                 -- storage sizes (si)
300                 kib = 1000,
301                 mib = 1000 * 1000,
302                 gib = 1000 * 1000 * 1000
303         }
304
305         -- parse input string
306         for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
307
308                 local num = spec:gsub("[^0-9%.]+$","")
309                 local spn = spec:gsub("^[0-9%.]+", "")
310
311                 if map[spn] or map[spn:sub(1,1)] then
312                         val = val + num * ( map[spn] or map[spn:sub(1,1)] )
313                 else
314                         val = val + num
315                 end
316         end
317
318
319         return val
320 end
321
322
323 -- Provide various sorting iterators
324 function _sortiter( t, f )
325         local keys = { }
326
327         for k, v in pairs(t) do
328                 table.insert( keys, k )
329         end
330
331         local _pos = 0
332         local _len = table.getn( keys )
333
334         table.sort( keys, f )
335
336         return function()
337                 _pos = _pos + 1
338                 if _pos <= _len then
339                         return keys[_pos], t[keys[_pos]]
340                 end
341         end
342 end
343
344 -- Return key, value pairs sorted by provided callback function
345 function spairs(t,f)
346         return _sortiter( t, f )                                        
347 end
348
349 -- Return key, value pairs sorted by keys
350 function kspairs(t)
351         return _sortiter( t )
352 end
353
354 -- Return key, value pairs sorted by values
355 function vspairs(t)
356         return _sortiter( t, function (a,b) return t[a] < t[b] end )
357 end