forked from zserge/luash
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sh.lua
151 lines (140 loc) · 3.52 KB
/
sh.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
local type, setmetatable, tostring, select, unpack = type, setmetatable, tostring, select, unpack
local ioo, iop, osrem = io.open, io.popen, os.remove
local tmpfile = os.tmpname()
local _EXIT = "exit"
local _SIGNAL = "signal"
local _TABLE = "table"
local _FUNC, _STR, _NUM, _BOOL = "function", "string", "number", "boolean"
local _TRIM = "^%s*(.-)%s*$"
local ignoreKeys = {
["__cmd"] = true,
["__input"] = true,
["__exitcode"] = true,
["__signal"] = true,
}
local function posixify(key, value)
if ignoreKeys[key] then return "" end
if type(key) == "function" then key = key(value) end
if type(key) ~= "string" or key == "" then return "" end
local t = type(value)
if t == _FUNC then
value = value(key)
t = type(value)
-- Silent return if the funcref returns nil.
if t == "nil" then return "" end
end
if key:sub(1, 1) ~= "_" then
if #key == 1 then key = " -" .. key
else
key = key:gsub("_", "-")
key = " --" .. key
end
end
if t == _TABLE then
local build = {}
local fmt = "%s='%s'"
for _, v in next, value do
if type(v) == _NUM or type(v) == _STR then
build[#build+1] = fmt:format(key, tostring(v))
end
end
return table.concat(build, " ")
elseif t == _STR then
-- Return --key='value'
if #value > 0 then return key .. "='" .. value .. "'" end
return "" -- 'value' is zero-length, so return nothing
elseif t == _NUM then
-- Return --key=value
return key .. "=" .. tostring(value)
elseif t == _BOOL then
if value == true then return key end
return ""
end
error("invalid argument type", t, value)
end
local process
process = function(n, s, input, ...)
for i = 1, n do
local a = (select(i, ...))
if type(a) == _TABLE then
if a.__input then
input = input .. " " .. a.__input
end
if #a ~= 0 then
process(#a, s, input, unpack(a))
else
for k, v in pairs(a) do s = s .. posixify(k, v) end
end
else
s = s .. " " .. tostring(a)
end
end
return s, input
end
local function run(cmd)
local p = iop(cmd, "r")
local output = p:read("*a")
local _, exit, status = p:close()
osrem(tmpfile)
return output, exit, status
end
local command
local invokedMt = {
__index = function(_, k) return command(k) end,
__tostring = function(self) return self.__input:match(_TRIM) end
}
local function invoke(cmd)
local output, exit, status = run(cmd)
-- If you add new keys here, add them to ignoreKeys
return setmetatable({
__cmd = cmd,
__input = output,
__exitcode = exit == _EXIT and status or 127,
__signal = exit == _SIGNAL and status or 0,
}, invokedMt)
end
local cmdMt = {
__call = function(self, ...)
local s = self.__cmd
local input
local n = select("#", ...)
if n ~= 0 then
local args, data = process(n, "", "", ...)
s = s .. args
input = data
end
if input and input ~= "" then
local f = ioo(tmpfile, "w")
f:write(input)
f:close()
s = s .. " <" .. tmpfile
end
return invoke(s)
end,
__tostring = function(self)
return (run(self.__cmd)):match(_TRIM)
end,
}
local cache = {}
command = function(cmd)
if not cache[cmd] then cache[cmd] = setmetatable({ __cmd = cmd }, cmdMt) end
return cache[cmd]
end
return setmetatable({
command = command,
_ = function(cmd, ...) return command(cmd)(...) end,
fork = "folknor",
version = 4,
}, {
__div = function(_, v) return command(v) end,
__mod = function(_, v) return (run(v)):match(_TRIM) end,
__call = function(_, ...)
local n = select("#", ...)
if n == 1 then return command(...) end
local ret = {}
for i = 1, n do
ret[#ret+1] = command((select(i, ...)))
end
return unpack(ret)
end
})