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