jerome@65
|
1 local M, assert, getenv = {}, assert, os.getenv
|
jerome@65
|
2
|
jerome@65
|
3 local function maybe(m)
|
jerome@65
|
4 local ok, v = pcall(require, m)
|
jerome@65
|
5 return ok and v
|
jerome@65
|
6 end
|
jerome@65
|
7 local lfs = maybe("lfs") -- LuaFileSystem; http://keplerproject.github.io/luafilesystem/
|
jerome@65
|
8 local zlib = maybe("zlib") -- lzlib; https://github.com/LuaDist/lzlib
|
jerome@65
|
9 local bit = maybe("bit") -- Lua BitOp; http://bitop.luajit.org
|
jerome@65
|
10 local socket = maybe("socket.http") -- LuaSocket; http://w3.impa.br/~diego/software/luasocket/home.html
|
jerome@65
|
11
|
jerome@65
|
12 local function shellEscape(s)
|
jerome@65
|
13 return '"' .. s:gsub('"', '"\\\\""') .. '"'
|
jerome@65
|
14 end
|
jerome@65
|
15 local function readAndDeleteFile(path)
|
jerome@65
|
16 local h, err = io.open(path, "rb")
|
jerome@65
|
17 if h then
|
jerome@65
|
18 local c = h:read("*a")
|
jerome@65
|
19 h:close()
|
jerome@65
|
20 h, err = c, nil
|
jerome@65
|
21 end
|
jerome@65
|
22 os.remove(path)
|
jerome@65
|
23 return h, err
|
jerome@65
|
24 end
|
jerome@65
|
25
|
jerome@65
|
26 local dir_sep = package and package.config and package.config:sub(1,1) or "/"
|
jerome@65
|
27 do -- M.path(a, b, ...)
|
jerome@65
|
28 M.path = function(a, b, ...)
|
jerome@65
|
29 if a and b then
|
jerome@65
|
30 return M.path(a .. (a:sub(-1) ~= dir_sep and dir_sep or "") .. b, ...)
|
jerome@65
|
31 end
|
jerome@65
|
32 return a
|
jerome@65
|
33 end
|
jerome@65
|
34 end
|
jerome@65
|
35 M.url = function(a, b, ...)
|
jerome@65
|
36 if a and b then
|
jerome@65
|
37 return M.url(a .. ((a:sub(-1) == "/" or b:sub(1,1) == "/") and "" or "/") .. b, ...)
|
jerome@65
|
38 end
|
jerome@65
|
39 return a
|
jerome@65
|
40 end
|
jerome@65
|
41
|
jerome@65
|
42 M.commands =
|
jerome@65
|
43 dir_sep == '/' and {toDevNull=' 2>/dev/null', ls='ls %s', mkdir='mkdir -p %s', gzip='gzip -dcq %s'} or
|
jerome@65
|
44 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
|
45
|
jerome@65
|
46 M.tmpname = function()
|
jerome@65
|
47 local tn = os.tmpname()
|
jerome@65
|
48 return (M.commands and M.commands.TMP or "") .. tn
|
jerome@65
|
49 end
|
jerome@65
|
50
|
jerome@65
|
51 M.decompress = zlib and zlib.decompress or function(compressed)
|
jerome@65
|
52 assert(type(compressed) == "string", 'Syntax: casc.platform.decompress("compressed")')
|
jerome@65
|
53 assert(M.commands and M.commands.gzip and M.commands.toDevNull, 'unsupported platform')
|
jerome@65
|
54
|
jerome@65
|
55 local f, f2 = M.tmpname(), M.tmpname()
|
jerome@65
|
56 local h = io.open(f, "wb")
|
jerome@65
|
57 h:write('\31\139\8\0\0\0\0\0')
|
jerome@65
|
58 h:write(compressed)
|
jerome@65
|
59 h:close()
|
jerome@65
|
60
|
jerome@65
|
61 os.execute(M.commands.gzip:format(shellEscape(f)) .. " 1>" .. f2 .. " " .. M.commands.toDevNull)
|
jerome@65
|
62 os.remove(f)
|
jerome@65
|
63
|
jerome@65
|
64 return readAndDeleteFile(f2)
|
jerome@65
|
65 end
|
jerome@65
|
66
|
jerome@65
|
67 M.mkdir = lfs and lfs.mkdir or function(path)
|
jerome@65
|
68 assert(type(path) == 'string', 'Syntax: casc.platform.mkdir("path")')
|
jerome@65
|
69 assert(M.commands and M.commands.mkdir, 'unsupported platform')
|
jerome@65
|
70
|
jerome@65
|
71 return os.execute(M.commands.mkdir:format(shellEscape(path)))
|
jerome@65
|
72 end
|
jerome@65
|
73
|
jerome@65
|
74 M.files = lfs and function(dir, glob)
|
jerome@65
|
75 assert(type(dir) == "string" and type(glob) == 'string', 'Syntax: casc.platform.files("dir", "glob")')
|
jerome@65
|
76 local pat = "^" .. glob:gsub("%.%-%+", "%%%0"):gsub("%*", ".*") .. "$"
|
jerome@65
|
77 local t, ni = {}, 1
|
jerome@65
|
78 for f in lfs.dir(dir) do
|
jerome@65
|
79 if f ~= "." and f ~= ".." and f:match(pat) then
|
jerome@65
|
80 t[ni], ni = M.path(dir, f), ni + 1
|
jerome@65
|
81 end
|
jerome@65
|
82 end
|
jerome@65
|
83 return pairs(t)
|
jerome@65
|
84 end or function(dir, glob)
|
jerome@65
|
85 assert(type(dir) == "string" and type(glob) == 'string', 'Syntax: casc.platform.files("dir", "glob")')
|
jerome@65
|
86 assert(M.commands and M.commands.ls, 'unsupported platform')
|
jerome@65
|
87
|
jerome@65
|
88 local dir, files = glob:match("^(.-)([^" .. dir_sep .. "]+)$")
|
jerome@65
|
89 local t, ni, h = {}, 1, io.popen(M.commands.ls:format(shellEscape(dir) .. files), "r")
|
jerome@65
|
90 for l in h:lines() do
|
jerome@65
|
91 t[ni], ni = l, ni + 1
|
jerome@65
|
92 end
|
jerome@65
|
93 h:close()
|
jerome@65
|
94 return pairs(t)
|
jerome@65
|
95 end
|
jerome@65
|
96
|
jerome@65
|
97 local floor = math.floor
|
jerome@65
|
98 M.rol = bit and bit.rol or function(n, b)
|
jerome@65
|
99 local n, e2 = n % 2^32, 2^(32-b)
|
jerome@65
|
100 return ((n % e2) * 2^b + floor(n/e2)) % 2^32
|
jerome@65
|
101 end
|
jerome@65
|
102
|
jerome@65
|
103 M.bxor = bit and bit.bxor or function(a, b)
|
jerome@65
|
104 local out, m, lm = 0, 1
|
jerome@65
|
105 for i=1,32 do
|
jerome@65
|
106 m, lm = m+m, m
|
jerome@65
|
107 local am, bm = a % m, b % m
|
jerome@65
|
108 out, a, b = out + (am == bm and 0 or lm), a-am, b-bm
|
jerome@65
|
109 if a == 0 or b == 0 then
|
jerome@65
|
110 return (out + b + a) % 2^32
|
jerome@65
|
111 end
|
jerome@65
|
112 end
|
jerome@65
|
113 return out
|
jerome@65
|
114 end
|
jerome@65
|
115
|
jerome@65
|
116 if socket then
|
jerome@65
|
117 socket.USERAGENT, socket.TIMEOUT = "lcasc/1.1", 5
|
jerome@65
|
118 local ltn12, RETRIES = require("ltn12"), 3
|
jerome@65
|
119 M.http = function(url, h)
|
jerome@65
|
120 for i=1,RETRIES do
|
jerome@65
|
121 local sink = {}
|
jerome@65
|
122 local ok, status, head = socket.request({url=url, sink=ltn12.sink.table(sink), headers=h})
|
jerome@65
|
123 if ok then
|
jerome@65
|
124 local cnt = table.concat(sink, "")
|
jerome@65
|
125 return status >= 200 and status < 300 and cnt, status, head, cnt
|
jerome@65
|
126 elseif i == RETRIES then
|
jerome@65
|
127 error("HTTP request failed: " .. tostring(status) .. "\nURL: " .. tostring(url))
|
jerome@65
|
128 end
|
jerome@65
|
129 end
|
jerome@65
|
130 end
|
jerome@65
|
131 else
|
jerome@65
|
132 M.http = function(url, h)
|
jerome@65
|
133 local c, of = "curl -s -S -A 'luacasc/1.1+curl'", M.tmpname()
|
jerome@65
|
134 if type(h) == "table" then
|
jerome@65
|
135 for k,v in pairs(h) do
|
jerome@65
|
136 c = c .. ' -H ' .. shellEscape(k .. ": " .. v)
|
jerome@65
|
137 end
|
jerome@65
|
138 end
|
jerome@65
|
139 c = c .. ' -o ' .. shellEscape(of)
|
jerome@65
|
140 c = c .. ' ' .. shellEscape(url)
|
jerome@65
|
141 if os.execute(c) == 0 then
|
jerome@65
|
142 return readAndDeleteFile(of)
|
jerome@65
|
143 end
|
jerome@65
|
144 os.remove(of)
|
jerome@65
|
145 end
|
jerome@65
|
146 end
|
jerome@65
|
147
|
jerome@65
|
148 return M |