ltn12.lua 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. --[[
  2. LuaSocket 2.0.2 license
  3. Copyright � 2004-2007 Diego Nehab
  4. Permission is hereby granted, free of charge, to any person obtaining a
  5. copy of this software and associated documentation files (the "Software"),
  6. to deal in the Software without restriction, including without limitation
  7. the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. and/or sell copies of the Software, and to permit persons to whom the
  9. Software is furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  17. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  18. DEALINGS IN THE SOFTWARE.
  19. ]]--
  20. --[[
  21. Changes made by LuCI project:
  22. * Renamed to luci.ltn12 to avoid collisions with luasocket
  23. * Added inline documentation
  24. ]]--
  25. -----------------------------------------------------------------------------
  26. -- LTN12 - Filters, sources, sinks and pumps.
  27. -- LuaSocket toolkit.
  28. -- Author: Diego Nehab
  29. -- RCS ID: $Id$
  30. -----------------------------------------------------------------------------
  31. -----------------------------------------------------------------------------
  32. -- Declare module
  33. -----------------------------------------------------------------------------
  34. local string = require("string")
  35. local table = require("table")
  36. local base = _G
  37. -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts
  38. module("luci.ltn12")
  39. filter = {}
  40. source = {}
  41. sink = {}
  42. pump = {}
  43. -- 2048 seems to be better in windows...
  44. BLOCKSIZE = 2048
  45. _VERSION = "LTN12 1.0.1"
  46. -----------------------------------------------------------------------------
  47. -- Filter stuff
  48. -----------------------------------------------------------------------------
  49. -- by passing it each chunk and updating a context between calls.
  50. function filter.cycle(low, ctx, extra)
  51. base.assert(low)
  52. return function(chunk)
  53. local ret
  54. ret, ctx = low(ctx, chunk, extra)
  55. return ret
  56. end
  57. end
  58. -- (thanks to Wim Couwenberg)
  59. function filter.chain(...)
  60. local n = table.getn(arg)
  61. local top, index = 1, 1
  62. local retry = ""
  63. return function(chunk)
  64. retry = chunk and retry
  65. while true do
  66. if index == top then
  67. chunk = arg[index](chunk)
  68. if chunk == "" or top == n then return chunk
  69. elseif chunk then index = index + 1
  70. else
  71. top = top+1
  72. index = top
  73. end
  74. else
  75. chunk = arg[index](chunk or "")
  76. if chunk == "" then
  77. index = index - 1
  78. chunk = retry
  79. elseif chunk then
  80. if index == n then return chunk
  81. else index = index + 1 end
  82. else base.error("filter returned inappropriate nil") end
  83. end
  84. end
  85. end
  86. end
  87. -----------------------------------------------------------------------------
  88. -- Source stuff
  89. -----------------------------------------------------------------------------
  90. -- create an empty source
  91. local function empty()
  92. return nil
  93. end
  94. function source.empty()
  95. return empty
  96. end
  97. function source.error(err)
  98. return function()
  99. return nil, err
  100. end
  101. end
  102. function source.file(handle, io_err)
  103. if handle then
  104. return function()
  105. local chunk = handle:read(BLOCKSIZE)
  106. if chunk and chunk:len() == 0 then chunk = nil end
  107. if not chunk then handle:close() end
  108. return chunk
  109. end
  110. else return source.error(io_err or "unable to open file") end
  111. end
  112. function source.simplify(src)
  113. base.assert(src)
  114. return function()
  115. local chunk, err_or_new = src()
  116. src = err_or_new or src
  117. if not chunk then return nil, err_or_new
  118. else return chunk end
  119. end
  120. end
  121. function source.string(s)
  122. if s then
  123. local i = 1
  124. return function()
  125. local chunk = string.sub(s, i, i+BLOCKSIZE-1)
  126. i = i + BLOCKSIZE
  127. if chunk ~= "" then return chunk
  128. else return nil end
  129. end
  130. else return source.empty() end
  131. end
  132. function source.rewind(src)
  133. base.assert(src)
  134. local t = {}
  135. return function(chunk)
  136. if not chunk then
  137. chunk = table.remove(t)
  138. if not chunk then return src()
  139. else return chunk end
  140. else
  141. t[#t+1] = chunk
  142. end
  143. end
  144. end
  145. function source.chain(src, f)
  146. base.assert(src and f)
  147. local last_in, last_out = "", ""
  148. local state = "feeding"
  149. local err
  150. return function()
  151. if not last_out then
  152. base.error('source is empty!', 2)
  153. end
  154. while true do
  155. if state == "feeding" then
  156. last_in, err = src()
  157. if err then return nil, err end
  158. last_out = f(last_in)
  159. if not last_out then
  160. if last_in then
  161. base.error('filter returned inappropriate nil')
  162. else
  163. return nil
  164. end
  165. elseif last_out ~= "" then
  166. state = "eating"
  167. if last_in then last_in = "" end
  168. return last_out
  169. end
  170. else
  171. last_out = f(last_in)
  172. if last_out == "" then
  173. if last_in == "" then
  174. state = "feeding"
  175. else
  176. base.error('filter returned ""')
  177. end
  178. elseif not last_out then
  179. if last_in then
  180. base.error('filter returned inappropriate nil')
  181. else
  182. return nil
  183. end
  184. else
  185. return last_out
  186. end
  187. end
  188. end
  189. end
  190. end
  191. -- Sources will be used one after the other, as if they were concatenated
  192. -- (thanks to Wim Couwenberg)
  193. function source.cat(...)
  194. local src = table.remove(arg, 1)
  195. return function()
  196. while src do
  197. local chunk, err = src()
  198. if chunk then return chunk end
  199. if err then return nil, err end
  200. src = table.remove(arg, 1)
  201. end
  202. end
  203. end
  204. -----------------------------------------------------------------------------
  205. -- Sink stuff
  206. -----------------------------------------------------------------------------
  207. function sink.table(t)
  208. t = t or {}
  209. local f = function(chunk, err)
  210. if chunk then t[#t+1] = chunk end
  211. return 1
  212. end
  213. return f, t
  214. end
  215. function sink.simplify(snk)
  216. base.assert(snk)
  217. return function(chunk, err)
  218. local ret, err_or_new = snk(chunk, err)
  219. if not ret then return nil, err_or_new end
  220. snk = err_or_new or snk
  221. return 1
  222. end
  223. end
  224. function sink.file(handle, io_err)
  225. if handle then
  226. return function(chunk, err)
  227. if not chunk then
  228. handle:close()
  229. return 1
  230. else return handle:write(chunk) end
  231. end
  232. else return sink.error(io_err or "unable to open file") end
  233. end
  234. -- creates a sink that discards data
  235. local function null()
  236. return 1
  237. end
  238. function sink.null()
  239. return null
  240. end
  241. function sink.error(err)
  242. return function()
  243. return nil, err
  244. end
  245. end
  246. function sink.chain(f, snk)
  247. base.assert(f and snk)
  248. return function(chunk, err)
  249. if chunk ~= "" then
  250. local filtered = f(chunk)
  251. local done = chunk and ""
  252. while true do
  253. local ret, snkerr = snk(filtered, err)
  254. if not ret then return nil, snkerr end
  255. if filtered == done then return 1 end
  256. filtered = f(done)
  257. end
  258. else return 1 end
  259. end
  260. end
  261. -----------------------------------------------------------------------------
  262. -- Pump stuff
  263. -----------------------------------------------------------------------------
  264. function pump.step(src, snk)
  265. local chunk, src_err = src()
  266. local ret, snk_err = snk(chunk, src_err)
  267. if chunk and ret then return 1
  268. else return nil, src_err or snk_err end
  269. end
  270. function pump.all(src, snk, step)
  271. base.assert(src and snk)
  272. step = step or pump.step
  273. while true do
  274. local ret, err = step(src, snk)
  275. if not ret then
  276. if err then return nil, err
  277. else return 1 end
  278. end
  279. end
  280. end