mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-15 10:21:45 -04:00
1.6 rc version 2
advanced install features as well as simplified install for common usage renamed .osprop to .prop support for named installed cp link copy fixes fix /bin/less pgdown and space package delay lookup no longer weak value fixed regression in sh tab complete for first arg completion
This commit is contained in:
parent
3e86a32783
commit
80e25f7be7
@ -1 +0,0 @@
|
|||||||
return {name = "OpenOS"}
|
|
@ -0,0 +1 @@
|
|||||||
|
return {label = "OpenOS", reboot=true, setlabel=true, setboot=true}
|
@ -64,8 +64,15 @@ end
|
|||||||
|
|
||||||
local function recurse(fromPath, toPath, origin)
|
local function recurse(fromPath, toPath, origin)
|
||||||
local isLink, target = fs.isLink(fromPath)
|
local isLink, target = fs.isLink(fromPath)
|
||||||
if isLink and options.P then
|
local toIsLink, toLinkTarget = fs.isLink(toPath)
|
||||||
|
local same_path = fs.canonical(isLink and target or fromPath) == fs.canonical(toIsLink and toLinkTarget or toPath)
|
||||||
|
local toExists = fs.exists(toPath)
|
||||||
|
|
||||||
|
if isLink and options.P and (not toExists or not same_path) then
|
||||||
status(fromPath, toPath)
|
status(fromPath, toPath)
|
||||||
|
if toIsLink then
|
||||||
|
fs.remove(toPath)
|
||||||
|
end
|
||||||
return fs.link(target, toPath)
|
return fs.link(target, toPath)
|
||||||
elseif fs.isDirectory(fromPath) then
|
elseif fs.isDirectory(fromPath) then
|
||||||
if not options.r then
|
if not options.r then
|
||||||
@ -95,35 +102,34 @@ local function recurse(fromPath, toPath, origin)
|
|||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
elseif fs.exists(fromPath) then
|
elseif fs.exists(fromPath) then
|
||||||
if fs.exists(toPath) then
|
if toExists then
|
||||||
if fs.canonical(fromPath) == fs.canonical(toPath) then
|
if same_path then
|
||||||
return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file"
|
return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file"
|
||||||
end
|
end
|
||||||
if fs.isDirectory(toPath) then
|
|
||||||
if options.i then
|
if options.n then
|
||||||
if not prompt("overwrite `" .. toPath .. "'?") then
|
return true
|
||||||
return true
|
|
||||||
end
|
|
||||||
elseif options.n then
|
|
||||||
return true
|
|
||||||
else -- yes, even for -f
|
|
||||||
return nil, "cannot overwrite directory `" .. toPath .. "' with non-directory"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if options.u then
|
|
||||||
if areEqual(fromPath, toPath) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if options.i then
|
|
||||||
if not prompt("overwrite `" .. toPath .. "'?") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
elseif options.n then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
-- else: default to overwriting
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- if target is link, we are updating the target
|
||||||
|
if toIsLink then
|
||||||
|
toPath = toLinkTarget
|
||||||
|
end
|
||||||
|
|
||||||
|
if options.u and not fs.isDirectory(toPath) and areEqual(fromPath, toPath) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if options.i then
|
||||||
|
if not prompt("overwrite `" .. toPath .. "'?") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.isDirectory(toPath) then
|
||||||
|
return nil, "cannot overwrite directory `" .. toPath .. "' with non-directory"
|
||||||
|
end
|
||||||
|
|
||||||
fs.remove(toPath)
|
fs.remove(toPath)
|
||||||
end
|
end
|
||||||
status(fromPath, toPath)
|
status(fromPath, toPath)
|
||||||
|
@ -7,12 +7,21 @@ local shell = require("shell")
|
|||||||
local tx = require("transforms")
|
local tx = require("transforms")
|
||||||
local text = require("text")
|
local text = require("text")
|
||||||
|
|
||||||
local args, options = shell.parse(...)
|
local lib = {}
|
||||||
|
|
||||||
local sources = {}
|
lib.args, lib.options = shell.parse(...)
|
||||||
local targets = {}
|
|
||||||
|
|
||||||
if options.help then
|
lib.sources = {}
|
||||||
|
lib.targets = {}
|
||||||
|
|
||||||
|
lib.source_label = lib.args[1]
|
||||||
|
|
||||||
|
lib.stdout = io.stdout
|
||||||
|
lib.stderr = io.stderr
|
||||||
|
lib.stdin = io.stdin
|
||||||
|
lib.exit = os.exit
|
||||||
|
|
||||||
|
if lib.options.help then
|
||||||
print([[Usage: install [OPTION]...
|
print([[Usage: install [OPTION]...
|
||||||
--from=ADDR install filesystem at ADDR
|
--from=ADDR install filesystem at ADDR
|
||||||
default: builds list of
|
default: builds list of
|
||||||
@ -22,25 +31,57 @@ if options.help then
|
|||||||
--root=PATH same as --fromDir but target
|
--root=PATH same as --fromDir but target
|
||||||
--toDir=PATH same as --root
|
--toDir=PATH same as --root
|
||||||
-u, --update update files interactively
|
-u, --update update files interactively
|
||||||
The following only pertain when .osprop exists
|
--label override label from .prop
|
||||||
--nolabelset do not label target
|
--nosetlabel do not label target
|
||||||
--name override label from .osprop
|
--nosetboot do not use target for boot
|
||||||
--noboot do not use target for boot
|
|
||||||
--noreboot do not reboot after install]])
|
--noreboot do not reboot after install]])
|
||||||
return nil -- exit success
|
return nil -- exit success
|
||||||
end
|
end
|
||||||
|
|
||||||
local rootfs = fs.get("/")
|
local rootfs = fs.get("/")
|
||||||
if not rootfs then
|
if not rootfs then
|
||||||
io.stderr:write("no root filesystem, aborting\n");
|
lib.stderr:write("no root filesystem, aborting\n");
|
||||||
return 1
|
lib.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function lib.up_deprecate(old_key, new_key)
|
||||||
|
if lib.options[new_key] == nil then
|
||||||
|
lib.options[new_key] = lib.options[old_key]
|
||||||
|
end
|
||||||
|
lib.options[old_key] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function lib.cleanPath(path)
|
||||||
|
if path then
|
||||||
|
local rpath = shell.resolve(path)
|
||||||
|
if fs.isDirectory(rpath) then
|
||||||
|
return fs.canonical(rpath):gsub("/+$", "") .. '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function lib.load_options()
|
||||||
|
lib.up_deprecate('noboot', 'nosetboot')
|
||||||
|
lib.up_deprecate('nolabelset', 'nosetlabel')
|
||||||
|
lib.up_deprecate('name', 'label')
|
||||||
|
|
||||||
|
lib.source_root = lib.cleanPath(lib.options.from)
|
||||||
|
lib.target_root = lib.cleanPath(lib.options.to)
|
||||||
|
|
||||||
|
lib.source_dir = (lib.options.fromDir or "") .. '/.'
|
||||||
|
lib.target_dir = (lib.options.root or lib.options.toDir or "") .. "/."
|
||||||
|
|
||||||
|
lib.update = lib.options.u or lib.options.update
|
||||||
|
|
||||||
|
lib.source_dev = lib.source_root and fs.get(lib.source_root)
|
||||||
|
lib.target_dev = lib.target_root and fs.get(lib.target_root)
|
||||||
end
|
end
|
||||||
|
|
||||||
local rootAddress = rootfs.address
|
local rootAddress = rootfs.address
|
||||||
-- if the rootfs is read only, it is probably the loot disk!
|
-- if the rootfs is read only, it is probably the loot disk!
|
||||||
local rootException = rootAddress
|
lib.rootException = rootAddress
|
||||||
if rootfs.isReadOnly() then
|
if rootfs.isReadOnly() then
|
||||||
rootException = nil
|
lib.rootException = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- this may be OpenOS specific, default to "" in case no /dev mount point
|
-- this may be OpenOS specific, default to "" in case no /dev mount point
|
||||||
@ -49,72 +90,96 @@ local devfsAddress = (fs.get("/dev/") or {}).address or ""
|
|||||||
-- tmp is only valid if specified as an option
|
-- tmp is only valid if specified as an option
|
||||||
local tmpAddress = computer.tmpAddress()
|
local tmpAddress = computer.tmpAddress()
|
||||||
|
|
||||||
local fromAddress = options.from
|
function lib.load(path, env)
|
||||||
local toAddress = options.to
|
if fs.exists(path) then
|
||||||
local fromDir = (options.fromDir or "") .. '/.'
|
local loader, reason = loadfile(path, "bt", setmetatable(env or {}, {__index=_G}))
|
||||||
local root = (options.root or options.toDir or "") .. "/."
|
if not loader then
|
||||||
options.update = options.u or options.update
|
return nil, reason
|
||||||
|
|
||||||
local function cleanPath(path)
|
|
||||||
if path then
|
|
||||||
local rpath = shell.resolve(path)
|
|
||||||
if fs.isDirectory(rpath) then
|
|
||||||
return fs.canonical(rpath):gsub("/+$", "") .. '/'
|
|
||||||
end
|
end
|
||||||
|
local ok, loaded = pcall(loader)
|
||||||
|
return ok and loaded, ok or loaded
|
||||||
end
|
end
|
||||||
return path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
fromAddress = cleanPath(fromAddress)
|
function lib.validDevice(candidate, exceptions, specified, existing)
|
||||||
toAddress = cleanPath(toAddress)
|
|
||||||
|
|
||||||
local function validDevice(candidate, exceptions, specified, existing)
|
|
||||||
local address = candidate.dev.address
|
local address = candidate.dev.address
|
||||||
|
|
||||||
if tx.first(existing, function(e) return e.dev.address == address end) then
|
if tx.first(existing, function(e) return e.dev.address == address end) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local path = candidate.path
|
|
||||||
if specified then
|
if specified then
|
||||||
return address:find(specified, 1, true) == 1 or specified == path
|
if type(specified) == "string" and address:find(specified, 1, true) == 1 or specified == candidate.dev then
|
||||||
|
return true
|
||||||
|
end
|
||||||
else
|
else
|
||||||
return not tx.find(exceptions, {address})
|
return not tx.find(exceptions, {address})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function lib.relevant(candidate, path)
|
||||||
|
if not path or fs.get(path) ~= candidate.dev then
|
||||||
|
return candidate.path
|
||||||
|
end
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
-- use a single for loop of all filesystems to build the list of candidates of sources and targets
|
-- use a single for loop of all filesystems to build the list of candidates of sources and targets
|
||||||
for dev, path in fs.mounts() do
|
function lib.load_candidates()
|
||||||
local candidate = {dev=dev, path=path}
|
for dev, path in fs.mounts() do
|
||||||
|
local candidate = {dev=dev, path=path:gsub("/+$","")..'/'}
|
||||||
|
|
||||||
if validDevice(candidate, {devfsAddress, tmpAddress, rootException}, fromAddress, sources) then
|
if lib.validDevice(candidate, {devfsAddress, tmpAddress, lib.rootException}, lib.source_dev or lib.options.from, lib.sources) then
|
||||||
if fromAddress or fs.list(path)() then
|
local root_path = lib.relevant(candidate, lib.source_root)
|
||||||
table.insert(sources, candidate)
|
if (lib.options.from or fs.list(root_path)()) then -- ignore empty sources unless specified
|
||||||
|
candidate.prop = lib.load(root_path .. "/.prop") or {}
|
||||||
|
if not lib.source_label or lib.source_label:lower() == (candidate.prop.label or candidate.dev.getLabel()):lower() then
|
||||||
|
table.insert(lib.sources, candidate)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- in case candidate is valid for BOTH, we want a new table
|
||||||
|
candidate = {dev=candidate.dev, path=candidate.path} -- but not the prop
|
||||||
|
|
||||||
|
if lib.validDevice(candidate, {devfsAddress, tmpAddress}, lib.target_dev or lib.options.to, lib.targets) then
|
||||||
|
if not dev.isReadOnly() then
|
||||||
|
table.insert(lib.targets, candidate)
|
||||||
|
elseif lib.options.to then
|
||||||
|
lib.stderr:write("Cannot install to " .. lib.options.to .. ", it is read only\n")
|
||||||
|
lib.exit(1)
|
||||||
|
return false -- in lib mode this can be hit
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if validDevice(candidate, {devfsAddress, tmpAddress}, toAddress, targets) then
|
return true
|
||||||
if not dev.isReadOnly() then
|
end
|
||||||
table.insert(targets, candidate)
|
|
||||||
elseif toAddress then
|
function lib.check_sources()
|
||||||
io.stderr:write("Cannot install to " .. toAddress .. ", it is read only\n")
|
if #lib.sources == 0 then
|
||||||
return 1
|
if lib.source_label then
|
||||||
|
lib.stderr:write("No filesystem to matched given label: " .. lib.source_label .. '\n')
|
||||||
|
elseif lib.options.from then
|
||||||
|
lib.stderr:write("No such filesystem to install from: " .. lib.options.from .. '\n')
|
||||||
|
else
|
||||||
|
lib.stderr:write("Could not find and available installations\n")
|
||||||
end
|
end
|
||||||
|
lib.exit(1)
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if fromAddress and #sources == 0 then
|
function lib.check_targets()
|
||||||
io.stderr:write("No such filesystem to install from: " .. fromAddress .. "\n")
|
if #lib.targets == 0 then
|
||||||
return 1
|
if lib.options.to then
|
||||||
end
|
lib.stderr:write("No such filesystem to install to: " .. lib.options.to .. '\n')
|
||||||
|
else
|
||||||
if #targets == 0 then
|
lib.stderr:write("No writable disks found, aborting\n")
|
||||||
if toAddress then
|
end
|
||||||
io.stderr:write("No such filesystem to install to: " .. toAddress .. "\n")
|
lib.exit(1)
|
||||||
else
|
|
||||||
io.stderr:write("No writable disks found, aborting\n")
|
|
||||||
end
|
end
|
||||||
return 1
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
----- For now, I am allowing source==target -- cp can handle it if the user prepares conditions correctly
|
----- For now, I am allowing source==target -- cp can handle it if the user prepares conditions correctly
|
||||||
@ -124,11 +189,12 @@ end
|
|||||||
-- return 1
|
-- return 1
|
||||||
--end
|
--end
|
||||||
|
|
||||||
local function prompt_select(devs, direction)
|
function lib.prompt_select(devs, direction)
|
||||||
|
table.sort(devs, function(a, b) return a.path<b.path end)
|
||||||
|
|
||||||
local choice = devs[1]
|
local choice = devs[1]
|
||||||
if #devs > 1 then
|
if #devs > 1 then
|
||||||
print("Select the device to install " .. direction)
|
lib.stdout:write("Select the device to install " .. direction .. '\n')
|
||||||
|
|
||||||
for i = 1, #devs do
|
for i = 1, #devs do
|
||||||
local src = devs[i]
|
local src = devs[i]
|
||||||
@ -138,118 +204,141 @@ local function prompt_select(devs, direction)
|
|||||||
else
|
else
|
||||||
label = src.dev.address
|
label = src.dev.address
|
||||||
end
|
end
|
||||||
print(i .. ") " .. label .. " at " .. src.path)
|
lib.stdout:write(i .. ") " .. label .. " at " .. src.path .. '\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Please enter a number between 1 and " .. #devs)
|
lib.stdout:write("Please enter a number between 1 and " .. #devs .. '\n')
|
||||||
io.write("Enter 'q' to cancel the installation: ")
|
lib.stdout:write("Enter 'q' to cancel the installation: ")
|
||||||
local choice
|
local choice
|
||||||
while not choice do
|
while not choice do
|
||||||
result = io.read()
|
result = lib.stdin:read()
|
||||||
if result:sub(1, 1):lower() == "q" then
|
if result:sub(1, 1):lower() == "q" then
|
||||||
os.exit()
|
lib.exit()
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
local number = tonumber(result)
|
local number = tonumber(result)
|
||||||
if number and number > 0 and number <= #devs then
|
if number and number > 0 and number <= #devs then
|
||||||
choice = devs[number]
|
choice = devs[number]
|
||||||
else
|
else
|
||||||
io.write("Invalid input, please try again: ")
|
lib.stdout:write("Invalid input, please try again: ")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
choice.display = (choice.path == '/' and "the root filesystem") or choice.dev.getLabel() or choice.path
|
-- normally it is helpful to call / the root filesystem
|
||||||
|
-- but if rootfs is readonly, then we know we are using rootfs as a source
|
||||||
if #devs == 1 then
|
-- in which case, it's label takes priority
|
||||||
print("Selecting " .. choice.display .. " (only option)")
|
choice.display =
|
||||||
end
|
not choice.dev.isReadOnly() and (choice.path == '/' and "the root filesystem") or
|
||||||
|
-- everything has props by this point, except for targets
|
||||||
|
(choice.prop or {}).label or
|
||||||
|
choice.dev.getLabel() or
|
||||||
|
choice.path
|
||||||
|
|
||||||
return choice
|
return choice
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(sources, function(a, b) return a.path<b.path end)
|
function lib.load_env()
|
||||||
table.sort(targets, function(a, b) return a.path<b.path end)
|
lib.env =
|
||||||
|
|
||||||
local source = prompt_select(sources, "from")
|
|
||||||
local target = prompt_select(targets, "to")
|
|
||||||
|
|
||||||
-- load .osprop (optional) settings
|
|
||||||
local osprop = nil
|
|
||||||
if fs.exists(source.path .. ".osprop") then
|
|
||||||
local osprop_data, reason = loadfile(source.path .. ".osprop", "bt", setmetatable({}, {__index=_G}))
|
|
||||||
if not osprop_data then
|
|
||||||
io.stderr:write("Failed to load .osprop: " .. tostring(reason) .. '\n')
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
osprop = osprop_data()
|
|
||||||
options.name = options.name or osprop.name
|
|
||||||
source.display = options.name or source.display
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if .lootprop exists
|
|
||||||
if fs.exists(source.path .. ".lootprop") then
|
|
||||||
local env = setmetatable(
|
|
||||||
{
|
{
|
||||||
lootprop =
|
from=lib.source_root,
|
||||||
{
|
to=lib.target_root,
|
||||||
from=source.path,
|
fromDir=lib.source_dir,
|
||||||
to=target.path,
|
root=lib.target_dir,
|
||||||
fromDir=fromDir,
|
update=lib.options.update,
|
||||||
root=root,
|
label=lib.options.label or lib.source.prop.label,
|
||||||
update=options.update,
|
setlabel=lib.source.prop.setlabel and not lib.options.nosetlabel,
|
||||||
nolabelset=options.nolabelset,
|
setboot=lib.source.prop.setboot and not lib.options.nosetboot,
|
||||||
name=options.name,
|
reboot=lib.source.prop.reboot and not lib.options.noreboot,
|
||||||
noboot=options.noboot,
|
}
|
||||||
noreboot=options.noreboot,
|
end
|
||||||
}
|
|
||||||
}, {__index=_G})
|
function lib.init()
|
||||||
local lootprop, reason = loadfile(source.path .. ".lootprop", "bt", env)
|
lib.load_options()
|
||||||
if not lootprop then
|
if not lib.load_candidates() then return false end
|
||||||
io.stderr:write("Failed to load .lootprop: " .. tostring(reason) .. '\n')
|
if not lib.check_sources() then return false end
|
||||||
return 1
|
if not lib.check_targets() then return false end
|
||||||
|
|
||||||
|
lib.source = lib.prompt_select(lib.sources, "from")
|
||||||
|
if not lib.source then return false end
|
||||||
|
lib.source_root = lib.source_root or lib.source.path
|
||||||
|
|
||||||
|
lib.target = lib.prompt_select(lib.targets, "to")
|
||||||
|
if not lib.target then return false end
|
||||||
|
lib.target_root = lib.target_root or lib.target.path
|
||||||
|
|
||||||
|
lib.load_env()
|
||||||
|
local reason
|
||||||
|
lib.installer, reason = lib.load(lib.source_root .. '/.install', {install=lib.env})
|
||||||
|
if not lib.installer then
|
||||||
|
if reason then
|
||||||
|
lib.stderr:write("installer failed to load: " .. tostring(reason) .. '\n')
|
||||||
|
lib.exit(1)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
lib.installer = lib.run
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return lootprop()
|
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local cp = shell.resolve("cp", "lua")
|
function lib.run()
|
||||||
local cp_options = "-vrx" .. (options.update and "ui" or "")
|
local cp = shell.resolve("cp", "lua")
|
||||||
local cp_source = (source.path .. fromDir):gsub("/+","/")
|
local cp_options = "-vrx" .. (lib.options.update and "ui" or "")
|
||||||
local cp_dest = (target.path .. root):gsub("/+","/")
|
local cp_source = (lib.source_root .. lib.source_dir):gsub("/+","/")
|
||||||
|
local cp_dest = (lib.target_root .. lib.target_dir):gsub("/+","/")
|
||||||
|
|
||||||
io.write("Install " .. source.display .. " to " .. target.display .. "? [Y/n] ")
|
io.write("Install " .. lib.source.display .. " to " .. lib.target.display .. "? [Y/n] ")
|
||||||
local choice = text.trim(io.read()):lower()
|
local choice = text.trim(lib.stdin:read()):lower()
|
||||||
if choice == "" then
|
if choice == "" then
|
||||||
choice = "y"
|
choice = "y"
|
||||||
end
|
|
||||||
if choice ~= "y" then
|
|
||||||
print("Installation cancelled")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local message = string.format("Installing %s [%s] to %s [%s]", source.display, cp_source, target.display, cp_dest)
|
|
||||||
local cmd = cp .. ' ' .. cp_options .. ' ' .. cp_source .. ' ' .. cp_dest
|
|
||||||
print(message)
|
|
||||||
print(cmd)
|
|
||||||
os.sleep(0.25)
|
|
||||||
|
|
||||||
local result, reason = os.execute(cmd)
|
|
||||||
|
|
||||||
if not result then
|
|
||||||
error(reason, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
if osprop then
|
|
||||||
if not options.nolabelset then
|
|
||||||
pcall(target.dev.setLabel, options.name)
|
|
||||||
end
|
end
|
||||||
if not options.noreboot then
|
if choice ~= "y" then
|
||||||
io.write("All done! " .. ((not options.noboot) and "Set as boot device and r" or "R") .. "eboot now? [Y/n] ")
|
lib.stdout:write("Installation cancelled\n")
|
||||||
local result = io.read()
|
lib.exit()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local message = string.format("Installing %s [%s] to %s [%s]", lib.source.display, cp_source, lib.target.display, cp_dest)
|
||||||
|
local cmd = cp .. ' ' .. cp_options .. ' ' .. cp_source .. ' ' .. cp_dest
|
||||||
|
lib.stdout:write(message .. '\n')
|
||||||
|
lib.stdout:write(cmd .. '\n')
|
||||||
|
os.sleep(0.25)
|
||||||
|
|
||||||
|
local result, reason = os.execute(cmd)
|
||||||
|
|
||||||
|
if not result then
|
||||||
|
error(reason, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.stdout:write("Installation complete!\n")
|
||||||
|
|
||||||
|
if lib.env.setlabel then
|
||||||
|
pcall(lib.target.dev.setLabel, lib.env.label)
|
||||||
|
end
|
||||||
|
|
||||||
|
local prereboot = function()end
|
||||||
|
if lib.env.setboot then
|
||||||
|
prereboot = computer.setBootAddress
|
||||||
|
end
|
||||||
|
|
||||||
|
if lib.env.reboot then
|
||||||
|
lib.stdout:write("Reboot now? [Y/n] ")
|
||||||
|
local result = lib.stdin:read()
|
||||||
if not result or result == "" or result:sub(1, 1):lower() == "y" then
|
if not result or result == "" or result:sub(1, 1):lower() == "y" then
|
||||||
if not options.noboot then computer.setBootAddress(target.dev.address)end
|
prereboot(lib.target.dev.address)
|
||||||
io.write("\nRebooting now!\n")
|
lib.stdout:write("\nRebooting now!\n")
|
||||||
computer.shutdown(true)
|
computer.shutdown(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
lib.stdout:write("Returning to shell.\n")
|
||||||
end
|
end
|
||||||
io.write("Returning to shell.\n")
|
|
||||||
|
if lib.options.lib then
|
||||||
|
return lib
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.init()
|
||||||
|
lib.run()
|
||||||
|
@ -277,7 +277,7 @@ while true do
|
|||||||
if code == keyboard.keys.q or code == keyboard.keys.d and keyboard.isControlDown() then
|
if code == keyboard.keys.q or code == keyboard.keys.d and keyboard.isControlDown() then
|
||||||
break
|
break
|
||||||
elseif code == keyboard.keys.space or code == keyboard.keys.pageDown then
|
elseif code == keyboard.keys.space or code == keyboard.keys.pageDown then
|
||||||
num = nil
|
num = max_display
|
||||||
elseif code == keyboard.keys.pageUp then
|
elseif code == keyboard.keys.pageUp then
|
||||||
num = -max_display
|
num = -max_display
|
||||||
elseif code == keyboard.keys.enter or code == keyboard.keys.down then
|
elseif code == keyboard.keys.enter or code == keyboard.keys.down then
|
||||||
|
@ -64,9 +64,8 @@ local delay_tools = setmetatable({},{__mode="v"})
|
|||||||
package.delay_data = delay_data
|
package.delay_data = delay_data
|
||||||
|
|
||||||
function delay_data.__index(tbl,key)
|
function delay_data.__index(tbl,key)
|
||||||
local lookup = delay_tools.lookup or loadfile("/lib/tools/delayLookup.lua")
|
delay_data.lookup = delay_data.lookup or loadfile("/lib/tools/delayLookup.lua")
|
||||||
delay_tools.lookup = lookup
|
return delay_data.lookup(delay_data, tbl, key)
|
||||||
return lookup(delay_data, tbl, key)
|
|
||||||
end
|
end
|
||||||
delay_data.__pairs = delay_data.__index -- nil key acts like pairs
|
delay_data.__pairs = delay_data.__index -- nil key acts like pairs
|
||||||
|
|
||||||
|
@ -642,7 +642,7 @@ function --[[@delayloaded-start@]] sh.internal.hintHandlerImpl(full_line, cursor
|
|||||||
local resultSuffix = suffix
|
local resultSuffix = suffix
|
||||||
if #result > 0 and unicode.sub(result[1], -1) ~= "/" and
|
if #result > 0 and unicode.sub(result[1], -1) ~= "/" and
|
||||||
not suffix:sub(1,1):find('%s') and
|
not suffix:sub(1,1):find('%s') and
|
||||||
(#result == 1 or cmd) then
|
#result == 1 or searchInPath then
|
||||||
resultSuffix = " " .. resultSuffix
|
resultSuffix = " " .. resultSuffix
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,20 +2,20 @@ NAME
|
|||||||
install - installs files from a source filesystem to a target filesystem
|
install - installs files from a source filesystem to a target filesystem
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
install [OPTIONS]...
|
install [name] [OPTIONS]...
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
install builds a list of candidate source and target mounted filesystems. If there are multiple candidates, the user is prompted for selections. By default, install copies all files from the source filesystem's root to the target filesystem's root path. If the source filesystem contains an .osprop, and unless command line options specify otherwise, the target filesystem's label is set and the user is prompted for reboot when install is complete. Alternatively, If a .lootprop file exists in the source filesystem, all default behavior is superceded and .lootprop is run as a script. .lootprop may copy files, set labels, prompt for reboot, etc. on its own. Developers creating their own .lootprop files for devices should respect environment variables set by install as per options it is given, such as ROOT. This manual page details the environment variables set by install when calling .lootprop.
|
install builds a list of candidate source and target mounted filesystems. If there are multiple candidates, the user is prompted for selections. By default, install copies all files from the source filesystem's root to the target filesystem's root path. The source filesystem can define label, boot, and reboot behavior via .prop and a fully custom install experience via .install which supercedes install running cp from source to target filesystems. Developers creating their own .install files for devices should respect environment variables set by install as per options it is given, such as the root path. This manual page details those environment variables.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
--from=ADDR
|
--from=ADDR
|
||||||
When searching for candidate source filesystems, if specified, only mounted filesystem device addresses or their mount point paths that match ADDR will be included. By default, all filesystems except the rootfs are valid sources for install. If the user is trying to install rootfs to another filesystem, --from=ADDR is required where ADDR matches the rootfs device address or --from=/
|
Specifies the source filesystem or its root path. ADDR can be the device guid or a directory path. If this is a directory path, it represents a root path to install from. This option can also be used to specify source paths that would otherwise be ignored, those being devfs, tmpfs, and the rootfs. e.g. --from=/tmp . Note that if both --from and a [name] is given, install expects the source path to have a .prop defining the same name. See .prop for more details.
|
||||||
|
|
||||||
--to=ADDR
|
--to=ADDR
|
||||||
same as --from but used when selecting target filesystem candidates. Note that the tmpfs is not a valid target filesystem by default, but must be specified explicitly if needed: i.e. --to=ADDR where ADDR matches the tmpfs device address or its mount point path, e.g. --to=/tmp . Note that install allows TO to equal FROM, also note that /bin/cp does not. But this detail may be noteworthy for .lootprop
|
Same as --from but specifies the target filesystem by guid or its root path. This option can also be used to specify filesystems that are otherwise ignored including tmpfs. i.e. --to=ADDR where ADDR matches the tmpfs device address or its mount point path. e.g. --to=/tmp
|
||||||
|
|
||||||
--fromDir=PATH
|
--fromDir=PATH
|
||||||
Install PATH from source. PATH is relative to the root of the source filesystem. The default is .
|
Install PATH from source. PATH is relative to the root of the source filesystem or path given by --from. The default is .
|
||||||
|
|
||||||
--root=PATH
|
--root=PATH
|
||||||
Same as --fromDir but for the target filesystem.
|
Same as --fromDir but for the target filesystem.
|
||||||
@ -26,56 +26,78 @@ OPTIONS
|
|||||||
-u, --update
|
-u, --update
|
||||||
Indicates that install should prompt the user before modifying files. This invokes -i and -u for /bin/cp.
|
Indicates that install should prompt the user before modifying files. This invokes -i and -u for /bin/cp.
|
||||||
|
|
||||||
The following only pertain when .osprop exists in the source filesystem. All environment variables are set for .lootprop regardless of the presense of .osprop
|
The following can override settings defined in .prop in the source filesystem.
|
||||||
|
|
||||||
--nolabelset
|
--label=NAME
|
||||||
do not set target label
|
use NAME for label instead of any value specified by .prop, --name is deprecated
|
||||||
|
|
||||||
--name=NAME
|
--nosetlabel
|
||||||
use NAME for label instead of any value specified by .osprop. This option is ignored if there is no .osprop (in which case, no label is set at all)
|
do not set target label. --nolabelset is deprecated
|
||||||
|
|
||||||
--noboot
|
--nosetboot
|
||||||
do not set target as default boot device when rebooting
|
do not set target as default boot device when rebooting. --noboot is deprecated
|
||||||
|
|
||||||
--noreboot
|
--noreboot
|
||||||
do not reboot after install
|
do not reboot after install
|
||||||
|
|
||||||
.lootprop ENVIRONMENT
|
.prop
|
||||||
When .lootprop is loaded and executed, a custom table is added to the environment: ENV_.lootprop
|
.prop should have valid lua code that returns a table of keys and their values: e.g. "return {name='OpenOS'}"
|
||||||
|
|
||||||
|
name=string
|
||||||
|
Declares an identifying name of the installation. This is displayed by install during source selection and also can be used on the commandline: e.g. (where {name="tape"} is given) `install tape`. If setlabel is true, this value is used for the target filesystem label. --name overrides this value. Note that install uses a case insensitive search: e.g. install TAPE works the same as install tape.
|
||||||
|
|
||||||
|
setlabel=boolean
|
||||||
|
Determines whether the install should set the target filesystem's label. If .prop does not define a name key and the user does not define a command line --name=VALUE, setlabel has no action. --nosetlabel overrides this value
|
||||||
|
|
||||||
|
setboot=boolean
|
||||||
|
Determines if the target filesystem should be set as the machine's default boot device. Default is false, overriden by --nosetboot
|
||||||
|
|
||||||
|
reboot=boolean
|
||||||
|
Determines if the machine should reboot after the install completes. Overriden by --noreboot
|
||||||
|
|
||||||
|
EXAMPLE:
|
||||||
|
return {name='OpenOS', setlabel=true, setboot=true, reboot=true}
|
||||||
|
|
||||||
|
.install ENVIRONMENT
|
||||||
|
When .install is loaded and executed, a custom table is added to the environment: ENV_.install
|
||||||
These are the keys of the table as populated by install
|
These are the keys of the table as populated by install
|
||||||
|
|
||||||
ENV_.lootprop.from:
|
ENV_.install.from:
|
||||||
This is the path of the selected source filesystem to install from. It should be the path to the executing .lootprop
|
This is the path of the selected source filesystem to install from. It should be the path to the executing .install
|
||||||
example: /mnt/ABC
|
example: /mnt/ABC
|
||||||
|
|
||||||
ENV_.lootprop.to:
|
ENV_.install.to:
|
||||||
This is the path of the selected target filesystem to install to.
|
This is the path of the selected target filesystem to install to.
|
||||||
example: /
|
example: /
|
||||||
|
|
||||||
_ENV.lootprop.fromdir
|
_ENV.install.fromdir
|
||||||
This is the relative path to use in the source filesysterm as passed by command line to install. If unspecified to install it defaults to "."
|
This is the relative path to use in the source filesysterm as passed by command line to install. If unspecified to install it defaults to "."
|
||||||
example: .
|
example: .
|
||||||
|
|
||||||
_ENV.lootprop.root
|
_ENV.install.root
|
||||||
This is the relative path to use in the target filesystem as passed by command line to install. If unspecified to install it defaults to "."
|
This is the relative path to use in the target filesystem as passed by command line to install. If unspecified to install it defaults to "."
|
||||||
example: .
|
example: .
|
||||||
|
|
||||||
_ENV.lootprop.update
|
_ENV.install.update
|
||||||
Assigned value of --update, see OPTIONS
|
Assigned value of --update, see OPTIONS
|
||||||
|
|
||||||
_ENV.lootprop.nolabelset
|
_ENV.install.label
|
||||||
Assigned value of --nolabelset, see OPTIONS
|
Assigned value of --name or .prop's label, see OPTIONS
|
||||||
|
|
||||||
_ENV.lootprop.name
|
_ENV.install.setlabel
|
||||||
Assigned value of --name, see OPTIONS
|
Assigned value of .prop's setlabel unless --nolabelset, see OPTIONS
|
||||||
|
|
||||||
_ENV.lootprop.noboot
|
_ENV.install.setboot
|
||||||
Assigned value of --noboot, see OPTIONS
|
Assigned value of .prop's boot unless --nosetboot, see OPTIONS
|
||||||
|
|
||||||
--noreboot
|
_ENV.install.reboot
|
||||||
Assigned value of --noreboot, see OPTIONS
|
Assigned value of .prop's reboot unless --noreboot, see OPTIONS
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
install
|
install
|
||||||
Searches all non rootfs filesystems to install from, and all non tmpfs filesystems to install to. Prompts the user for a selection, and copies. If .osprop is defined in source, sets label and will prompt for reboot when completed.
|
Searches all non rootfs filesystems to install from, and all non tmpfs filesystems to install to. Prompts the user for a selection, and copies. If .prop is defined in source, sets label and will prompt for reboot when completed.
|
||||||
|
|
||||||
|
install openos
|
||||||
|
Searches candidates source filesystems that have .prop's that define name="OpenOS" and prompts the user to confirm install to candidate target filesystems.
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user