local output_directory = ...
local execute, popen = os.execute, io.popen
local find, gsub, sub = string.find, string.gsub, string.sub
local function mode()
return status.shell_escape
end
local function words(cmd)
local out, cur, i, n, inword = {}, {}, 1, #cmd, false
while i <= n do
local c = sub(cmd, i, i)
if c == "'" or c == '"' then
local j = find(cmd, c, i + 1, true) or (n + 1)
cur[#cur + 1] = sub(cmd, i + 1, j - 1)
inword, i = true, j + 1
elseif find(c, "^%s$") then
if inword then out[#out + 1] = table.concat(cur); cur, inword = {}, false end
i = i + 1
else
cur[#cur + 1] = c
inword, i = true, i + 1
end
end
if inword then out[#out + 1] = table.concat(cur) end
return out
end
local function allowed(name)
local list = kpse.var_value("shell_escape_commands") or ""
for c in list:gmatch("[^,%s]+") do
if c == name then return true end
end
return false
end
local function restricted(cmd)
local w = words(cmd)
if #w == 0 or not allowed(w[1]) then return nil end
for i = 1, #w do w[i] = "'" .. gsub(w[i], "'", "'\\''") .. "'" end
return table.concat(w, " ")
end
local function with_output_directory(cmd)
local dir = output_directory()
if not dir then return cmd end
return "TEXMF_OUTPUT_DIRECTORY='" .. gsub(dir, "'", "'\\''") .. "' " .. cmd
end
local function wait_status(ok, how, code)
if how == "exit" then return code * 256 end
if how == "signal" then return code end
return ok and 0 or 256
end
function os.execute(cmd)
local m = mode()
if cmd == nil then return m end
if m == 0 then return nil, "All command execution disabled." end
if m == 2 then
cmd = restricted(cmd)
if not cmd then return nil, "Command execution disabled via shell_escape='p'" end
end
return wait_status(execute(with_output_directory(cmd)))
end
function io.popen(cmd, how)
local m = mode()
if m == 0 then return nil, "all command execution is disabled" end
if m == 2 then
cmd = restricted(cmd)
if not cmd then return nil, "specific command execution disabled" end
end
return popen(with_output_directory(cmd), how)
end