Merge pull request #278 from nmav/ocserv
[project/luci.git] / modules / base / luasrc / ltn12.lua
1 --[[
2 LuaSocket 2.0.2 license
3 Copyright � 2004-2007 Diego Nehab
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 ]]--
23 --[[
24         Changes made by LuCI project:
25                 * Renamed to luci.ltn12 to avoid collisions with luasocket
26                 * Added inline documentation
27 ]]--
28 -----------------------------------------------------------------------------
29 -- LTN12 - Filters, sources, sinks and pumps.
30 -- LuaSocket toolkit.
31 -- Author: Diego Nehab
32 -- RCS ID: $Id$
33 -----------------------------------------------------------------------------
34
35 -----------------------------------------------------------------------------
36 -- Declare module
37 -----------------------------------------------------------------------------
38 local string = require("string")
39 local table = require("table")
40 local base = _G
41
42 --- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
43 -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
44 module("luci.ltn12")
45
46 filter = {}
47 source = {}
48 sink = {}
49 pump = {}
50
51 -- 2048 seems to be better in windows...
52 BLOCKSIZE = 2048
53 _VERSION = "LTN12 1.0.1"
54
55 -----------------------------------------------------------------------------
56 -- Filter stuff
57 -----------------------------------------------------------------------------
58
59 --- LTN12 Filter constructors
60 -- @class module
61 -- @name luci.ltn12.filter
62
63 --- Return a high level filter that cycles a low-level filter
64 -- by passing it each chunk and updating a context between calls. 
65 -- @param low   Low-level filter
66 -- @param ctx   Context
67 -- @param extra Extra argument passed to the low-level filter
68 -- @return LTN12 filter
69 function filter.cycle(low, ctx, extra)
70     base.assert(low)
71     return function(chunk)
72         local ret
73         ret, ctx = low(ctx, chunk, extra)
74         return ret
75     end
76 end
77
78 --- Chain a bunch of filters together.
79 -- (thanks to Wim Couwenberg)
80 -- @param ... filters to be chained
81 -- @return LTN12 filter
82 function filter.chain(...)
83     local n = table.getn(arg)
84     local top, index = 1, 1
85     local retry = ""
86     return function(chunk)
87         retry = chunk and retry
88         while true do
89             if index == top then
90                 chunk = arg[index](chunk)
91                 if chunk == "" or top == n then return chunk
92                 elseif chunk then index = index + 1
93                 else
94                     top = top+1
95                     index = top
96                 end
97             else
98                 chunk = arg[index](chunk or "")
99                 if chunk == "" then
100                     index = index - 1
101                     chunk = retry
102                 elseif chunk then
103                     if index == n then return chunk
104                     else index = index + 1 end
105                 else base.error("filter returned inappropriate nil") end
106             end
107         end
108     end
109 end
110
111 -----------------------------------------------------------------------------
112 -- Source stuff
113 -----------------------------------------------------------------------------
114
115 --- LTN12 Source constructors
116 -- @class module
117 -- @name luci.ltn12.source
118
119 -- create an empty source
120 local function empty()
121     return nil
122 end
123
124 --- Create an empty source.
125 -- @return LTN12 source
126 function source.empty()
127     return empty
128 end
129
130 --- Return a source that just outputs an error.
131 -- @param err Error object
132 -- @return LTN12 source
133 function source.error(err)
134     return function()
135         return nil, err
136     end
137 end
138
139 --- Create a file source.
140 -- @param handle File handle ready for reading
141 -- @param io_err IO error object
142 -- @return LTN12 source
143 function source.file(handle, io_err)
144     if handle then
145         return function()
146             local chunk = handle:read(BLOCKSIZE)
147             if chunk and chunk:len() == 0 then chunk = nil end
148             if not chunk then handle:close() end
149             return chunk
150         end
151     else return source.error(io_err or "unable to open file") end
152 end
153
154 --- Turn a fancy source into a simple source.
155 -- @param src fancy source
156 -- @return LTN12 source
157 function source.simplify(src)
158     base.assert(src)
159     return function()
160         local chunk, err_or_new = src()
161         src = err_or_new or src
162         if not chunk then return nil, err_or_new
163         else return chunk end
164     end
165 end
166
167 --- Create a string source.
168 -- @param s Data
169 -- @return LTN12 source
170 function source.string(s)
171     if s then
172         local i = 1
173         return function()
174             local chunk = string.sub(s, i, i+BLOCKSIZE-1)
175             i = i + BLOCKSIZE
176             if chunk ~= "" then return chunk
177             else return nil end
178         end
179     else return source.empty() end
180 end
181
182 --- Creates rewindable source.
183 -- @param src LTN12 source to be made rewindable
184 -- @return LTN12 source
185 function source.rewind(src)
186     base.assert(src)
187     local t = {}
188     return function(chunk)
189         if not chunk then
190             chunk = table.remove(t)
191             if not chunk then return src()
192             else return chunk end
193         else
194             t[#t+1] = chunk
195         end
196     end
197 end
198
199 --- Chain a source and a filter together.
200 -- @param src LTN12 source
201 -- @param f LTN12 filter
202 -- @return LTN12 source
203 function source.chain(src, f)
204     base.assert(src and f)
205     local last_in, last_out = "", ""
206     local state = "feeding"
207     local err
208     return function()
209         if not last_out then
210             base.error('source is empty!', 2)
211         end
212         while true do
213             if state == "feeding" then
214                 last_in, err = src()
215                 if err then return nil, err end
216                 last_out = f(last_in)
217                 if not last_out then
218                     if last_in then
219                         base.error('filter returned inappropriate nil')
220                     else
221                         return nil
222                     end
223                 elseif last_out ~= "" then
224                     state = "eating"
225                     if last_in then last_in = "" end
226                     return last_out
227                 end
228             else
229                 last_out = f(last_in)
230                 if last_out == "" then
231                     if last_in == "" then
232                         state = "feeding"
233                     else
234                         base.error('filter returned ""')
235                     end
236                 elseif not last_out then
237                     if last_in then
238                         base.error('filter returned inappropriate nil')
239                     else
240                         return nil
241                     end
242                 else
243                     return last_out
244                 end
245             end
246         end
247     end
248 end
249
250 --- Create a source that produces contents of several sources.
251 -- Sources will be used one after the other, as if they were concatenated
252 -- (thanks to Wim Couwenberg)
253 -- @param ... LTN12 sources
254 -- @return LTN12 source
255 function source.cat(...)
256     local src = table.remove(arg, 1)
257     return function()
258         while src do
259             local chunk, err = src()
260             if chunk then return chunk end
261             if err then return nil, err end
262             src = table.remove(arg, 1)
263         end
264     end
265 end
266
267 -----------------------------------------------------------------------------
268 -- Sink stuff
269 -----------------------------------------------------------------------------
270
271 --- LTN12 sink constructors
272 -- @class module
273 -- @name luci.ltn12.sink
274
275 --- Create a sink that stores into a table.
276 -- @param t output table to store into
277 -- @return LTN12 sink
278 function sink.table(t)
279     t = t or {}
280     local f = function(chunk, err)
281         if chunk then t[#t+1] = chunk end
282         return 1
283     end
284     return f, t
285 end
286
287 --- Turn a fancy sink into a simple sink.
288 -- @param snk fancy sink
289 -- @return LTN12 sink
290 function sink.simplify(snk)
291     base.assert(snk)
292     return function(chunk, err)
293         local ret, err_or_new = snk(chunk, err)
294         if not ret then return nil, err_or_new end
295         snk = err_or_new or snk
296         return 1
297     end
298 end
299
300 --- Create a file sink.
301 -- @param handle file handle to write to
302 -- @param io_err IO error
303 -- @return LTN12 sink
304 function sink.file(handle, io_err)
305     if handle then
306         return function(chunk, err)
307             if not chunk then
308                 handle:close()
309                 return 1
310             else return handle:write(chunk) end
311         end
312     else return sink.error(io_err or "unable to open file") end
313 end
314
315 -- creates a sink that discards data
316 local function null()
317     return 1
318 end
319
320 --- Create a sink that discards data.
321 -- @return LTN12 sink
322 function sink.null()
323     return null
324 end
325
326 --- Create a sink that just returns an error.
327 -- @param err Error object
328 -- @return LTN12 sink
329 function sink.error(err)
330     return function()
331         return nil, err
332     end
333 end
334
335 --- Chain a sink with a filter.
336 -- @param f LTN12 filter
337 -- @param snk LTN12 sink
338 -- @return LTN12 sink
339 function sink.chain(f, snk)
340     base.assert(f and snk)
341     return function(chunk, err)
342         if chunk ~= "" then
343             local filtered = f(chunk)
344             local done = chunk and ""
345             while true do
346                 local ret, snkerr = snk(filtered, err)
347                 if not ret then return nil, snkerr end
348                 if filtered == done then return 1 end
349                 filtered = f(done)
350             end
351         else return 1 end
352     end
353 end
354
355 -----------------------------------------------------------------------------
356 -- Pump stuff
357 -----------------------------------------------------------------------------
358
359 --- LTN12 pump functions
360 -- @class module
361 -- @name luci.ltn12.pump
362
363 --- Pump one chunk from the source to the sink.
364 -- @param src LTN12 source
365 -- @param snk LTN12 sink
366 -- @return Chunk of data or nil if an error occured
367 -- @return Error object
368 function pump.step(src, snk)
369     local chunk, src_err = src()
370     local ret, snk_err = snk(chunk, src_err)
371     if chunk and ret then return 1
372     else return nil, src_err or snk_err end
373 end
374
375 --- Pump all data from a source to a sink, using a step function.
376 -- @param src LTN12 source
377 -- @param snk LTN12 sink
378 -- @param step step function (optional)
379 -- @return 1 if the operation succeeded otherwise nil
380 -- @return Error object
381 function pump.all(src, snk, step)
382     base.assert(src and snk)
383     step = step or pump.step
384     while true do
385         local ret, err = step(src, snk)
386         if not ret then
387             if err then return nil, err
388             else return 1 end
389         end
390     end
391 end
392