Remove remaining references to boa and lucid
[project/luci.git] / libs / core / 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