OpenWrt_Luci_Lua/1_1.mi_Lua/luci/ltn12.lua
2015-06-28 14:39:56 +08:00

392 lines
11 KiB
Lua
Raw Permalink Blame History

--[[
LuaSocket 2.0.2 license
Copyright <20> 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: ltn12.lua 3212 2008-09-09 12:44:41Z Cyrus $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local base = _G
--- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
-- 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
-----------------------------------------------------------------------------
--- LTN12 Filter constructors
-- @class module
-- @name luci.ltn12.filter
--- Return a high level filter that cycles a low-level filter
-- by passing it each chunk and updating a context between calls.
-- @param low Low-level filter
-- @param ctx Context
-- @param extra Extra argument passed to the low-level filter
-- @return LTN12 filter
function filter.cycle(low, ctx, extra)
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
--- Chain a bunch of filters together.
-- (thanks to Wim Couwenberg)
-- @param ... filters to be chained
-- @return LTN12 filter
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
-----------------------------------------------------------------------------
--- LTN12 Source constructors
-- @class module
-- @name luci.ltn12.source
-- create an empty source
local function empty()
return nil
end
--- Create an empty source.
-- @return LTN12 source
function source.empty()
return empty
end
--- Return a source that just outputs an error.
-- @param err Error object
-- @return LTN12 source
function source.error(err)
return function()
return nil, err
end
end
--- Create a file source.
-- @param handle File handle ready for reading
-- @param io_err IO error object
-- @return LTN12 source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
else return source.error(io_err or "unable to open file") end
end
--- Turn a fancy source into a simple source.
-- @param src fancy source
-- @return LTN12 source
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
--- Create a string source.
-- @param s Data
-- @return LTN12 source
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
--- Creates rewindable source.
-- @param src LTN12 source to be made rewindable
-- @return LTN12 source
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
--- Chain a source and a filter together.
-- @param src LTN12 source
-- @param f LTN12 filter
-- @return LTN12 source
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
--- Create a source that produces contents of several sources.
-- Sources will be used one after the other, as if they were concatenated
-- (thanks to Wim Couwenberg)
-- @param ... LTN12 sources
-- @return LTN12 source
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
-----------------------------------------------------------------------------
--- LTN12 sink constructors
-- @class module
-- @name luci.ltn12.sink
--- Create a sink that stores into a table.
-- @param t output table to store into
-- @return LTN12 sink
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
--- Turn a fancy sink into a simple sink.
-- @param snk fancy sink
-- @return LTN12 sink
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
--- Create a file sink.
-- @param handle file handle to write to
-- @param io_err IO error
-- @return LTN12 sink
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
--- Create a sink that discards data.
-- @return LTN12 sink
function sink.null()
return null
end
--- Create a sink that just returns an error.
-- @param err Error object
-- @return LTN12 sink
function sink.error(err)
return function()
return nil, err
end
end
--- Chain a sink with a filter.
-- @param f LTN12 filter
-- @param snk LTN12 sink
-- @return LTN12 sink
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
-----------------------------------------------------------------------------
--- LTN12 pump functions
-- @class module
-- @name luci.ltn12.pump
--- Pump one chunk from the source to the sink.
-- @param src LTN12 source
-- @param snk LTN12 sink
-- @return Chunk of data or nil if an error occured
-- @return Error object
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
--- Pump all data from a source to a sink, using a step function.
-- @param src LTN12 source
-- @param snk LTN12 sink
-- @param step step function (optional)
-- @return 1 if the operation succeeded otherwise nil
-- @return Error object
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