9371290c61778ee4f3e5bb23c46d9796190e89b3
[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 not chunk then handle:close() end
148             return chunk
149         end
150     else return source.error(io_err or "unable to open file") end
151 end
152
153 --- Turn a fancy source into a simple source.
154 -- @param src fancy source
155 -- @return LTN12 source
156 function source.simplify(src)
157     base.assert(src)
158     return function()
159         local chunk, err_or_new = src()
160         src = err_or_new or src
161         if not chunk then return nil, err_or_new
162         else return chunk end
163     end
164 end
165
166 --- Create a string source.
167 -- @param s Data
168 -- @return LTN12 source
169 function source.string(s)
170     if s then
171         local i = 1
172         return function()
173             local chunk = string.sub(s, i, i+BLOCKSIZE-1)
174             i = i + BLOCKSIZE
175             if chunk ~= "" then return chunk
176             else return nil end
177         end
178     else return source.empty() end
179 end
180
181 --- Creates rewindable source.
182 -- @param src LTN12 source to be made rewindable
183 -- @return LTN12 source
184 function source.rewind(src)
185     base.assert(src)
186     local t = {}
187     return function(chunk)
188         if not chunk then
189             chunk = table.remove(t)
190             if not chunk then return src()
191             else return chunk end
192         else
193             t[#t+1] = chunk
194         end
195     end
196 end
197
198 --- Chain a source and a filter together.
199 -- @param src LTN12 source
200 -- @param f LTN12 filter
201 -- @return LTN12 source
202 function source.chain(src, f)
203     base.assert(src and f)
204     local last_in, last_out = "", ""
205     local state = "feeding"
206     local err
207     return function()
208         if not last_out then
209             base.error('source is empty!', 2)
210         end
211         while true do
212             if state == "feeding" then
213                 last_in, err = src()
214                 if err then return nil, err end
215                 last_out = f(last_in)
216                 if not last_out then
217                     if last_in then
218                         base.error('filter returned inappropriate nil')
219                     else
220                         return nil
221                     end
222                 elseif last_out ~= "" then
223                     state = "eating"
224                     if last_in then last_in = "" end
225                     return last_out
226                 end
227             else
228                 last_out = f(last_in)
229                 if last_out == "" then
230                     if last_in == "" then
231                         state = "feeding"
232                     else
233                         base.error('filter returned ""')
234                     end
235                 elseif not last_out then
236                     if last_in then
237                         base.error('filter returned inappropriate nil')
238                     else
239                         return nil
240                     end
241                 else
242                     return last_out
243                 end
244             end
245         end
246     end
247 end
248
249 --- Create a source that produces contents of several sources.
250 -- Sources will be used one after the other, as if they were concatenated
251 -- (thanks to Wim Couwenberg)
252 -- @param ... LTN12 sources
253 -- @return LTN12 source
254 function source.cat(...)
255     local src = table.remove(arg, 1)
256     return function()
257         while src do
258             local chunk, err = src()
259             if chunk then return chunk end
260             if err then return nil, err end
261             src = table.remove(arg, 1)
262         end
263     end
264 end
265
266 -----------------------------------------------------------------------------
267 -- Sink stuff
268 -----------------------------------------------------------------------------
269
270 --- LTN12 sink constructors
271 -- @class module
272 -- @name luci.ltn12.sink
273
274 --- Create a sink that stores into a table.
275 -- @param t output table to store into
276 -- @return LTN12 sink
277 function sink.table(t)
278     t = t or {}
279     local f = function(chunk, err)
280         if chunk then t[#t+1] = chunk end
281         return 1
282     end
283     return f, t
284 end
285
286 --- Turn a fancy sink into a simple sink.
287 -- @param snk fancy sink
288 -- @return LTN12 sink
289 function sink.simplify(snk)
290     base.assert(snk)
291     return function(chunk, err)
292         local ret, err_or_new = snk(chunk, err)
293         if not ret then return nil, err_or_new end
294         snk = err_or_new or snk
295         return 1
296     end
297 end
298
299 --- Create a file sink.
300 -- @param handle file handle to write to
301 -- @param io_err IO error
302 -- @return LTN12 sink
303 function sink.file(handle, io_err)
304     if handle then
305         return function(chunk, err)
306             if not chunk then
307                 handle:close()
308                 return 1
309             else return handle:write(chunk) end
310         end
311     else return sink.error(io_err or "unable to open file") end
312 end
313
314 -- creates a sink that discards data
315 local function null()
316     return 1
317 end
318
319 --- Create a sink that discards data.
320 -- @return LTN12 sink
321 function sink.null()
322     return null
323 end
324
325 --- Create a sink that just returns an error.
326 -- @param err Error object
327 -- @return LTN12 sink
328 function sink.error(err)
329     return function()
330         return nil, err
331     end
332 end
333
334 --- Chain a sink with a filter.
335 -- @param f LTN12 filter
336 -- @param snk LTN12 sink
337 -- @return LTN12 sink
338 function sink.chain(f, snk)
339     base.assert(f and snk)
340     return function(chunk, err)
341         if chunk ~= "" then
342             local filtered = f(chunk)
343             local done = chunk and ""
344             while true do
345                 local ret, snkerr = snk(filtered, err)
346                 if not ret then return nil, snkerr end
347                 if filtered == done then return 1 end
348                 filtered = f(done)
349             end
350         else return 1 end
351     end
352 end
353
354 -----------------------------------------------------------------------------
355 -- Pump stuff
356 -----------------------------------------------------------------------------
357
358 --- LTN12 pump functions
359 -- @class module
360 -- @name luci.ltn12.pump
361
362 --- Pump one chunk from the source to the sink.
363 -- @param src LTN12 source
364 -- @param snk LTN12 sink
365 -- @return Chunk of data or nil if an error occured
366 -- @return Error object
367 function pump.step(src, snk)
368     local chunk, src_err = src()
369     local ret, snk_err = snk(chunk, src_err)
370     if chunk and ret then return 1
371     else return nil, src_err or snk_err end
372 end
373
374 --- Pump all data from a source to a sink, using a step function.
375 -- @param src LTN12 source
376 -- @param snk LTN12 sink
377 -- @param step step function (optional)
378 -- @return 1 if the operation succeeded otherwise nil
379 -- @return Error object
380 function pump.all(src, snk, step)
381     base.assert(src and snk)
382     step = step or pump.step
383     while true do
384         local ret, err = step(src, snk)
385         if not ret then
386             if err then return nil, err
387             else return 1 end
388         end
389     end
390 end
391