--[[ LuaSocket 2.0.2 license Copyright � 2004-2007 Diego Nehab Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]]-- --[[ Changes made by LuCI project: * Renamed to luci.ltn12 to avoid collisions with luasocket * Added inline documentation ]]-- ----------------------------------------------------------------------------- -- LTN12 - Filters, sources, sinks and pumps. -- LuaSocket toolkit. -- Author: Diego Nehab -- RCS ID: $Id$ ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -- Declare module ----------------------------------------------------------------------------- local string = require("string") local table = require("table") local base = _G -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts module("luci.ltn12") filter = {} source = {} sink = {} pump = {} -- 2048 seems to be better in windows... BLOCKSIZE = 2048 _VERSION = "LTN12 1.0.1" ----------------------------------------------------------------------------- -- Filter stuff ----------------------------------------------------------------------------- -- by passing it each chunk and updating a context between calls. function filter.cycle(low, ctx, extra) base.assert(low) return function(chunk) local ret ret, ctx = low(ctx, chunk, extra) return ret end end -- (thanks to Wim Couwenberg) function filter.chain(...) local n = table.getn(arg) local top, index = 1, 1 local retry = "" return function(chunk) retry = chunk and retry while true do if index == top then chunk = arg[index](chunk) if chunk == "" or top == n then return chunk elseif chunk then index = index + 1 else top = top+1 index = top end else chunk = arg[index](chunk or "") if chunk == "" then index = index - 1 chunk = retry elseif chunk then if index == n then return chunk else index = index + 1 end else base.error("filter returned inappropriate nil") end end end end end ----------------------------------------------------------------------------- -- Source stuff ----------------------------------------------------------------------------- -- create an empty source local function empty() return nil end function source.empty() return empty end function source.error(err) return function() return nil, err end end function source.file(handle, io_err) if handle then return function() local chunk = handle:read(BLOCKSIZE) if chunk and chunk:len() == 0 then chunk = nil end if not chunk then handle:close() end return chunk end else return source.error(io_err or "unable to open file") end end function source.simplify(src) base.assert(src) return function() local chunk, err_or_new = src() src = err_or_new or src if not chunk then return nil, err_or_new else return chunk end end end function source.string(s) if s then local i = 1 return function() local chunk = string.sub(s, i, i+BLOCKSIZE-1) i = i + BLOCKSIZE if chunk ~= "" then return chunk else return nil end end else return source.empty() end end function source.rewind(src) base.assert(src) local t = {} return function(chunk) if not chunk then chunk = table.remove(t) if not chunk then return src() else return chunk end else t[#t+1] = chunk end end end function source.chain(src, f) base.assert(src and f) local last_in, last_out = "", "" local state = "feeding" local err return function() if not last_out then base.error('source is empty!', 2) end while true do if state == "feeding" then last_in, err = src() if err then return nil, err end last_out = f(last_in) if not last_out then if last_in then base.error('filter returned inappropriate nil') else return nil end elseif last_out ~= "" then state = "eating" if last_in then last_in = "" end return last_out end else last_out = f(last_in) if last_out == "" then if last_in == "" then state = "feeding" else base.error('filter returned ""') end elseif not last_out then if last_in then base.error('filter returned inappropriate nil') else return nil end else return last_out end end end end end -- Sources will be used one after the other, as if they were concatenated -- (thanks to Wim Couwenberg) function source.cat(...) local src = table.remove(arg, 1) return function() while src do local chunk, err = src() if chunk then return chunk end if err then return nil, err end src = table.remove(arg, 1) end end end ----------------------------------------------------------------------------- -- Sink stuff ----------------------------------------------------------------------------- function sink.table(t) t = t or {} local f = function(chunk, err) if chunk then t[#t+1] = chunk end return 1 end return f, t end function sink.simplify(snk) base.assert(snk) return function(chunk, err) local ret, err_or_new = snk(chunk, err) if not ret then return nil, err_or_new end snk = err_or_new or snk return 1 end end function sink.file(handle, io_err) if handle then return function(chunk, err) if not chunk then handle:close() return 1 else return handle:write(chunk) end end else return sink.error(io_err or "unable to open file") end end -- creates a sink that discards data local function null() return 1 end function sink.null() return null end function sink.error(err) return function() return nil, err end end function sink.chain(f, snk) base.assert(f and snk) return function(chunk, err) if chunk ~= "" then local filtered = f(chunk) local done = chunk and "" while true do local ret, snkerr = snk(filtered, err) if not ret then return nil, snkerr end if filtered == done then return 1 end filtered = f(done) end else return 1 end end end ----------------------------------------------------------------------------- -- Pump stuff ----------------------------------------------------------------------------- function pump.step(src, snk) local chunk, src_err = src() local ret, snk_err = snk(chunk, src_err) if chunk and ret then return 1 else return nil, src_err or snk_err end end function pump.all(src, snk, step) base.assert(src and snk) step = step or pump.step while true do local ret, err = step(src, snk) if not ret then if err then return nil, err else return 1 end end end end