jerome@65: local M, assert, getenv = {}, assert, os.getenv jerome@65: jerome@65: local function maybe(m) jerome@65: local ok, v = pcall(require, m) jerome@65: return ok and v jerome@65: end jerome@65: local lfs = maybe("lfs") -- LuaFileSystem; http://keplerproject.github.io/luafilesystem/ jerome@65: local zlib = maybe("zlib") -- lzlib; https://github.com/LuaDist/lzlib jerome@65: local bit = maybe("bit") -- Lua BitOp; http://bitop.luajit.org jerome@65: local socket = maybe("socket.http") -- LuaSocket; http://w3.impa.br/~diego/software/luasocket/home.html jerome@65: jerome@65: local function shellEscape(s) jerome@65: return '"' .. s:gsub('"', '"\\\\""') .. '"' jerome@65: end jerome@65: local function readAndDeleteFile(path) jerome@65: local h, err = io.open(path, "rb") jerome@65: if h then jerome@65: local c = h:read("*a") jerome@65: h:close() jerome@65: h, err = c, nil jerome@65: end jerome@65: os.remove(path) jerome@65: return h, err jerome@65: end jerome@65: jerome@65: local dir_sep = package and package.config and package.config:sub(1,1) or "/" jerome@65: do -- M.path(a, b, ...) jerome@65: M.path = function(a, b, ...) jerome@65: if a and b then jerome@65: return M.path(a .. (a:sub(-1) ~= dir_sep and dir_sep or "") .. b, ...) jerome@65: end jerome@65: return a jerome@65: end jerome@65: end jerome@65: M.url = function(a, b, ...) jerome@65: if a and b then jerome@65: return M.url(a .. ((a:sub(-1) == "/" or b:sub(1,1) == "/") and "" or "/") .. b, ...) jerome@65: end jerome@65: return a jerome@65: end jerome@65: jerome@65: M.commands = jerome@65: dir_sep == '/' and {toDevNull=' 2>/dev/null', ls='ls %s', mkdir='mkdir -p %s', gzip='gzip -dcq %s'} or jerome@65: dir_sep == '\\' and {toDevNull=' 2>NUL', ls='(for %%a in (%s) do @echo %%~fa)', mkdir='mkdir %s', gzip='gzip -dcq %s', TMP=os.getenv('TMP') or os.getenv('TEMP')} jerome@65: jerome@65: M.tmpname = function() jerome@65: local tn = os.tmpname() jerome@65: return (M.commands and M.commands.TMP or "") .. tn jerome@65: end jerome@65: jerome@65: M.decompress = zlib and zlib.decompress or function(compressed) jerome@65: assert(type(compressed) == "string", 'Syntax: casc.platform.decompress("compressed")') jerome@65: assert(M.commands and M.commands.gzip and M.commands.toDevNull, 'unsupported platform') jerome@65: jerome@65: local f, f2 = M.tmpname(), M.tmpname() jerome@65: local h = io.open(f, "wb") jerome@65: h:write('\31\139\8\0\0\0\0\0') jerome@65: h:write(compressed) jerome@65: h:close() jerome@65: jerome@65: os.execute(M.commands.gzip:format(shellEscape(f)) .. " 1>" .. f2 .. " " .. M.commands.toDevNull) jerome@65: os.remove(f) jerome@65: jerome@65: return readAndDeleteFile(f2) jerome@65: end jerome@65: jerome@65: M.mkdir = lfs and lfs.mkdir or function(path) jerome@65: assert(type(path) == 'string', 'Syntax: casc.platform.mkdir("path")') jerome@65: assert(M.commands and M.commands.mkdir, 'unsupported platform') jerome@65: jerome@65: return os.execute(M.commands.mkdir:format(shellEscape(path))) jerome@65: end jerome@65: jerome@65: M.files = lfs and function(dir, glob) jerome@65: assert(type(dir) == "string" and type(glob) == 'string', 'Syntax: casc.platform.files("dir", "glob")') jerome@65: local pat = "^" .. glob:gsub("%.%-%+", "%%%0"):gsub("%*", ".*") .. "$" jerome@65: local t, ni = {}, 1 jerome@65: for f in lfs.dir(dir) do jerome@65: if f ~= "." and f ~= ".." and f:match(pat) then jerome@65: t[ni], ni = M.path(dir, f), ni + 1 jerome@65: end jerome@65: end jerome@65: return pairs(t) jerome@65: end or function(dir, glob) jerome@65: assert(type(dir) == "string" and type(glob) == 'string', 'Syntax: casc.platform.files("dir", "glob")') jerome@65: assert(M.commands and M.commands.ls, 'unsupported platform') jerome@65: jerome@65: local dir, files = glob:match("^(.-)([^" .. dir_sep .. "]+)$") jerome@65: local t, ni, h = {}, 1, io.popen(M.commands.ls:format(shellEscape(dir) .. files), "r") jerome@65: for l in h:lines() do jerome@65: t[ni], ni = l, ni + 1 jerome@65: end jerome@65: h:close() jerome@65: return pairs(t) jerome@65: end jerome@65: jerome@65: local floor = math.floor jerome@65: M.rol = bit and bit.rol or function(n, b) jerome@65: local n, e2 = n % 2^32, 2^(32-b) jerome@65: return ((n % e2) * 2^b + floor(n/e2)) % 2^32 jerome@65: end jerome@65: jerome@65: M.bxor = bit and bit.bxor or function(a, b) jerome@65: local out, m, lm = 0, 1 jerome@65: for i=1,32 do jerome@65: m, lm = m+m, m jerome@65: local am, bm = a % m, b % m jerome@65: out, a, b = out + (am == bm and 0 or lm), a-am, b-bm jerome@65: if a == 0 or b == 0 then jerome@65: return (out + b + a) % 2^32 jerome@65: end jerome@65: end jerome@65: return out jerome@65: end jerome@65: jerome@65: if socket then jerome@65: socket.USERAGENT, socket.TIMEOUT = "lcasc/1.1", 5 jerome@65: local ltn12, RETRIES = require("ltn12"), 3 jerome@65: M.http = function(url, h) jerome@65: for i=1,RETRIES do jerome@65: local sink = {} jerome@65: local ok, status, head = socket.request({url=url, sink=ltn12.sink.table(sink), headers=h}) jerome@65: if ok then jerome@65: local cnt = table.concat(sink, "") jerome@65: return status >= 200 and status < 300 and cnt, status, head, cnt jerome@65: elseif i == RETRIES then jerome@65: error("HTTP request failed: " .. tostring(status) .. "\nURL: " .. tostring(url)) jerome@65: end jerome@65: end jerome@65: end jerome@65: else jerome@65: M.http = function(url, h) jerome@65: local c, of = "curl -s -S -A 'luacasc/1.1+curl'", M.tmpname() jerome@65: if type(h) == "table" then jerome@65: for k,v in pairs(h) do jerome@65: c = c .. ' -H ' .. shellEscape(k .. ": " .. v) jerome@65: end jerome@65: end jerome@65: c = c .. ' -o ' .. shellEscape(of) jerome@65: c = c .. ' ' .. shellEscape(url) jerome@65: if os.execute(c) == 0 then jerome@65: return readAndDeleteFile(of) jerome@65: end jerome@65: os.remove(of) jerome@65: end jerome@65: end jerome@65: jerome@65: return M