1
2
3
4local select, unpack = select, unpack
5local insert, remove = table.insert, table.remove
6local sub = string.sub
7
8local function report(fmt,first,...)
9 if logs then
10 report = logs and logs.reporter("ltn12")
11 report(fmt,first,...)
12 elseif fmt then
13 fmt = "ltn12: " .. fmt
14 if first then
15 print(format(fmt,first,...))
16 else
17 print(fmt)
18 end
19 end
20end
21
22local filter = { }
23local source = { }
24local sink = { }
25local pump = { }
26
27local ltn12 = {
28
29 _VERSION = "LTN12 1.0.3",
30
31 BLOCKSIZE = 2048,
32
33 filter = filter,
34 source = source,
35 sink = sink,
36 pump = pump,
37
38 report = report,
39
40}
41
42
43
44function filter.cycle(low, ctx, extra)
45 if low then
46 return function(chunk)
47 return (low(ctx, chunk, extra))
48 end
49 end
50end
51
52
53
54function filter.chain(...)
55 local arg = { ... }
56 local n = select('#',...)
57 local top = 1
58 local index = 1
59 local retry = ""
60 return function(chunk)
61 retry = chunk and retry
62 while true do
63 local action = arg[index]
64 if index == top then
65 chunk = action(chunk)
66 if chunk == "" or top == n then
67 return chunk
68 elseif chunk then
69 index = index + 1
70 else
71 top = top + 1
72 index = top
73 end
74 else
75 chunk = action(chunk or "")
76 if chunk == "" then
77 index = index - 1
78 chunk = retry
79 elseif chunk then
80 if index == n then
81 return chunk
82 else
83 index = index + 1
84 end
85 else
86 report("error: filter returned inappropriate 'nil'")
87 return
88 end
89 end
90 end
91 end
92end
93
94
95
96local function empty()
97 return nil
98end
99
100function source.empty()
101 return empty
102end
103
104
105
106local function sourceerror(err)
107 return function()
108 return nil, err
109 end
110end
111
112source.error = sourceerror
113
114
115
116function source.file(handle, io_err)
117 if handle then
118 local blocksize = ltn12.BLOCKSIZE
119 return function()
120 local chunk = handle:read(blocksize)
121 if not chunk then
122 handle:close()
123 end
124 return chunk
125 end
126 else
127 return sourceerror(io_err or "unable to open file")
128 end
129end
130
131
132
133function source.simplify(src)
134 return function()
135 local chunk, err_or_new = src()
136 if err_or_new then
137 src = err_or_new
138 end
139 if chunk then
140 return chunk
141 else
142 return nil, err_or_new
143 end
144 end
145end
146
147
148
149function source.string(s)
150 if s then
151 local blocksize = ltn12.BLOCKSIZE
152 local i = 1
153 return function()
154 local nexti = i + blocksize
155 local chunk = sub(s, i, nexti - 1)
156 i = nexti
157 if chunk ~= "" then
158 return chunk
159 else
160 return nil
161 end
162 end
163 else return source.empty() end
164end
165
166
167
168function source.rewind(src)
169 local t = { }
170 return function(chunk)
171 if chunk then
172 insert(t, chunk)
173 else
174 chunk = remove(t)
175 if chunk then
176 return chunk
177 else
178 return src()
179 end
180 end
181 end
182end
183
184
185
186function source.chain(src, f, ...)
187 if ... then
188 f = filter.chain(f, ...)
189 end
190 local last_in = ""
191 local last_out = ""
192 local state = "feeding"
193 local err
194 return function()
195 if not last_out then
196 report("error: source is empty")
197 return
198 end
199 while true do
200 if state == "feeding" then
201 last_in, err = src()
202 if err then
203 return nil, err
204 end
205 last_out = f(last_in)
206 if not last_out then
207 if last_in then
208 report("error: filter returned inappropriate 'nil'")
209 end
210 return nil
211 elseif last_out ~= "" then
212 state = "eating"
213 if last_in then
214 last_in = ""
215 end
216 return last_out
217 end
218 else
219 last_out = f(last_in)
220 if last_out == "" then
221 if last_in == "" then
222 state = "feeding"
223 else
224 report("error: filter returned nothing")
225 return
226 end
227 elseif not last_out then
228 if last_in then
229 report("filter returned inappropriate 'nil'")
230 end
231 return nil
232 else
233 return last_out
234 end
235 end
236 end
237 end
238end
239
240
241
242
243function source.cat(...)
244 local arg = { ... }
245 local src = remove(arg,1)
246 return function()
247 while src do
248 local chunk, err = src()
249 if chunk then
250 return chunk
251 end
252 if err then
253 return nil, err
254 end
255 src = remove(arg,1)
256 end
257 end
258end
259
260
261
262function sink.table(t)
263 if not t then
264 t = { }
265 end
266 local f = function(chunk, err)
267 if chunk then
268 insert(t, chunk)
269 end
270 return 1
271 end
272 return f, t
273end
274
275
276
277function sink.simplify(snk)
278 return function(chunk, err)
279 local ret, err_or_new = snk(chunk, err)
280 if not ret then
281 return nil, err_or_new
282 end
283 if err_or_new then
284 snk = err_or_new
285 end
286 return 1
287 end
288end
289
290
291
292local function null()
293 return 1
294end
295
296function sink.null()
297 return null
298end
299
300
301
302local function sinkerror(err)
303 return function()
304 return nil, err
305 end
306end
307
308sink.error = sinkerror
309
310
311
312function sink.file(handle, io_err)
313 if handle then
314 return function(chunk, err)
315 if not chunk then
316 handle:close()
317 return 1
318 else
319 return handle:write(chunk)
320 end
321 end
322 else
323 return sinkerror(io_err or "unable to open file")
324 end
325end
326
327
328
329function sink.chain(f, snk, ...)
330 if ... then
331 local args = { f, snk, ... }
332 snk = remove(args, #args)
333 f = filter.chain(unpack(args))
334 end
335 return function(chunk, err)
336 if chunk ~= "" then
337 local filtered = f(chunk)
338 local done = chunk and ""
339 while true do
340 local ret, snkerr = snk(filtered, err)
341 if not ret then
342 return nil, snkerr
343 end
344 if filtered == done then
345 return 1
346 end
347 filtered = f(done)
348 end
349 else
350 return 1
351 end
352 end
353end
354
355
356
357function pump.step(src, snk)
358 local chunk, src_err = src()
359 local ret, snk_err = snk(chunk, src_err)
360 if chunk and ret then
361 return 1
362 else
363 return nil, src_err or snk_err
364 end
365end
366
367
368
369function pump.all(src, snk, step)
370 if not step then
371 step = pump.step
372 end
373 while true do
374 local ret, err = step(src, snk)
375 if not ret then
376 if err then
377 return nil, err
378 else
379 return 1
380 end
381 end
382 end
383end
384
385package.loaded["ltn12"] = ltn12
386
387return ltn12
388 |