mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 09:46:53 -04:00
parent
08bb90faa3
commit
f2b5e01730
@ -4,15 +4,17 @@ local transfer = require("tools/transfer")
|
||||
local args, options = shell.parse(...)
|
||||
options.h = options.h or options.help
|
||||
if #args < 2 or options.h then
|
||||
io.write("Usage: cp [-inrv] <from...> <to>\n")
|
||||
io.write(" -i: prompt before overwrite (overrides -n option).\n")
|
||||
io.write(" -n: do not overwrite an existing file.\n")
|
||||
io.write(" -r: copy directories recursively.\n")
|
||||
io.write(" -u: copy only when the SOURCE file differs from the destination\n")
|
||||
io.write(" file or when the destination file is missing.\n")
|
||||
io.write(" -P: preserve attributes, e.g. symbolic links.\n")
|
||||
io.write(" -v: verbose output.\n")
|
||||
io.write(" -x: stay on original source file system.\n")
|
||||
io.write([[Usage: cp [OPTIONS] <from...> <to>
|
||||
-i: prompt before overwrite (overrides -n option).
|
||||
-n: do not overwrite an existing file.
|
||||
-r: copy directories recursively.
|
||||
-u: copy only when the SOURCE file differs from the destination
|
||||
file or when the destination file is missing.
|
||||
-P: preserve attributes, e.g. symbolic links.
|
||||
-v: verbose output.
|
||||
-x: stay on original source file system.
|
||||
--skip=P: skip files matching lua regex P
|
||||
]])
|
||||
return not not options.h
|
||||
end
|
||||
|
||||
@ -27,6 +29,7 @@ options =
|
||||
P = options.P,
|
||||
v = options.v,
|
||||
x = options.x,
|
||||
skip = options.skip,
|
||||
}
|
||||
|
||||
return transfer.batch(args, options)
|
||||
|
@ -13,21 +13,23 @@ do
|
||||
end
|
||||
|
||||
if not options then return end
|
||||
local write = io.write
|
||||
|
||||
if computer.freeMemory() < 50000 then
|
||||
write("Low memory, collecting garbage\n")
|
||||
print("Low memory, collecting garbage")
|
||||
for i=1,20 do os.sleep(0) end
|
||||
end
|
||||
|
||||
local cp, reason = loadfile(shell.resolve("cp", "lua"), "bt", _G)
|
||||
assert(cp, reason)
|
||||
|
||||
local ok, ec = pcall(cp, table.unpack(options.cp_args))
|
||||
assert(ok, ec)
|
||||
|
||||
local ec = cp(table.unpack(options.cp_args))
|
||||
if ec ~= nil and ec ~= 0 then
|
||||
return ec
|
||||
end
|
||||
|
||||
write("Installation complete!\n")
|
||||
print("Installation complete!")
|
||||
|
||||
if options.setlabel then
|
||||
pcall(options.target.dev.setLabel, options.label)
|
||||
@ -36,16 +38,16 @@ end
|
||||
if options.setboot then
|
||||
local address = options.target.dev.address
|
||||
if computer.setBootAddress(address) then
|
||||
write("Boot address set to " .. address)
|
||||
print("Boot address set to " .. address)
|
||||
end
|
||||
end
|
||||
|
||||
if options.reboot then
|
||||
write("Reboot now? [Y/n] ")
|
||||
io.write("Reboot now? [Y/n] ")
|
||||
if ((io.read() or "n").."y"):match("^%s*[Yy]") then
|
||||
write("\nRebooting now!\n")
|
||||
print("\nRebooting now!\n")
|
||||
computer.shutdown(true)
|
||||
end
|
||||
end
|
||||
|
||||
write("Returning to shell.\n")
|
||||
print("Returning to shell.\n")
|
||||
|
@ -4,12 +4,13 @@ local transfer = require("tools/transfer")
|
||||
local args, options = shell.parse(...)
|
||||
options.h = options.h or options.help
|
||||
if #args < 2 or options.h then
|
||||
io.write([[Usage: mv [-fiv] <from> <to>
|
||||
io.write([[Usage: mv [OPTIONS] <from> <to>
|
||||
-f overwrite without prompt
|
||||
-i prompt before overwriting
|
||||
unless -f
|
||||
-v verbose
|
||||
-n do not overwrite an existing file
|
||||
--skip=P ignore paths matching lua regex P
|
||||
-h, --help show this help
|
||||
]])
|
||||
return not not options.h
|
||||
@ -23,6 +24,7 @@ options =
|
||||
i = options.i,
|
||||
v = options.v,
|
||||
n = options.n, -- no clobber
|
||||
skip = options.skip,
|
||||
P = true, -- move operations always preserve
|
||||
r = true, -- move is allowed to move entire dirs
|
||||
x = true, -- cannot move mount points
|
||||
|
@ -78,22 +78,21 @@ function require(module)
|
||||
return loaded[module]
|
||||
elseif not loading[module] then
|
||||
loading[module] = true
|
||||
local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"}
|
||||
local loader, errorMsg = nil, {"module '" .. module .. "' not found:"}
|
||||
for i = 1, #package.searchers do
|
||||
-- the pcall is mostly for out of memory errors
|
||||
local ok, f, extra = pcall(package.searchers[i], module)
|
||||
local ok, f = pcall(package.searchers[i], module)
|
||||
if not ok then
|
||||
table.insert(errorMsg, "\t" .. (f or "nil"))
|
||||
elseif f and type(f) ~= "string" then
|
||||
loader = f
|
||||
value = extra
|
||||
break
|
||||
elseif f then
|
||||
table.insert(errorMsg, f)
|
||||
end
|
||||
end
|
||||
if loader then
|
||||
local success, result = pcall(loader, module, value)
|
||||
local success, result = pcall(loader, module)
|
||||
loading[module] = false
|
||||
if not success then
|
||||
error(result, 2)
|
||||
|
@ -1,5 +1,6 @@
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local text = require("text")
|
||||
local lib = {}
|
||||
|
||||
local function perr(ops, format, ...)
|
||||
@ -31,10 +32,10 @@ end
|
||||
|
||||
local function areEqual(path1, path2)
|
||||
local f1, f2 = fs.open(path1, "rb")
|
||||
local result = true
|
||||
if f1 then
|
||||
f2 = fs.open(path2, "rb")
|
||||
if f2 then
|
||||
local result = true
|
||||
local chunkSize = 4 * 1024
|
||||
repeat
|
||||
local s1, s2 = f1:read(chunkSize), f2:read(chunkSize)
|
||||
@ -86,10 +87,19 @@ local function stat(path, ops, P)
|
||||
end
|
||||
|
||||
function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
fromPath = fromPath:gsub("/+", "/")
|
||||
toPath = toPath:gsub("/+", "/")
|
||||
local fromPathFull = shell.resolve(fromPath)
|
||||
local toPathFull = shell.resolve(toPath)
|
||||
local mv = options.cmd == "mv"
|
||||
local verbose = options.v and (not mv or top)
|
||||
if select(2, fromPathFull:find(options.skip)) == #fromPathFull then
|
||||
status(verbose, string.format("skipping %s", fromPath))
|
||||
return true
|
||||
end
|
||||
local function release(result, reason)
|
||||
if result and mv and top then
|
||||
local rm_result = not fs.get(fromPath).isReadOnly() and fs.remove(fromPath)
|
||||
local rm_result = not fs.get(fromPathFull).isReadOnly() and fs.remove(fromPathFull)
|
||||
if not rm_result then
|
||||
perr(options, "cannot remove '%s': filesystem is readonly", fromPath)
|
||||
result = false
|
||||
@ -101,22 +111,22 @@ function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
local
|
||||
ok,
|
||||
fromReal,
|
||||
fromError,
|
||||
_, --fromError,
|
||||
fromIsLink,
|
||||
fromLinkTarget,
|
||||
fromExists,
|
||||
fromFs,
|
||||
fromIsDir = stat(fromPath, options, options.P)
|
||||
fromIsDir = stat(fromPathFull, options, options.P)
|
||||
if not ok then return nil end
|
||||
local
|
||||
ok,
|
||||
toReal,
|
||||
toError,
|
||||
_,--toError,
|
||||
toIsLink,
|
||||
toLinkTarget,
|
||||
_,--toLinkTarget,
|
||||
toExists,
|
||||
toFs,
|
||||
toIsDir = stat(toPath, options)
|
||||
toIsDir = stat(toPathFull, options)
|
||||
if not ok then os.exit(1) end
|
||||
if toFs.isReadOnly() then
|
||||
perr(options, "cannot create target '%s': filesystem is readonly", toPath)
|
||||
@ -124,9 +134,7 @@ function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
end
|
||||
|
||||
local same_path = fromReal == toReal
|
||||
local same_link = fromIsLink and toIsLink and same_path
|
||||
|
||||
local verbose = options.v
|
||||
local same_fs = fromFs == toFs
|
||||
local is_mount = origin[fromReal]
|
||||
|
||||
@ -138,12 +146,12 @@ function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
if toExists and options.n then
|
||||
return true
|
||||
end
|
||||
fs.remove(toPath)
|
||||
fs.remove(toPathFull)
|
||||
if toExists then
|
||||
status(verbose, string.format("removed '%s'\n", toPath))
|
||||
status(verbose, string.format("removed '%s'", toPath))
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
return release(fs.link(fromLinkTarget, toPath))
|
||||
return release(fs.link(fromLinkTarget, toPathFull))
|
||||
elseif fromIsDir then
|
||||
if not options.r then
|
||||
status(true, string.format("omitting directory '%s'", fromPath))
|
||||
@ -160,17 +168,20 @@ function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
if same_fs then
|
||||
if (toReal.."/"):find(fromReal.."/",1,true) then
|
||||
return nil, "cannot write a directory, '" .. fromPath .. "', into itself, '" .. toPath .. "'"
|
||||
elseif mv then
|
||||
status(verbose, fromPath, toPath)
|
||||
return os.rename(fromPath, toPath)
|
||||
end
|
||||
end
|
||||
if mv then
|
||||
if fs.list(toReal)() then -- to is NOT empty
|
||||
return nil, "cannot move '" .. fromPath .. "' to '" .. toPath .. "': Directory not empty"
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
end
|
||||
if not toExists then
|
||||
status(verbose, fromPath, toPath)
|
||||
fs.makeDirectory(toPath)
|
||||
fs.makeDirectory(toPathFull)
|
||||
end
|
||||
for file in fs.list(fromPath) do
|
||||
local result, reason = lib.recurse(fs.concat(fromPath, file), fs.concat(toPath, file), options, origin, false) -- false, no longer top
|
||||
for file in fs.list(fromPathFull) do
|
||||
local result, reason = lib.recurse(fromPath .."/".. file, toPath.."/"..file, options, origin, false) -- false, no longer top
|
||||
if not result then
|
||||
return false, reason
|
||||
end
|
||||
@ -198,7 +209,7 @@ function lib.recurse(fromPath, toPath, options, origin, top)
|
||||
fs.remove(toReal)
|
||||
end
|
||||
status(verbose, fromPath, toPath)
|
||||
return release(fs.copy(fromPath, toPath))
|
||||
return release(fs.copy(fromPathFull, toPathFull))
|
||||
else
|
||||
return nil, "'" .. fromPath .. "': No such file or directory"
|
||||
end
|
||||
@ -210,6 +221,7 @@ function lib.batch(args, options)
|
||||
-- standardized options
|
||||
options.i = options.i and not options.f
|
||||
options.P = options.P or options.r
|
||||
options.skip = text.escapeMagic(options.skip or "")
|
||||
|
||||
local origin = {}
|
||||
for dev,path in fs.mounts() do
|
||||
@ -217,27 +229,31 @@ function lib.batch(args, options)
|
||||
end
|
||||
|
||||
local toArg = table.remove(args)
|
||||
local _, toPath = contents_check(toArg, options)
|
||||
if not toPath then
|
||||
local _, ok = contents_check(toArg, options)
|
||||
if not ok then
|
||||
return 1
|
||||
end
|
||||
local originalToIsDir = fs.isDirectory(toPath)
|
||||
local originalToIsDir = fs.isDirectory(ok)
|
||||
|
||||
for _,arg in ipairs(args) do
|
||||
for _, fromArg in ipairs(args) do
|
||||
-- a "contents of" copy is where src path ends in . or ..
|
||||
-- a source path ending with . is not sufficient - could be the source filename
|
||||
local contents_of, fromPath = contents_check(arg, options, true)
|
||||
if fromPath then
|
||||
local contents_of
|
||||
contents_of, ok = contents_check(fromArg, options, true)
|
||||
if ok then
|
||||
-- we do not append fromPath name to toPath in case of contents_of copy
|
||||
local nextPath = toPath
|
||||
local toPath = toArg
|
||||
if contents_of and options.cmd == "mv" then
|
||||
perr(options, "invalid move path '%s'", arg)
|
||||
perr(options, "invalid move path '%s'", fromArg)
|
||||
else
|
||||
if not contents_of and originalToIsDir then
|
||||
nextPath = fs.concat(nextPath, fs.name(fromPath))
|
||||
local fromName = fs.name(fromArg)
|
||||
if fromName then
|
||||
toPath = toPath .. "/" .. fromName
|
||||
end
|
||||
end
|
||||
|
||||
local result, reason = lib.recurse(fromPath, nextPath, options, origin, true)
|
||||
local result, reason = lib.recurse(fromArg, toPath, options, origin, true)
|
||||
|
||||
if not result then
|
||||
perr(options, reason)
|
||||
|
@ -1,23 +1,11 @@
|
||||
local computer = require("computer")
|
||||
local shell = require("shell")
|
||||
local component = require("component")
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local unicode = require("unicode")
|
||||
local text = require("text")
|
||||
|
||||
local write = io.write
|
||||
|
||||
local args, options = shell.parse(...)
|
||||
|
||||
options.sources = {}
|
||||
options.targets = {}
|
||||
options.source_label = args[1]
|
||||
|
||||
local root_exception
|
||||
|
||||
if options.help then
|
||||
write([[Usage: install [OPTION]...
|
||||
io.write([[Usage: install [OPTION]...
|
||||
--from=ADDR install filesystem at ADDR
|
||||
default: builds list of
|
||||
candidates and prompts user
|
||||
@ -34,176 +22,161 @@ if options.help then
|
||||
return nil -- exit success
|
||||
end
|
||||
|
||||
local utils_path = "/opt/core/install_utils.lua"
|
||||
local utils
|
||||
|
||||
local rootfs = fs.get("/")
|
||||
if not rootfs then
|
||||
io.stderr:write("no root filesystem, aborting\n");
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function up_deprecate(old_key, new_key)
|
||||
if options[new_key] == nil then
|
||||
options[new_key] = options[old_key]
|
||||
end
|
||||
options[old_key] = nil
|
||||
end
|
||||
local label = args[1]
|
||||
options.label = label
|
||||
|
||||
local function cleanPath(path)
|
||||
if path then
|
||||
local rpath = shell.resolve(path)
|
||||
if fs.isDirectory(rpath) then
|
||||
return fs.canonical(rpath) .. '/'
|
||||
end
|
||||
local source_filter = options.from
|
||||
local source_filter_dev
|
||||
if source_filter then
|
||||
local from_path = shell.resolve(source_filter)
|
||||
if fs.isDirectory(from_path) then
|
||||
source_filter_dev = fs.get(from_path)
|
||||
source_filter = source_filter_dev.address
|
||||
options.from = from_path
|
||||
end
|
||||
end
|
||||
|
||||
local rootAddress = rootfs.address
|
||||
-- if the rootfs is read only, it is probably the loot disk!
|
||||
root_exception = rootAddress
|
||||
if rootfs.isReadOnly() then
|
||||
root_exception = nil
|
||||
end
|
||||
|
||||
-- this may be OpenOS specific, default to "" in case no /dev mount point
|
||||
local devfsAddress = (fs.get("/dev/") or {}).address or ""
|
||||
|
||||
-- tmp is only valid if specified as an option
|
||||
local tmpAddress = computer.tmpAddress()
|
||||
|
||||
----- For now, I am allowing source==target -- cp can handle it if the user prepares conditions correctly
|
||||
----- in other words, install doesn't need to filter this scenario:
|
||||
--if #options.targets == 1 and #options.sources == 1 and options.targets[1] == options.sources[1] then
|
||||
-- io.stderr:write("It is not the intent of install to use the same source and target filesystem.\n")
|
||||
-- return 1
|
||||
--end
|
||||
|
||||
------ load options
|
||||
up_deprecate('noboot', 'nosetboot')
|
||||
up_deprecate('nolabelset', 'nosetlabel')
|
||||
up_deprecate('name', 'label')
|
||||
|
||||
options.source_root = cleanPath(options.from)
|
||||
options.target_root = cleanPath(options.to)
|
||||
|
||||
options.target_dir = fs.canonical(options.root or options.toDir or "")
|
||||
|
||||
options.update = options.u or options.update
|
||||
|
||||
local function path_to_dev(path)
|
||||
return path and fs.isDirectory(path) and not fs.isLink(path) and fs.get(path)
|
||||
end
|
||||
|
||||
local source_dev = path_to_dev(options.source_root)
|
||||
local target_dev = path_to_dev(options.target_root)
|
||||
|
||||
-- use a single for loop of all filesystems to build the list of candidates of sources and targets
|
||||
local function validDevice(candidate, exceptions, specified, existing)
|
||||
local address = candidate.dev.address
|
||||
|
||||
for _,e in ipairs(existing) do
|
||||
if e.dev.address == address then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if specified then
|
||||
if address:find(specified, 1, true) == 1 then
|
||||
return true
|
||||
end
|
||||
else
|
||||
for _,e in ipairs(exceptions) do
|
||||
if e == address then
|
||||
return
|
||||
end
|
||||
end
|
||||
return true
|
||||
local target_filter = options.to
|
||||
local target_filter_dev
|
||||
if target_filter then
|
||||
local to_path = shell.resolve(target_filter)
|
||||
if fs.isDirectory(target_filter) then
|
||||
target_filter_dev = fs.get(to_path)
|
||||
target_filter = target_filter_dev.address
|
||||
options.to = to_path
|
||||
end
|
||||
end
|
||||
|
||||
local sources = {}
|
||||
local targets = {}
|
||||
|
||||
-- tmpfs is not a candidate unless it is specified
|
||||
|
||||
local devices = {}
|
||||
|
||||
for dev, path in fs.mounts() do
|
||||
local candidate = {dev=dev, path=path:gsub("/+$","")..'/'}
|
||||
devices[dev] = path
|
||||
end
|
||||
|
||||
if validDevice(candidate, {devfsAddress, tmpAddress, root_exception}, source_dev and source_dev.address or options.from, options.sources) then
|
||||
-- root path is either the command line path given for this dev or its natural mount point
|
||||
local root_path = source_dev == dev and options.source_root or path
|
||||
if (options.from or fs.list(root_path)()) then -- ignore empty sources unless specified
|
||||
local prop = fs.open(root_path .. '/.prop')
|
||||
if prop then
|
||||
local prop_data = prop:read(math.huge)
|
||||
prop:close()
|
||||
prop = prop_data
|
||||
end
|
||||
candidate.prop = prop and load('return ' .. prop)() or {}
|
||||
if not candidate.prop.ignore then
|
||||
if not options.source_label or options.source_label:lower() == (candidate.prop.label or (dev.getLabel() or "")):lower() then
|
||||
table.insert(options.sources, candidate)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
devices[fs.get("/dev/") or false] = nil
|
||||
local tmpAddress = computer.tmpAddress()
|
||||
|
||||
-- in case candidate is valid for BOTH, we want a new table
|
||||
candidate = {dev=candidate.dev, path=candidate.path} -- but not the prop
|
||||
for dev, path in pairs(devices) do
|
||||
local address = dev.address
|
||||
local install_path = dev == target_filter_dev and options.to or path
|
||||
local specified = target_filter and address:find(target_filter, 1, true) == 1
|
||||
|
||||
if validDevice(candidate, {devfsAddress, tmpAddress}, target_dev and target_dev.address or options.to, options.targets) then
|
||||
if not dev.isReadOnly() then
|
||||
table.insert(options.targets, candidate)
|
||||
elseif options.to then
|
||||
if dev.isReadOnly() then
|
||||
if specified then
|
||||
io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n")
|
||||
os.exit(1)
|
||||
end
|
||||
elseif specified or
|
||||
not target_filter and
|
||||
address ~= tmpAddress then
|
||||
table.insert(targets, {dev=dev, path=install_path, specified=specified})
|
||||
end
|
||||
end
|
||||
|
||||
local source = options.sources[1]
|
||||
local target = options.targets[1]
|
||||
local utils_path = "/opt/core/install_utils.lua"
|
||||
local target = targets[1]
|
||||
if #targets ~= 1 then
|
||||
utils = loadfile(utils_path, "bt", _G)
|
||||
target = utils("select", "targets", options, targets)
|
||||
end
|
||||
if not target then return end
|
||||
devices[target.dev] = nil
|
||||
|
||||
if #options.sources ~= 1 or #options.targets ~= 1 then
|
||||
source, target = loadfile(utils_path, "bt", _G)('select', options)
|
||||
for dev, path in pairs(devices) do
|
||||
local address = dev.address
|
||||
local install_path = dev == source_filter_dev and options.from or path
|
||||
local specified = source_filter and address:find(source_filter, 1, true) == 1
|
||||
|
||||
if fs.list(install_path)()
|
||||
and (specified or
|
||||
not source_filter and
|
||||
address ~= tmpAddress and
|
||||
not (address == rootfs.address and not rootfs.isReadOnly())) then
|
||||
local prop = {}
|
||||
local prop_path = path .. "/.prop"
|
||||
local prop_file = fs.open(prop_path)
|
||||
if prop_file then
|
||||
local prop_data = prop_file:read(math.huge)
|
||||
prop_file:close()
|
||||
local prop_load = load("return " .. prop_data)
|
||||
prop = prop_load and prop_load()
|
||||
if not prop then
|
||||
io.stderr:write("Ignoring " .. path .. " due to malformed prop file\n")
|
||||
prop = {ignore = true}
|
||||
end
|
||||
end
|
||||
if not prop.ignore then
|
||||
if not label or label:lower() == (prop.label or dev.getLabel() or ""):lower() then
|
||||
table.insert(sources, {dev=dev, path=install_path, prop=prop, specified=specified})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local source = sources[1]
|
||||
if #sources ~= 1 then
|
||||
utils = utils or loadfile(utils_path, "bt", _G)
|
||||
source = utils("select", "sources", options, sources)
|
||||
end
|
||||
if not source then return end
|
||||
options.source_root = options.source_root or source.path
|
||||
|
||||
if not target then return end
|
||||
options.target_root = options.target_root or target.path
|
||||
|
||||
-- now that source is selected, we can update options
|
||||
options.label = options.label or source.prop.label
|
||||
options.setlabel = source.prop.setlabel and not options.nosetlabel
|
||||
options.setboot = source.prop.setboot and not options.nosetboot
|
||||
options.reboot = source.prop.reboot and not options.noreboot
|
||||
options.source_dir = fs.canonical(source.prop.fromDir or options.fromDir or "") .. '/.'
|
||||
options =
|
||||
{
|
||||
from = source.path .. '/',
|
||||
to = target.path .. '/',
|
||||
fromDir = fs.canonical(options.fromDir or source.prop.fromDir or ""),
|
||||
root = fs.canonical(options.root or options.toDir or source.prop.root or ""),
|
||||
update = options.update or options.u,
|
||||
label = source.prop.label or label,
|
||||
setlabel = not (options.nosetlabel or options.nolabelset) and source.prop.setlabel,
|
||||
setboot = not (options.nosetboot or options.noboot) and source.prop.setboot,
|
||||
reboot = not options.noreboot and source.prop.reboot,
|
||||
}
|
||||
|
||||
local cp_args =
|
||||
{
|
||||
"-vrx" .. (options.update and "ui" or ""),
|
||||
options.source_root .. options.source_dir,
|
||||
options.target_root:gsub("//","/") .. options.target_dir
|
||||
"--skip=.prop",
|
||||
fs.concat(options.from, options.fromDir) .. "/.",
|
||||
fs.concat(options.to , options.root)
|
||||
}
|
||||
|
||||
local source_display = (source.prop or {}).label or source.dev.getLabel() or source.path
|
||||
local source_display = options.label or source.dev.getLabel() or source.path
|
||||
local special_target = ""
|
||||
if #options.targets > 1 or options.to then
|
||||
if #targets > 1 or target_filter then
|
||||
special_target = " to " .. cp_args[3]
|
||||
end
|
||||
|
||||
io.write("Install " .. source_display .. special_target .. "? [Y/n] ")
|
||||
if not ((io.read() or "n").."y"):match("^%s*[Yy]") then
|
||||
write("Installation cancelled\n")
|
||||
io.write("Installation cancelled\n")
|
||||
os.exit()
|
||||
end
|
||||
|
||||
local installer_path = options.source_root .. "/.install"
|
||||
local installer_path = options.from .. "/.install"
|
||||
if fs.exists(installer_path) then
|
||||
os.exit(loadfile(utils_path, "bt", _G)('install', options))
|
||||
local installer, reason = loadfile(installer_path, "bt", setmetatable({install=options}, {__index = _G}))
|
||||
if not installer then
|
||||
io.stderr:write("installer failed to load: " .. tostring(reason) .. '\n')
|
||||
os.exit(1)
|
||||
end
|
||||
os.exit(installer())
|
||||
end
|
||||
|
||||
return
|
||||
{
|
||||
setlabel = options.setlabel,
|
||||
label = options.label,
|
||||
setboot = options.setboot,
|
||||
reboot = options.reboot,
|
||||
target = target,
|
||||
cp_args = cp_args,
|
||||
}
|
||||
options.cp_args = cp_args
|
||||
options.target = target
|
||||
|
||||
return options
|
||||
|
@ -1,48 +1,51 @@
|
||||
local cmd, options = ...
|
||||
local cmd, arg, options, devices = ...
|
||||
|
||||
local function select_prompt(devs, prompt)
|
||||
table.sort(devs, function(a, b) return a.path<b.path end)
|
||||
local num_devs = #devs
|
||||
|
||||
if num_devs < 2 then
|
||||
return devs[1]
|
||||
end
|
||||
|
||||
local choice = devs[1]
|
||||
if #devs > 1 then
|
||||
io.write(prompt,'\n')
|
||||
|
||||
for i = 1, #devs do
|
||||
for i = 1, num_devs do
|
||||
local src = devs[i]
|
||||
local label = src.dev.getLabel()
|
||||
if label then
|
||||
label = label .. " (" .. src.dev.address:sub(1, 8) .. "...)"
|
||||
local dev = src.dev
|
||||
local selection_label = src.prop.label or dev.getLabel()
|
||||
if selection_label then
|
||||
selection_label = string.format("%s (%s...)", selection_label, dev.address:sub(1, 8))
|
||||
else
|
||||
label = src.dev.address
|
||||
selection_label = dev.address
|
||||
end
|
||||
io.write(i .. ") " .. label .. " at " .. src.path .. '\n')
|
||||
io.write(string.format("%d) %s at %s [r%s]\n", i, selection_label, src.path, dev.isReadOnly() and 'o' or 'w'))
|
||||
end
|
||||
|
||||
io.write("Please enter a number between 1 and " .. #devs .. '\n')
|
||||
io.write("Please enter a number between 1 and " .. num_devs .. '\n')
|
||||
io.write("Enter 'q' to cancel the installation: ")
|
||||
choice = nil
|
||||
while not choice do
|
||||
result = io.read() or "q"
|
||||
for _=1,5 do
|
||||
local result = io.read() or "q"
|
||||
if result == "q" then
|
||||
os.exit()
|
||||
end
|
||||
local number = tonumber(result)
|
||||
if number and number > 0 and number <= #devs then
|
||||
choice = devs[number]
|
||||
if number and number > 0 and number <= num_devs then
|
||||
return devs[number]
|
||||
else
|
||||
io.write("Invalid input, please try again: ")
|
||||
os.sleep(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return choice
|
||||
print("\ntoo many bad inputs, aborting")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
if cmd == 'select' then
|
||||
if #options.sources == 0 then
|
||||
if options.source_label then
|
||||
io.stderr:write("Nothing to install labeled: " .. options.source_label .. '\n')
|
||||
if cmd == "select" then
|
||||
if arg == "sources" then
|
||||
if #devices == 0 then
|
||||
if options.label then
|
||||
io.stderr:write("Nothing to install labeled: " .. options.label .. '\n')
|
||||
elseif options.from then
|
||||
io.stderr:write("Nothing to install from: " .. options.from .. '\n')
|
||||
else
|
||||
@ -50,8 +53,9 @@ if cmd == 'select' then
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
if #options.targets == 0 then
|
||||
return select_prompt(devices, "What do you want to install?")
|
||||
elseif arg == "targets" then
|
||||
if #devices == 0 then
|
||||
if options.to then
|
||||
io.stderr:write("No such target to install to: " .. options.to .. '\n')
|
||||
else
|
||||
@ -60,32 +64,6 @@ if cmd == 'select' then
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local source = select_prompt(options.sources, "What do you want to install?")
|
||||
if #options.sources > 1 and #options.targets > 1 then
|
||||
io.write('\n')
|
||||
end
|
||||
local target = select_prompt(options.targets, "Where do you want to install to?")
|
||||
|
||||
return source, target
|
||||
|
||||
elseif cmd == 'install' then
|
||||
local installer_path = options.source_root .. "/.install"
|
||||
local installer, reason = loadfile(installer_path, "bt", setmetatable({install=
|
||||
{
|
||||
from=options.source_root,
|
||||
to=options.target_root,
|
||||
fromDir=options.source_dir,
|
||||
root=options.target_dir,
|
||||
update=options.update,
|
||||
label=options.label,
|
||||
setlabel=options.setlabel,
|
||||
setboot=options.setboot,
|
||||
reboot=options.reboot,
|
||||
}}, {__index=_G}))
|
||||
if not installer then
|
||||
io.stderr:write("installer failed to load: " .. tostring(reason) .. '\n')
|
||||
os.exit(1)
|
||||
else
|
||||
return installer()
|
||||
return select_prompt(devices, "Where do you want to install to?")
|
||||
end
|
||||
end
|
@ -65,7 +65,7 @@ The following can override settings defined in .prop in the source filesystem.
|
||||
.install ENVIRONMENT
|
||||
A loot disc can optionally provide a custom installation script at the root of the source filesytem selected for installation. The script must be named ".install"
|
||||
When provided, the default install action is replaced by executation of this script. The default action is to copy all source files to the destination
|
||||
An _ENV.install table is set in the environment of '.install' when loaded
|
||||
A table of configuration options, named `install`, is provided in _ENV
|
||||
These are the keys and their descriptions of that table
|
||||
|
||||
_ENV.install.from:
|
||||
@ -76,7 +76,7 @@ The following can override settings defined in .prop in the source filesystem.
|
||||
This is the path of the selected target filesystem to install to.
|
||||
example: "/"
|
||||
|
||||
_ENV.install.fromdir
|
||||
_ENV.install.fromDir
|
||||
This is the relative path to use in the source filesystem as passed by command line to install. If unspecified to install it defaults to "."
|
||||
example: Perhaps the user executed `install --fromDir="bin"` with the intention that only files under /mnt/ABC/bin would be copied to their rootfs
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user