074b43519a88d0f88d76adc5a16d9c8f1619609b
[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) .. k, 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 -- Validates a variable
280 function validate(value, cast_number, cast_int)
281 if cast_number or cast_int then
282 value = tonumber(value)
283 end
284
285 if cast_int and value and not(value % 1 == 0) then
286 value = nil
287 end
288
289 return value
290 end
291
292
293 -- Parse units from a string and return integer value
294 function parse_units(ustr)
295
296 local val = 0
297
298 -- unit map
299 local map = {
300 -- date stuff
301 y = 60 * 60 * 24 * 366,
302 m = 60 * 60 * 24 * 31,
303 w = 60 * 60 * 24 * 7,
304 d = 60 * 60 * 24,
305 h = 60 * 60,
306 min = 60,
307
308 -- storage sizes
309 kb = 1024,
310 mb = 1024 * 1024,
311 gb = 1024 * 1024 * 1024,
312
313 -- storage sizes (si)
314 kib = 1000,
315 mib = 1000 * 1000,
316 gib = 1000 * 1000 * 1000
317 }
318
319 -- parse input string
320 for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
321
322 local num = spec:gsub("[^0-9%.]+$","")
323 local spn = spec:gsub("^[0-9%.]+", "")
324
325 if map[spn] or map[spn:sub(1,1)] then
326 val = val + num * ( map[spn] or map[spn:sub(1,1)] )
327 else
328 val = val + num
329 end
330 end
331
332
333 return val
334 end