source: trunk/lua/util.lua @ 111

Revision 111, 6.4 KB checked in by reyalp, 16 months ago (diff)

split filesystem / path utilities into fsutil

  • Property svn:eol-style set to native
Line 
1--[[
2 Copyright (C) 2010-2011 <reyalp (at) gmail dot com>
3
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License version 2 as
6  published by the Free Software Foundation.
7
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
12
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16--]]
17--[[
18common generic lua utilities
19utilities that depend on the chdkptp api go in chdku
20]]
21local util={}
22
23--[[
24to allow overriding, e.g. for gui
25--]]
26util.util_stderr = io.stderr
27util.util_stdout = io.stdout
28
29function util.fprintf(f,...)
30        local args={...}
31        if #args == 0 or type(args[1]) ~= 'string' then
32                args[1]=''
33        end
34        f:write(string.format(unpack(args)))
35end
36
37function util.printf(...)
38        fprintf(util.util_stdout,...)
39end
40
41function util.warnf(format,...)
42        fprintf(util.util_stderr,"WARNING: " .. format,...)
43end
44
45function util.errf(format,...)
46        fprintf(util.util_stderr,"ERROR: " .. format,...)
47end
48
49util.extend_table_max_depth = 10
50local extend_table_r
51extend_table_r = function(target,source,seen,depth) 
52        if not seen then
53                seen = {}
54        end
55        if not depth then
56                depth = 1
57        end
58        if depth > util.extend_table_max_depth then
59                error('extend_table: max depth');
60        end
61        -- a source could have references to the target, don't want to do that
62        seen[target]=true
63        if seen[source] then
64                error('extend_table: cycles');
65        end
66        seen[source]=true
67        for k,v in pairs(source) do
68                if type(v) == 'table' then
69                        if type(target[k]) ~= 'table' then
70                                target[k] = {}
71                        end
72                        extend_table_r(target[k],v,seen,depth+1)
73                else
74                        target[k]=v
75                end
76        end
77        return target
78end
79
80--[[
81copy members of source into target
82by default, not deep so any tables will be copied as references
83returns target so you can do x=extend_table({},...)
84if deep, cycles result in an error
85deep does not copy keys which are themselves tables (the key will be a reference to the original key table)
86]]
87function util.extend_table(target,source,deep)
88        if type(target) ~= 'table' then
89                error('extend_table: target not table')
90        end
91        if source == nil then -- easier handling of default options
92                return target
93        end
94        if type(source) ~= 'table' then 
95                error('extend_table: source not table')
96        end
97        if source == target then
98                error('extend_table: source == target')
99        end
100        if deep then
101                return extend_table_r(target, source)
102        else 
103                for k,v in pairs(source) do
104                        target[k]=v
105                end
106                return target
107        end
108end
109
110--[[
111does table have value in it ?
112]]
113function util.in_table(table,value)
114        if table == nil then
115                return false
116        end
117        for k,v in pairs(table) do
118                if v == value then
119                        return true
120                end
121        end
122end
123
124--[[
125very simple meta-table inheritance
126]]
127function util.mt_inherit(t,base)
128        local mt={
129                __index=function(table, key)
130                        return base[key]
131                end
132        }
133        setmetatable(t,mt)
134        return t
135end
136
137function util.hexdump(str,offset)
138        local c, result, byte
139        if not offset then
140                offset = 0
141        end
142        c = 0
143        result = ''
144        for i=1,#str do
145                if c == 0 then
146                        result = result .. string.format("%8x: ",offset)
147                end
148                result = result .. string.format("%02x ",string.byte(str,i))
149                c = c + 1
150                if c == 16 then
151                        c = 0
152                        offset = offset + 16
153                        result = result .. "| " .. string.gsub(string.sub(str,i-15,i),'[%c%z%s\128-\255]','.') .. '\n'
154                end
155        end
156        if c ~= 0 then
157                for i=1,16-c do
158                        result = result .. "-- "
159                end
160                result = result .. "| " .. string.gsub(string.sub(str,-c),'[%c%z%s\128-\255]','.')
161        end
162        return result
163end
164
165local serialize_r
166serialize_r = function(v,opts,seen,depth)
167        local vt = type(v)
168        if vt == 'nil' or  vt == 'boolean' then
169                return tostring(v)
170        elseif vt == 'number' then
171                -- camera has problems with decimal constants that would be negative
172                if opts.fix_bignum and v > 0x7FFFFFFF then
173                        return string.format("0x%x",v)
174                end
175                return tostring(v)
176        elseif vt == 'string' then
177                return string.format('%q',v)
178        elseif vt == 'table' then
179                if not depth then
180                        depth = 1
181                end
182                if depth >= opts.maxdepth then
183                        error('serialize: max depth')
184                end
185                if not seen then
186                        seen={}
187                elseif seen[v] then 
188                        if opts.err_cycle then
189                                error('serialize: cycle')
190                        else
191                                return '"cycle:'..tostring(v)..'"'
192                        end
193                end
194                seen[v] = true;
195                local r='{'
196                for k,v1 in pairs(v) do
197                        if opts.pretty then
198                                r = r .. '\n' ..  string.rep(' ',depth)
199                        end
200                        -- more compact/friendly format simple string keys
201                        -- TODO we could make integers more compact by doing array part first
202                        if type(k) == 'string' and string.match(k,'^[_%a][%a%d_]*$') then
203                                r = r .. tostring(k)
204                        else
205                                r = r .. '[' .. serialize_r(k,opts,seen,depth+1) .. ']'
206                        end
207                        r = r .. '=' .. serialize_r(v1,opts,seen,depth+1) .. ','
208                end
209                if opts.pretty then
210                        r = r .. '\n' .. string.rep(' ',depth-1)
211                end
212                r = r .. '}'
213                return r
214        elseif opts.err_type then
215                error('serialize: unsupported type ' .. vt, 2)
216        else
217                return '"'..tostring(v)..'"'
218        end
219end
220
221util.serialize_defaults = {
222        -- maximum nested depth
223        maxdepth=10,
224        -- ignore or error on various conditions
225        err_type=true, -- bad type, e.g. function, userdata
226        err_cycle=true, -- cyclic references
227        pretty=true, -- indents and newlines
228        fix_bignum=true, -- send values > 2^31 as hex, to avoid problems with camera conversion from decimal
229--      forceint=false, -- TODO convert numbers to integer
230}
231
232--[[
233serialize lua values
234options as documented above
235]]
236function util.serialize(v,opts)
237        return serialize_r(v,util.extend_table(util.extend_table({},util.serialize_defaults),opts))
238end
239
240--[[
241turn string back into lua data by executing it and returning the value
242the value is sandboxed in an empty function environment
243returns the resulting value, or false + an error message on failure
244check the message, since the serialized value might be false or nil!
245]]
246function util.unserialize(s)
247        local f,err=loadstring('return ' .. s)
248        if not f then
249                return false, err
250        end
251        setfenv(f,{}) -- empty fenv
252        local status,r=pcall(f)
253        if status then
254                return r
255        end
256        return false,r
257end
258
259--[[
260hacky hacky
261"import" values from a table into globals
262]]
263function util.import(t,names)
264        if names == nil then
265                for name,val in pairs(t) do
266                        _G[name] = val
267                end
268                return
269        end
270        for i,name in ipairs(names) do
271                if t[name] ~= nil then
272                        _G[name] = t[name]
273                end
274        end
275end
276return util
Note: See TracBrowser for help on using the repository browser.