summaryrefslogtreecommitdiff
path: root/src/luarocks/fs/unix.lua
diff options
context:
space:
mode:
authorMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
committerMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
commit5155816b7b925dec5d5feb1568b1d7ceb00938b9 (patch)
treedeca28ea15e79f6f804c3d90d2ba757881638af5 /src/luarocks/fs/unix.lua
fetch tarballHEADmaster
Diffstat (limited to 'src/luarocks/fs/unix.lua')
-rw-r--r--src/luarocks/fs/unix.lua266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua
new file mode 100644
index 0000000..41a9ba8
--- /dev/null
+++ b/src/luarocks/fs/unix.lua
@@ -0,0 +1,266 @@
+
+--- Unix implementation of filesystem and platform abstractions.
+local unix = {}
+
+local fs = require("luarocks.fs")
+
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+
+--- Annotate command string for quiet execution.
+-- @param cmd string: A command-line string.
+-- @return string: The command-line, with silencing annotation.
+function unix.quiet(cmd)
+ return cmd.." 1> /dev/null 2> /dev/null"
+end
+
+--- Annotate command string for execution with quiet stderr.
+-- @param cmd string: A command-line string.
+-- @return string: The command-line, with stderr silencing annotation.
+function unix.quiet_stderr(cmd)
+ return cmd.." 2> /dev/null"
+end
+
+--- Quote argument for shell processing.
+-- Adds single quotes and escapes.
+-- @param arg string: Unquoted argument.
+-- @return string: Quoted argument.
+function unix.Q(arg)
+ assert(type(arg) == "string")
+ return "'" .. arg:gsub("'", "'\\''") .. "'"
+end
+
+--- Return an absolute pathname from a potentially relative one.
+-- @param pathname string: pathname to convert.
+-- @param relative_to string or nil: path to prepend when making
+-- pathname absolute, or the current dir in the dir stack if
+-- not given.
+-- @return string: The pathname converted to absolute.
+function unix.absolute_name(pathname, relative_to)
+ assert(type(pathname) == "string")
+ assert(type(relative_to) == "string" or not relative_to)
+
+ local unquoted = pathname:match("^['\"](.*)['\"]$")
+ if unquoted then
+ pathname = unquoted
+ end
+
+ relative_to = relative_to or fs.current_dir()
+ if pathname:sub(1,1) == "/" then
+ return dir.normalize(pathname)
+ else
+ return dir.path(relative_to, pathname)
+ end
+end
+
+--- Return the root directory for the given path.
+-- In Unix, root is always "/".
+-- @param pathname string: pathname to use.
+-- @return string: The root of the given pathname.
+function unix.root_of(_)
+ return "/"
+end
+
+--- Create a wrapper to make a script executable from the command-line.
+-- @param script string: Pathname of script to be made executable.
+-- @param target string: wrapper target pathname (without wrapper suffix).
+-- @param name string: rock name to be used in loader context.
+-- @param version string: rock version to be used in loader context.
+-- @return boolean or (nil, string): True if succeeded, or nil and
+-- an error message.
+function unix.wrap_script(script, target, deps_mode, name, version, ...)
+ assert(type(script) == "string" or not script)
+ assert(type(target) == "string")
+ assert(type(deps_mode) == "string")
+ assert(type(name) == "string" or not name)
+ assert(type(version) == "string" or not version)
+
+ local wrapper = io.open(target, "w")
+ if not wrapper then
+ return nil, "Could not open "..target.." for writing."
+ end
+
+ local lpath, lcpath = path.package_paths(deps_mode)
+
+ local luainit = {
+ "package.path="..util.LQ(lpath..";").."..package.path",
+ "package.cpath="..util.LQ(lcpath..";").."..package.cpath",
+ }
+
+ local remove_interpreter = false
+ local base = dir.base_name(target):gsub("%..*$", "")
+ if base == "luarocks" or base == "luarocks-admin" then
+ if cfg.is_binary then
+ remove_interpreter = true
+ end
+ luainit = {
+ "package.path="..util.LQ(package.path),
+ "package.cpath="..util.LQ(package.cpath),
+ }
+ end
+
+ if name and version then
+ local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k " ..
+ "and l.add_context("..util.LQ(name)..","..util.LQ(version)..")"
+ table.insert(luainit, addctx)
+ end
+
+ local argv = {
+ fs.Q(cfg.variables["LUA"]),
+ "-e",
+ fs.Q(table.concat(luainit, ";")),
+ script and fs.Q(script) or [[$([ "$*" ] || echo -i)]],
+ ...
+ }
+ if remove_interpreter then
+ table.remove(argv, 1)
+ table.remove(argv, 1)
+ table.remove(argv, 1)
+ end
+
+ wrapper:write("#!/bin/sh\n\n")
+ wrapper:write("LUAROCKS_SYSCONFDIR="..fs.Q(cfg.sysconfdir) .. " ")
+ wrapper:write("exec "..table.concat(argv, " ")..' "$@"\n')
+ wrapper:close()
+
+ if fs.set_permissions(target, "exec", "all") then
+ return true
+ else
+ return nil, "Could not make "..target.." executable."
+ end
+end
+
+--- Check if a file (typically inside path.bin_dir) is an actual binary
+-- or a Lua wrapper.
+-- @param filename string: the file name with full path.
+-- @return boolean: returns true if file is an actual binary
+-- (or if it couldn't check) or false if it is a Lua wrapper.
+function unix.is_actual_binary(filename)
+ if filename:match("%.lua$") then
+ return false
+ end
+ local file = io.open(filename)
+ if not file then
+ return true
+ end
+ local first = file:read(2)
+ file:close()
+ if not first then
+ util.warning("could not read "..filename)
+ return true
+ end
+ return first ~= "#!"
+end
+
+function unix.copy_binary(filename, dest)
+ return fs.copy(filename, dest, "exec")
+end
+
+--- Move a file on top of the other.
+-- The new file ceases to exist under its original name,
+-- and takes over the name of the old file.
+-- On Unix this is done through a single rename operation.
+-- @param old_file The name of the original file,
+-- which will be the new name of new_file.
+-- @param new_file The name of the new file,
+-- which will replace old_file.
+-- @return boolean or (nil, string): True if succeeded, or nil and
+-- an error message.
+function unix.replace_file(old_file, new_file)
+ return os.rename(new_file, old_file)
+end
+
+function unix.tmpname()
+ return os.tmpname()
+end
+
+function unix.export_cmd(var, val)
+ return ("export %s='%s'"):format(var, val)
+end
+
+local octal_to_rwx = {
+ ["0"] = "---",
+ ["1"] = "--x",
+ ["2"] = "-w-",
+ ["3"] = "-wx",
+ ["4"] = "r--",
+ ["5"] = "r-x",
+ ["6"] = "rw-",
+ ["7"] = "rwx",
+}
+local rwx_to_octal = {}
+for octal, rwx in pairs(octal_to_rwx) do
+ rwx_to_octal[rwx] = octal
+end
+--- Moderate the given permissions based on the local umask
+-- @param perms string: permissions to moderate
+-- @return string: the moderated permissions
+local function apply_umask(perms)
+ local umask = fs._unix_umask()
+
+ local moderated_perms = ""
+ for i = 1, 3 do
+ local p_rwx = octal_to_rwx[perms:sub(i, i)]
+ local u_rwx = octal_to_rwx[umask:sub(i, i)]
+ local new_perm = ""
+ for j = 1, 3 do
+ local p_val = p_rwx:sub(j, j)
+ local u_val = u_rwx:sub(j, j)
+ if p_val == u_val then
+ new_perm = new_perm .. "-"
+ else
+ new_perm = new_perm .. p_val
+ end
+ end
+ moderated_perms = moderated_perms .. rwx_to_octal[new_perm]
+ end
+ return moderated_perms
+end
+
+function unix._unix_mode_scope_to_perms(mode, scope)
+ local perms
+ if mode == "read" and scope == "user" then
+ perms = apply_umask("600")
+ elseif mode == "exec" and scope == "user" then
+ perms = apply_umask("700")
+ elseif mode == "read" and scope == "all" then
+ perms = apply_umask("666")
+ elseif mode == "exec" and scope == "all" then
+ perms = apply_umask("777")
+ else
+ return false, "Invalid permission " .. mode .. " for " .. scope
+ end
+ return perms
+end
+
+function unix.system_cache_dir()
+ if fs.is_dir("/var/cache") then
+ return "/var/cache"
+ end
+ return dir.path(fs.system_temp_dir(), "cache")
+end
+
+function unix.search_in_path(program)
+ if program:match("/") then
+ local fd = io.open(dir.path(program), "r")
+ if fd then
+ fd:close()
+ return true, program
+ end
+
+ return false
+ end
+
+ for d in (os.getenv("PATH") or ""):gmatch("([^:]+)") do
+ local fd = io.open(dir.path(d, program), "r")
+ if fd then
+ fd:close()
+ return true, d
+ end
+ end
+ return false
+end
+
+return unix