40aa8843c7e46dd493f54e4ac143152d6f30cc6b
[prosody.git] / util / ztact.lua
1 -- Prosody IM
2 -- This file is included with Prosody IM. It has modifications,
3 -- which are hereby placed in the public domain.
4
5 -- public domain 20080410 lua@ztact.com
6
7
8 pcall (require, 'lfs')      -- lfs may not be installed/necessary.
9 pcall (require, 'pozix')    -- pozix may not be installed/necessary.
10
11
12 local getfenv, ipairs, next, pairs, pcall, require, select, tostring, type =
13       getfenv, ipairs, next, pairs, pcall, require, select, tostring, type
14 local unpack, xpcall =
15       unpack, xpcall
16
17 local io, lfs, os, string, table, pozix = io, lfs, os, string, table, pozix
18
19 local assert, print = assert, print
20
21 local error             = error
22
23
24 module ((...) or 'ztact')    ------------------------------------- module ztact
25
26
27 -- dir -------------------------------------------------------------------- dir
28
29
30 function dir (path)    -- - - - - - - - - - - - - - - - - - - - - - - - - - dir
31   local it = lfs.dir (path)
32   return function ()
33     repeat
34       local dir = it ()
35       if dir ~= '.' and dir ~= '..' then  return dir  end
36     until not dir
37     end  end
38
39
40 function is_file (path)    -- - - - - - - - - - - - - - - - - -  is_file (path)
41   local mode = lfs.attributes (path, 'mode')
42   return mode == 'file' and path
43   end
44
45
46 -- network byte ordering -------------------------------- network byte ordering
47
48
49 function htons (word)    -- - - - - - - - - - - - - - - - - - - - - - - - htons
50   return (word-word%0x100)/0x100, word%0x100
51   end
52
53
54 -- pcall2 -------------------------------------------------------------- pcall2
55
56
57 getfenv ().pcall = pcall    -- store the original pcall as ztact.pcall
58
59
60 local argc, argv, errorhandler, pcall2_f
61
62
63 local function _pcall2 ()    -- - - - - - - - - - - - - - - - - - - - - _pcall2
64   local tmpv = argv
65   argv = nil
66   return pcall2_f (unpack (tmpv, 1, argc))
67   end
68
69
70 function seterrorhandler (func)    -- - - - - - - - - - - - - - seterrorhandler
71   errorhandler = func
72   end
73
74
75 function pcall2 (f, ...)    -- - - - - - - - - - - - - - - - - - - - - - pcall2
76
77   pcall2_f = f
78   argc = select ('#', ...)
79   argv = { ... }
80
81   if not errorhandler then
82     local debug = require ('debug')
83     errorhandler = debug.traceback
84     end
85
86   return xpcall (_pcall2, errorhandler)
87   end
88
89
90 function append (t, ...)    -- - - - - - - - - - - - - - - - - - - - - - append
91   local insert = table.insert
92   for i,v in ipairs {...} do
93     insert (t, v)
94     end  end
95
96
97 function print_r (d, indent)    -- - - - - - - - - - - - - - - - - - -  print_r
98   local rep = string.rep ('  ', indent or 0)
99   if type (d) == 'table' then
100     for k,v in pairs (d) do
101       if type (v) == 'table' then
102         io.write (rep, k, '\n')
103         print_r (v, (indent or 0) + 1)
104       else  io.write (rep, k, ' = ', tostring (v), '\n')  end
105       end
106   else  io.write (d, '\n')  end
107   end
108
109
110 function tohex (s)    -- - - - - - - - - - - - - - - - - - - - - - - - -  tohex
111   return string.format (string.rep ('%02x ', #s), string.byte (s, 1, #s))
112   end
113
114
115 function tostring_r (d, indent, tab0)    -- - - - - - - - - - - - -  tostring_r
116
117   local tab1 = tab0 or {}
118   local rep = string.rep ('  ', indent or 0)
119   if type (d) == 'table' then
120     for k,v in pairs (d) do
121       if type (v) == 'table' then
122         append (tab1, rep, k, '\n')
123         tostring_r (v, (indent or 0) + 1, tab1)
124       else  append (tab1, rep, k, ' = ', tostring (v), '\n')  end
125       end
126   else  append (tab1, d, '\n')  end
127
128   if not tab0 then  return table.concat (tab1)  end
129   end
130
131
132 -- queue manipulation -------------------------------------- queue manipulation
133
134
135 -- Possible queue states.  1 (i.e. queue.p[1]) is head of queue.
136 --
137 -- 1..2
138 -- 3..4  1..2
139 -- 3..4  1..2  5..6
140 -- 1..2        5..6
141 --             1..2
142
143
144 local function print_queue (queue, ...)    -- - - - - - - - - - - - print_queue
145   for i=1,10 do  io.write ((queue[i]   or '.')..' ')  end
146   io.write ('\t')
147   for i=1,6  do  io.write ((queue.p[i] or '.')..' ')  end
148   print (...)
149   end
150
151
152 function dequeue (queue)    -- - - - - - - - - - - - - - - - - - - - -  dequeue
153
154   local p = queue.p
155   if not p and queue[1] then  queue.p = { 1, #queue }  p = queue.p  end
156
157   if not p[1] then  return nil  end
158
159   local element = queue[p[1]]
160   queue[p[1]] = nil
161
162   if p[1] < p[2] then  p[1] = p[1] + 1
163
164   elseif p[4] then  p[1], p[2], p[3], p[4]  =  p[3], p[4], nil, nil
165
166   elseif p[5] then  p[1], p[2], p[5], p[6]  =  p[5], p[6], nil, nil
167
168   else  p[1], p[2]  =  nil, nil  end
169
170   print_queue (queue, '  de '..element)
171   return element
172   end
173
174
175 function enqueue (queue, element)    -- - - - - - - - - - - - - - - - - enqueue
176
177   local p = queue.p
178   if not p then  queue.p = {}  p = queue.p  end
179
180   if p[5] then    -- p3..p4 p1..p2 p5..p6
181     p[6] = p[6]+1
182     queue[p[6]] = element
183
184   elseif p[3] then    -- p3..p4 p1..p2
185
186     if p[4]+1 < p[1] then
187       p[4] = p[4] + 1
188       queue[p[4]] = element
189
190     else
191       p[5] = p[2]+1
192       p[6], queue[p[5]] = p[5], element
193       end
194
195   elseif p[1] then    -- p1..p2
196     if p[1] == 1 then
197       p[2] = p[2] + 1
198       queue[p[2]] = element
199
200     else
201         p[3], p[4], queue[1] = 1, 1, element
202         end
203
204   else    -- empty queue
205     p[1], p[2], queue[1] = 1, 1, element
206     end
207
208   print_queue (queue, '     '..element)
209   end
210
211
212 local function test_queue ()
213   local t = {}
214   enqueue (t, 1)
215   enqueue (t, 2)
216   enqueue (t, 3)
217   enqueue (t, 4)
218   enqueue (t, 5)
219   dequeue (t)
220   dequeue (t)
221   enqueue (t, 6)
222   enqueue (t, 7)
223   enqueue (t, 8)
224   enqueue (t, 9)
225   dequeue (t)
226   dequeue (t)
227   dequeue (t)
228   dequeue (t)
229   enqueue (t, 'a')
230   dequeue (t)
231   enqueue (t, 'b')
232   enqueue (t, 'c')
233   dequeue (t)
234   dequeue (t)
235   dequeue (t)
236   dequeue (t)
237   dequeue (t)
238   enqueue (t, 'd')
239   dequeue (t)
240   dequeue (t)
241   dequeue (t)
242   end
243
244
245 -- test_queue ()
246
247
248 function queue_len (queue)
249   end
250
251
252 function queue_peek (queue)
253   end
254
255
256 -- tree manipulation ---------------------------------------- tree manipulation
257
258
259 function set (parent, ...)    --- - - - - - - - - - - - - - - - - - - - - - set
260
261   -- print ('set', ...)
262
263   local len = select ('#', ...)
264   local key, value = select (len-1, ...)
265   local cutpoint, cutkey
266
267   for i=1,len-2 do
268
269     local key = select (i, ...)
270     local child = parent[key]
271
272     if value == nil then
273       if child == nil then  return
274       elseif next (child, next (child)) then  cutpoint = nil  cutkey = nil
275       elseif cutpoint == nil then  cutpoint = parent  cutkey = key  end
276
277     elseif child == nil then  child = {}  parent[key] = child  end
278
279     parent = child
280     end
281
282   if value == nil and cutpoint then  cutpoint[cutkey] = nil
283   else  parent[key] = value  return value  end
284   end
285
286
287 function get (parent, ...)    --- - - - - - - - - - - - - - - - - - - - - - get
288   local len = select ('#', ...)
289   for i=1,len do
290     parent = parent[select (i, ...)]
291     if parent == nil then  break  end
292     end
293   return parent
294   end
295
296
297 -- misc ------------------------------------------------------------------ misc
298
299
300 function find (path, ...)    --------------------------------------------- find
301
302   local dirs, operators = { path }, {...}
303   for operator in ivalues (operators) do
304     if not operator (path) then  break  end  end
305
306   while next (dirs) do
307     local parent = table.remove (dirs)
308     for child in assert (pozix.opendir (parent)) do
309       if  child  and  child ~= '.'  and  child ~= '..'  then
310         local path = parent..'/'..child
311         if pozix.stat (path, 'is_dir') then  table.insert (dirs, path)  end
312         for operator in ivalues (operators) do
313           if not operator (path) then  break  end  end
314         end  end  end  end
315
316
317 function ivalues (t)    ----------------------------------------------- ivalues
318   local i = 0
319   return function ()  if t[i+1] then  i = i + 1  return t[i]  end  end
320   end
321
322
323 function lson_encode (mixed, f, indent, indents)    --------------- lson_encode
324
325
326   local capture
327   if not f then
328     capture = {}
329     f = function (s)  append (capture, s)  end
330     end
331
332   indent = indent or 0
333   indents = indents or {}
334   indents[indent] = indents[indent] or string.rep (' ', 2*indent)
335
336   local type = type (mixed)
337
338   if type == 'number' then f (mixed)
339
340   else if type == 'string' then f (string.format ('%q', mixed))
341
342   else if type == 'table' then
343     f ('{')
344     for k,v in pairs (mixed) do
345       f ('\n')
346       f (indents[indent])
347       f ('[')  f (lson_encode (k))  f ('] = ')
348       lson_encode (v, f, indent+1, indents)
349       f (',')
350       end
351     f (' }')
352     end  end  end
353
354   if capture then  return table.concat (capture)  end
355   end
356
357
358 function timestamp (time)    ---------------------------------------- timestamp
359   return os.date ('%Y%m%d.%H%M%S', time)
360   end
361
362
363 function values (t)    ------------------------------------------------- values
364   local k, v
365   return function ()  k, v = next (t, k)  return v  end
366   end