Rework LuCI build system
[project/luci.git] / modules / luci-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