Files
2026-08-06 12:54:48 -04:00

125 lines
3.7 KiB
Lua

-- path.lua -- minimal self-contained filesystem helpers for zeta-makepkg.
--
-- Deliberately avoids any external dependencies so the toolchain can run
-- without Zeta installed. All subprocess calls use plain os.execute.
local path = {}
---------------------------------------------------------------------------
-- String helpers
---------------------------------------------------------------------------
-- Join path components with a single separator. The first non-empty
-- component decides whether the result is absolute; later components are
-- treated as plain name pieces even if they carry a leading slash.
function path.join(...)
local parts = {}
local abs = false
local first = true
for i = 1, select("#", ...) do
local part = tostring((select(i, ...)))
if part == "/" then
if first then abs = true end
elseif part ~= "" then
local p = part:gsub("^/+", ""):gsub("/+$", "")
if p ~= "" then
if first then abs = (part:match("^/") ~= nil) end
parts[#parts + 1] = p
end
end
first = false
end
if #parts == 0 then return "/" end
local out = table.concat(parts, "/")
if abs then out = "/" .. out end
return out
end
function path.is_abs(p)
return p:match("^/") ~= nil
end
function path.basename(p)
p = p:gsub("/+$", "")
return p:match("[^/]+$") or p
end
function path.dirname(p)
p = p:gsub("/+$", "")
if p == "" then return "." end
local d = p:match("^(.*)/[^/]+$")
if not d then return "." end
if d == "" then return "/" end
return d
end
-- Shell-quote a value for safe use in a command. Values containing only
-- "safe" characters are returned as-is; everything else is single-quoted.
function path.quote(s)
if s == "" then return "''" end
if s:match("^[%w%._%/+%-:=@,]+$") then return s end
return "'" .. s:gsub("'", "'\\''") .. "'"
end
---------------------------------------------------------------------------
-- Filesystem helpers
---------------------------------------------------------------------------
-- Run a shell command, streaming output to the parent. Returns true on
-- exit status 0. Compatible with Lua 5.1 through 5.4.
function path.run(cmd)
local a, b, c = os.execute(cmd)
if type(a) == "number" then return a == 0 end
return a == true and b == "exit" and c == 0
end
-- mkdir -p wrapper.
function path.mkdir_p(dir)
if dir == "" or dir == "/" then return true end
return path.run("mkdir -p " .. path.quote(dir))
end
-- True if a file or directory (or dangling symlink) exists.
function path.exists(p)
local f = io.open(p, "rb")
if f then
f:close()
return true
end
return path.run("test -e " .. path.quote(p) .. " -o -L " .. path.quote(p))
end
-- Package names must be tightly constrained (alphanumeric, dots, underscores,
-- plus, hyphen). No slashes, no leading dots, no other special characters.
function path.sanitize_name(name)
if type(name) ~= "string" then return nil end
if name == "" then return nil end
if name:match("^%.") then return nil end
if not name:match("^[%w%._+%-]+$") then return nil end
return name
end
---------------------------------------------------------------------------
-- Download / tool detection
---------------------------------------------------------------------------
-- True if the given command is available on PATH.
function path.have(cmd)
local f = io.popen("command -v " .. cmd .. " 2>/dev/null")
if not f then return false end
local out = f:read("*l")
f:close()
return out ~= nil and out ~= ""
end
-- Capture stdout of a command. Returns output string or nil on failure.
function path.capture(cmd)
local f = io.popen(cmd .. " 2>/dev/null")
if not f then return nil end
local out = f:read("*a")
f:close()
return out
end
return path