* Moved luci.http.protocol to libs/http
[project/luci.git] / libs / core / lua / 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 -- LTN12 - Filters, sources, sinks and pumps.
25 -- LuaSocket toolkit.
26 -- Author: Diego Nehab
27 -- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
28 -----------------------------------------------------------------------------
29
30 -----------------------------------------------------------------------------
31 -- Declare module
32 -----------------------------------------------------------------------------
33 local string = require("string")
34 local table = require("table")
35 local base = _G
36 module("ltn12")
37
38 filter = {}
39 source = {}
40 sink = {}
41 pump = {}
42
43 -- 2048 seems to be better in windows...
44 BLOCKSIZE = 2048
45 _VERSION = "LTN12 1.0.1"
46
47 -----------------------------------------------------------------------------
48 -- Filter stuff
49 -----------------------------------------------------------------------------
50 -- returns a high level filter that cycles a low-level filter
51 function filter.cycle(low, ctx, extra)
52     base.assert(low)
53     return function(chunk)
54         local ret
55         ret, ctx = low(ctx, chunk, extra)
56         return ret
57     end
58 end
59
60 -- chains a bunch of filters together
61 -- (thanks to Wim Couwenberg)
62 function filter.chain(...)
63     local n = table.getn(arg)
64     local top, index = 1, 1
65     local retry = ""
66     return function(chunk)
67         retry = chunk and retry
68         while true do
69             if index == top then
70                 chunk = arg[index](chunk)
71                 if chunk == "" or top == n then return chunk
72                 elseif chunk then index = index + 1
73                 else
74                     top = top+1
75                     index = top
76                 end
77             else
78                 chunk = arg[index](chunk or "")
79                 if chunk == "" then
80                     index = index - 1
81                     chunk = retry
82                 elseif chunk then
83                     if index == n then return chunk
84                     else index = index + 1 end
85                 else base.error("filter returned inappropriate nil") end
86             end
87         end
88     end
89 end
90
91 -----------------------------------------------------------------------------
92 -- Source stuff
93 -----------------------------------------------------------------------------
94 -- create an empty source
95 local function empty()
96     return nil
97 end
98
99 function source.empty()
100     return empty
101 end
102
103 -- returns a source that just outputs an error
104 function source.error(err)
105     return function()
106         return nil, err
107     end
108 end
109
110 -- creates a file source
111 function source.file(handle, io_err)
112     if handle then
113         return function()
114             local chunk = handle:read(BLOCKSIZE)
115             if not chunk then handle:close() end
116             return chunk
117         end
118     else return source.error(io_err or "unable to open file") end
119 end
120
121 -- turns a fancy source into a simple source
122 function source.simplify(src)
123     base.assert(src)
124     return function()
125         local chunk, err_or_new = src()
126         src = err_or_new or src
127         if not chunk then return nil, err_or_new
128         else return chunk end
129     end
130 end
131
132 -- creates string source
133 function source.string(s)
134     if s then
135         local i = 1
136         return function()
137             local chunk = string.sub(s, i, i+BLOCKSIZE-1)
138             i = i + BLOCKSIZE
139             if chunk ~= "" then return chunk
140             else return nil end
141         end
142     else return source.empty() end
143 end
144
145 -- creates rewindable source
146 function source.rewind(src)
147     base.assert(src)
148     local t = {}
149     return function(chunk)
150         if not chunk then
151             chunk = table.remove(t)
152             if not chunk then return src()
153             else return chunk end
154         else
155             table.insert(t, chunk)
156         end
157     end
158 end
159
160 function source.chain(src, f)
161     base.assert(src and f)
162     local last_in, last_out = "", ""
163     local state = "feeding"
164     local err
165     return function()
166         if not last_out then
167             base.error('source is empty!', 2)
168         end
169         while true do
170             if state == "feeding" then
171                 last_in, err = src()
172                 if err then return nil, err end
173                 last_out = f(last_in)
174                 if not last_out then
175                     if last_in then
176                         base.error('filter returned inappropriate nil')
177                     else
178                         return nil
179                     end
180                 elseif last_out ~= "" then
181                     state = "eating"
182                     if last_in then last_in = "" end
183                     return last_out
184                 end
185             else
186                 last_out = f(last_in)
187                 if last_out == "" then
188                     if last_in == "" then
189                         state = "feeding"
190                     else
191                         base.error('filter returned ""')
192                     end
193                 elseif not last_out then
194                     if last_in then
195                         base.error('filter returned inappropriate nil')
196                     else
197                         return nil
198                     end
199                 else
200                     return last_out
201                 end
202             end
203         end
204     end
205 end
206
207 -- creates a source that produces contents of several sources, one after the
208 -- other, as if they were concatenated
209 -- (thanks to Wim Couwenberg)
210 function source.cat(...)
211     local src = table.remove(arg, 1)
212     return function()
213         while src do
214             local chunk, err = src()
215             if chunk then return chunk end
216             if err then return nil, err end
217             src = table.remove(arg, 1)
218         end
219     end
220 end
221
222 -----------------------------------------------------------------------------
223 -- Sink stuff
224 -----------------------------------------------------------------------------
225 -- creates a sink that stores into a table
226 function sink.table(t)
227     t = t or {}
228     local f = function(chunk, err)
229         if chunk then table.insert(t, chunk) end
230         return 1
231     end
232     return f, t
233 end
234
235 -- turns a fancy sink into a simple sink
236 function sink.simplify(snk)
237     base.assert(snk)
238     return function(chunk, err)
239         local ret, err_or_new = snk(chunk, err)
240         if not ret then return nil, err_or_new end
241         snk = err_or_new or snk
242         return 1
243     end
244 end
245
246 -- creates a file sink
247 function sink.file(handle, io_err)
248     if handle then
249         return function(chunk, err)
250             if not chunk then
251                 handle:close()
252                 return 1
253             else return handle:write(chunk) end
254         end
255     else return sink.error(io_err or "unable to open file") end
256 end
257
258 -- creates a sink that discards data
259 local function null()
260     return 1
261 end
262
263 function sink.null()
264     return null
265 end
266
267 -- creates a sink that just returns an error
268 function sink.error(err)
269     return function()
270         return nil, err
271     end
272 end
273
274 -- chains a sink with a filter
275 function sink.chain(f, snk)
276     base.assert(f and snk)
277     return function(chunk, err)
278         if chunk ~= "" then
279             local filtered = f(chunk)
280             local done = chunk and ""
281             while true do
282                 local ret, snkerr = snk(filtered, err)
283                 if not ret then return nil, snkerr end
284                 if filtered == done then return 1 end
285                 filtered = f(done)
286             end
287         else return 1 end
288     end
289 end
290
291 -----------------------------------------------------------------------------
292 -- Pump stuff
293 -----------------------------------------------------------------------------
294 -- pumps one chunk from the source to the sink
295 function pump.step(src, snk)
296     local chunk, src_err = src()
297     local ret, snk_err = snk(chunk, src_err)
298     if chunk and ret then return 1
299     else return nil, src_err or snk_err end
300 end
301
302 -- pumps all data from a source to a sink, using a step function
303 function pump.all(src, snk, step)
304     base.assert(src and snk)
305     step = step or pump.step
306     while true do
307         local ret, err = step(src, snk)
308         if not ret then
309             if err then return nil, err
310             else return 1 end
311         end
312     end
313 end
314