From f2b5e01730f5bd10303b58ffd1bbb34ad452013f Mon Sep 17 00:00:00 2001 From: payonel Date: Mon, 22 May 2017 23:10:59 -0700 Subject: [PATCH] install and cp fixes (#2398) testing completed --- .../opencomputers/loot/openos/bin/cp.lua | 21 +- .../opencomputers/loot/openos/bin/install.lua | 18 +- .../opencomputers/loot/openos/bin/mv.lua | 4 +- .../opencomputers/loot/openos/lib/package.lua | 7 +- .../loot/openos/lib/tools/transfer.lua | 74 +++-- .../loot/openos/opt/core/install_basics.lua | 255 ++++++++---------- .../loot/openos/opt/core/install_utils.lua | 134 ++++----- .../opencomputers/loot/openos/usr/man/install | 4 +- 8 files changed, 245 insertions(+), 272 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua index a69e4c1a6..9b68c7190 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua @@ -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] \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] + -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) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua index ce99627e7..2358ada49 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua @@ -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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua index f3666605a..9567d6f64 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mv.lua @@ -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] + io.write([[Usage: mv [OPTIONS] -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua index 10670c257..19631f1ed 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua index b7607c296..3e3f4b725 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/transfer.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_basics.lua index 2dd26910f..7e4c21be6 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_basics.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_basics.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_utils.lua b/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_utils.lua index 60d39b004..557f6e521 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_utils.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/opt/core/install_utils.lua @@ -1,91 +1,69 @@ -local cmd, options = ... +local cmd, arg, options, devices = ... local function select_prompt(devs, prompt) table.sort(devs, function(a, b) return a.path 1 then - io.write(prompt,'\n') - - for i = 1, #devs do - local src = devs[i] - local label = src.dev.getLabel() - if label then - label = label .. " (" .. src.dev.address:sub(1, 8) .. "...)" - else - label = src.dev.address - end - io.write(i .. ") " .. label .. " at " .. src.path .. '\n') - end - - io.write("Please enter a number between 1 and " .. #devs .. '\n') - io.write("Enter 'q' to cancel the installation: ") - choice = nil - while not choice do - 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] - else - io.write("Invalid input, please try again: ") - os.sleep(0) - end - end + if num_devs < 2 then + return devs[1] end - return choice + io.write(prompt,'\n') + + for i = 1, num_devs do + local src = devs[i] + 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 + selection_label = dev.address + end + 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 " .. num_devs .. '\n') + io.write("Enter 'q' to cancel the installation: ") + 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 <= num_devs then + return devs[number] + else + io.write("Invalid input, please try again: ") + os.sleep(0) + end + end + 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') - elseif options.from then - io.stderr:write("Nothing to install from: " .. options.from .. '\n') - else - io.stderr:write("Nothing to install\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 + io.stderr:write("Nothing to install\n") + end + os.exit(1) end - os.exit(1) - end - - if #options.targets == 0 then - if options.to then - io.stderr:write("No such target to install to: " .. options.to .. '\n') - else - io.stderr:write("No writable disks found, aborting\n") + 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 + io.stderr:write("No writable disks found, aborting\n") + end + os.exit(1) end - 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 \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/install b/src/main/resources/assets/opencomputers/loot/openos/usr/man/install index e1b60ed79..aece8f663 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/install +++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/install @@ -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