mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-19 04:06:43 -04:00
Merge branch 'master-MC1.8.9' of github.com:MightyPirates/OpenComputers into master-MC1.9.4
This commit is contained in:
commit
3e55ca2de6
@ -27,6 +27,7 @@
|
||||
- `getInput(index:number)` - Запрос текущего состояния контакта с указанным индексом.
|
||||
- `setInput(index:number, value:boolean)` - Устанавливает указанный контакт в указанное состояние.
|
||||
- `getActiveEffects()` - Запрос списка активных эффектов. Некоторые эффекты могут быть не показаны в этом списке.
|
||||
- `saveConfiguration()` - Требует наличия в инвентаре нанороботов, сохраняет текущую конфигурацию нанороботов игрока в них.
|
||||
|
||||
Например, в OpenOS:
|
||||
- `component.modem.broadcast(1, "nanomachines", "setInput", 1, true)` активирует первый контакт.
|
||||
|
@ -1,31 +0,0 @@
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local process = require("process")
|
||||
|
||||
local proxy = ...
|
||||
|
||||
-- Install symlinks if they don't already exist.
|
||||
local links = {}
|
||||
local fsroot = fs.path(process.running())
|
||||
local function inject(path)
|
||||
for file in fs.list(fs.concat(fsroot, path)) do
|
||||
local source = fs.concat(fsroot, path, file)
|
||||
local target = fs.concat(path, file)
|
||||
if fs.link(source, target) then
|
||||
table.insert(links, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
inject("lib")
|
||||
inject("bin")
|
||||
inject("usr/man")
|
||||
|
||||
-- Delete symlinks on removal.
|
||||
event.listen("component_removed", function(_, address)
|
||||
if address == proxy.address then
|
||||
for _, link in ipairs(links) do
|
||||
fs.remove(link)
|
||||
end
|
||||
return false -- remove listener
|
||||
end
|
||||
end)
|
@ -1,31 +0,0 @@
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local process = require("process")
|
||||
|
||||
local proxy = ...
|
||||
|
||||
-- Install symlinks if they don't already exist.
|
||||
local links = {}
|
||||
local fsroot = fs.path(process.running())
|
||||
local function inject(path)
|
||||
for file in fs.list(fs.concat(fsroot, path)) do
|
||||
local source = fs.concat(fsroot, path, file)
|
||||
local target = fs.concat(path, file)
|
||||
if fs.link(source, target) then
|
||||
table.insert(links, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
inject("lib")
|
||||
inject("bin")
|
||||
inject("usr/man")
|
||||
|
||||
-- Delete symlinks on removal.
|
||||
event.listen("component_removed", function(_, address)
|
||||
if address == proxy.address then
|
||||
for _, link in ipairs(links) do
|
||||
fs.remove(link)
|
||||
end
|
||||
return false -- remove listener
|
||||
end
|
||||
end)
|
@ -1,31 +0,0 @@
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local process = require("process")
|
||||
|
||||
local proxy = ...
|
||||
|
||||
-- Install symlinks if they don't already exist.
|
||||
local links = {}
|
||||
local fsroot = fs.path(process.running())
|
||||
local function inject(path)
|
||||
for file in fs.list(fs.concat(fsroot, path)) do
|
||||
local source = fs.concat(fsroot, path, file)
|
||||
local target = fs.concat(path, file)
|
||||
if fs.link(source, target) then
|
||||
table.insert(links, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
inject("lib")
|
||||
inject("bin")
|
||||
inject("usr/man")
|
||||
|
||||
-- Delete symlinks on removal.
|
||||
event.listen("component_removed", function(_, address)
|
||||
if address == proxy.address then
|
||||
for _, link in ipairs(links) do
|
||||
fs.remove(link)
|
||||
end
|
||||
return false -- remove listener
|
||||
end
|
||||
end)
|
@ -1 +0,0 @@
|
||||
return {name = "OpenOS"}
|
@ -0,0 +1 @@
|
||||
{label = "OpenOS", reboot=true, setlabel=true, setboot=true}
|
@ -4,32 +4,27 @@ local fs = require("filesystem")
|
||||
local args = shell.parse(...)
|
||||
local ec = 0
|
||||
if #args == 0 then
|
||||
repeat
|
||||
local read = io.read("*L")
|
||||
if read then
|
||||
io.write(read)
|
||||
end
|
||||
until not read
|
||||
else
|
||||
for i = 1, #args do
|
||||
local arg = args[i]
|
||||
if fs.isDirectory(arg) then
|
||||
io.stderr:write(string.format('cat %s: Is a directory\n', arg))
|
||||
args = {"-"}
|
||||
end
|
||||
|
||||
for i = 1, #args do
|
||||
local arg = args[i]
|
||||
if fs.isDirectory(arg) then
|
||||
io.stderr:write(string.format('cat %s: Is a directory\n', arg))
|
||||
ec = 1
|
||||
else
|
||||
local file, reason = args[i] == "-" and io.stdin or io.open(shell.resolve(args[i]))
|
||||
if not file then
|
||||
io.stderr:write(string.format("cat: %s: %s\n",args[i],tostring(reason)))
|
||||
ec = 1
|
||||
else
|
||||
local file, reason = args[i] == "-" and io.stdin or io.open(shell.resolve(args[i]))
|
||||
if not file then
|
||||
io.stderr:write(string.format("cat: %s: %s\n",args[i],tostring(reason)))
|
||||
ec = 1
|
||||
else
|
||||
repeat
|
||||
local line = file:read("*L")
|
||||
if line then
|
||||
io.write(line)
|
||||
end
|
||||
until not line
|
||||
file:close()
|
||||
end
|
||||
repeat
|
||||
local line = file:read("*L")
|
||||
if line then
|
||||
io.write(line)
|
||||
end
|
||||
until not line
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local computer = require("computer")
|
||||
|
||||
local args, options = shell.parse(...)
|
||||
if #args < 2 then
|
||||
@ -15,19 +16,20 @@ if #args < 2 then
|
||||
return 1
|
||||
end
|
||||
|
||||
local exit_code = nil
|
||||
options.P = options.P or options.r
|
||||
|
||||
local from = {}
|
||||
for i = 1, #args - 1 do
|
||||
table.insert(from, shell.resolve(args[i]))
|
||||
end
|
||||
local to = shell.resolve(args[#args])
|
||||
-- interrupting is important, but not EVERY copy
|
||||
local greedy = computer.uptime()
|
||||
|
||||
local function status(from, to)
|
||||
if options.v then
|
||||
io.write(from .. " -> " .. to .. "\n")
|
||||
end
|
||||
os.sleep(0) -- allow interrupting
|
||||
if computer.uptime() - greedy > 4 then
|
||||
os.sleep(0) -- allow interrupting
|
||||
greedy = computer.uptime()
|
||||
end
|
||||
end
|
||||
|
||||
local result, reason
|
||||
@ -68,14 +70,25 @@ for dev,path in fs.mounts() do
|
||||
end
|
||||
|
||||
local function recurse(fromPath, toPath, origin)
|
||||
status(fromPath, toPath)
|
||||
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
|
||||
if toExists and options.n then
|
||||
return true
|
||||
end
|
||||
fs.remove(toPath)
|
||||
if toExists and options.v then
|
||||
io.write(string.format("removed '%s'\n", toPath))
|
||||
end
|
||||
status(fromPath, toPath)
|
||||
return fs.link(target, toPath)
|
||||
end
|
||||
if fs.isDirectory(fromPath) then
|
||||
elseif fs.isDirectory(fromPath) then
|
||||
if not options.r then
|
||||
io.write("omitting directory `" .. fromPath .. "'\n")
|
||||
exit_code = 1
|
||||
return true
|
||||
end
|
||||
if fs.exists(toPath) and not fs.isDirectory(toPath) then
|
||||
@ -85,10 +98,13 @@ local function recurse(fromPath, toPath, origin)
|
||||
if options.x and origin and mounts[fs.canonical(fromPath)] then
|
||||
return true
|
||||
end
|
||||
if fs.get(fromPath) == fs.get(toPath) and fs.canonical(fs.path(toPath)):find(fs.canonical(fromPath),1,true) then
|
||||
if fs.get(fromPath) == fs.get(toPath) and fs.canonical(toPath):find(fs.canonical(fromPath),1,true) then
|
||||
return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'"
|
||||
end
|
||||
fs.makeDirectory(toPath)
|
||||
if not fs.exists(toPath) then
|
||||
status(fromPath, toPath)
|
||||
fs.makeDirectory(toPath)
|
||||
end
|
||||
for file in fs.list(fromPath) do
|
||||
local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file), origin or fs.get(fromPath))
|
||||
if not result then
|
||||
@ -96,44 +112,53 @@ local function recurse(fromPath, toPath, origin)
|
||||
end
|
||||
end
|
||||
return true
|
||||
else
|
||||
if fs.exists(toPath) then
|
||||
if fs.canonical(fromPath) == fs.canonical(toPath) then
|
||||
elseif fs.exists(fromPath) then
|
||||
if toExists then
|
||||
if same_path then
|
||||
return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file"
|
||||
end
|
||||
if fs.isDirectory(toPath) then
|
||||
if options.i then
|
||||
if not prompt("overwrite `" .. toPath .. "'?") then
|
||||
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
|
||||
|
||||
if options.n then
|
||||
return true
|
||||
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)
|
||||
end
|
||||
status(fromPath, toPath)
|
||||
return fs.copy(fromPath, toPath)
|
||||
else
|
||||
return nil, "`" .. fromPath .. "': No such file or directory"
|
||||
end
|
||||
end
|
||||
for _, fromPath in ipairs(from) do
|
||||
|
||||
local to = shell.resolve(args[#args])
|
||||
|
||||
for i = 1, #args - 1 do
|
||||
local fromPath, cuts = args[i]:gsub("(/%.%.?)$", "%1")
|
||||
fromPath = shell.resolve(fromPath)
|
||||
local toPath = to
|
||||
if fs.isDirectory(toPath) then
|
||||
-- fromPath ending with /. indicates copying the contents of fromPath
|
||||
-- in which case (cuts>0) we do not append fromPath name to toPath
|
||||
if cuts == 0 and fs.isDirectory(toPath) then
|
||||
toPath = fs.concat(toPath, fs.name(fromPath))
|
||||
end
|
||||
result, reason = recurse(fromPath, toPath)
|
||||
@ -144,3 +169,5 @@ for _, fromPath in ipairs(from) do
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
return exit_code
|
||||
|
@ -27,7 +27,7 @@ if #args == 0 then
|
||||
end
|
||||
else
|
||||
for i = 1, #args do
|
||||
local proxy, path = fs.get(args[i])
|
||||
local proxy, path = fs.get(shell.resolve(args[i]))
|
||||
if not proxy then
|
||||
io.stderr:write(args[i], ": no such file or directory\n")
|
||||
else
|
||||
|
@ -110,7 +110,7 @@ for i=1,#args do
|
||||
local file
|
||||
if arg == '-' then
|
||||
arg = 'standard input'
|
||||
file = setmetatable({close=function()end},{__index=io.stdin})
|
||||
file = io.stdin
|
||||
else
|
||||
file, reason = io.open(arg, 'r')
|
||||
if not file then
|
||||
@ -119,7 +119,7 @@ for i=1,#args do
|
||||
end
|
||||
if file then
|
||||
if verbose or #args > 1 then
|
||||
io.write(string.format('==> %s <==', arg))
|
||||
io.write(string.format('==> %s <==\n', arg))
|
||||
end
|
||||
|
||||
local stream = new_stream()
|
||||
|
@ -1,80 +1,44 @@
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local event = require("event")
|
||||
local filesystem = require("filesystem")
|
||||
local unicode = require("unicode")
|
||||
local shell = require("shell")
|
||||
|
||||
local args, options = shell.parse(...)
|
||||
local options
|
||||
|
||||
local fromAddress = options.from and component.get(options.from) or filesystem.get(os.getenv("_")).address
|
||||
local candidates = {}
|
||||
for address in component.list("filesystem", true) do
|
||||
local dev = component.proxy(address)
|
||||
if not dev.isReadOnly() and dev.address ~= computer.tmpAddress() and dev.address ~= fromAddress then
|
||||
table.insert(candidates, dev)
|
||||
end
|
||||
do
|
||||
local basic = loadfile("/lib/tools/install_basics.lua", "bt", _G)
|
||||
options = basic(...)
|
||||
end
|
||||
|
||||
if #candidates == 0 then
|
||||
io.stderr:write("No writable disks found, aborting.\n")
|
||||
return 1
|
||||
if computer.freeMemory() < 50000 then
|
||||
print("Low memory, collecting garbage")
|
||||
for i=1,20 do os.sleep(0) end
|
||||
end
|
||||
|
||||
for i = 1, #candidates do
|
||||
local label = candidates[i].getLabel()
|
||||
if label then
|
||||
label = label .. " (" .. candidates[i].address:sub(1, 8) .. "...)"
|
||||
else
|
||||
label = candidates[i].address
|
||||
end
|
||||
io.write(i .. ") " .. label .. "\n")
|
||||
local cp, reason = loadfile(shell.resolve("cp", "lua"), "bt", _G)
|
||||
|
||||
local ec = cp(table.unpack(options.cp_args))
|
||||
if ec ~= nil and ec ~= 0 then
|
||||
return ec
|
||||
end
|
||||
|
||||
io.write("To select the device to install to, please enter a number between 1 and " .. #candidates .. ".\n")
|
||||
io.write("Press 'q' to cancel the installation.\n")
|
||||
local choice
|
||||
while not choice do
|
||||
result = io.read()
|
||||
if result:sub(1, 1):lower() == "q" then
|
||||
os.exit()
|
||||
end
|
||||
local number = tonumber(result)
|
||||
if number and number > 0 and number <= #candidates then
|
||||
choice = candidates[number]
|
||||
else
|
||||
io.write("Invalid input, please try again.\n")
|
||||
end
|
||||
local write = io.write
|
||||
local read = io.read
|
||||
write("Installation complete!\n")
|
||||
|
||||
if options.setlabel then
|
||||
pcall(options.target.dev.setLabel, options.label)
|
||||
end
|
||||
|
||||
local function findMount(address)
|
||||
for fs, path in filesystem.mounts() do
|
||||
if fs.address == component.get(address) then
|
||||
return path
|
||||
end
|
||||
end
|
||||
if options.setboot then
|
||||
computer.setBootAddress(options.target.dev.address)
|
||||
end
|
||||
|
||||
local name = options.name or "OpenOS"
|
||||
io.write("Installing " .. name .." to device " .. (choice.getLabel() or choice.address) .. "\n")
|
||||
os.sleep(0.25)
|
||||
local cpPath = filesystem.concat(findMount(filesystem.get(os.getenv("_")).address), "bin/cp")
|
||||
local cpOptions = "-vrx" .. (options.u and "ui " or "")
|
||||
local cpSource = filesystem.concat(findMount(fromAddress), options.fromDir or "/")
|
||||
local cpDest = findMount(choice.address) .. "/"
|
||||
local result, reason = os.execute(cpPath .. " " .. cpOptions .. " " .. cpSource .. " " .. cpDest)
|
||||
if not result then
|
||||
error(reason, 0)
|
||||
end
|
||||
if not options.nolabelset then pcall(choice.setLabel, name) end
|
||||
|
||||
if not options.noreboot then
|
||||
io.write("All done! " .. ((not options.noboot) and "Set as boot device and r" or "R") .. "eboot now? [Y/n]\n")
|
||||
local result = io.read()
|
||||
if options.reboot then
|
||||
write("Reboot now? [Y/n] ")
|
||||
local result = read()
|
||||
if not result or result == "" or result:sub(1, 1):lower() == "y" then
|
||||
if not options.noboot then computer.setBootAddress(choice.address)end
|
||||
io.write("\nRebooting now!\n")
|
||||
write("\nRebooting now!\n")
|
||||
computer.shutdown(true)
|
||||
end
|
||||
end
|
||||
io.write("Returning to shell.\n")
|
||||
|
||||
write("Returning to shell.\n")
|
||||
|
300
src/main/resources/assets/opencomputers/loot/openos/bin/less.lua
Normal file
300
src/main/resources/assets/opencomputers/loot/openos/bin/less.lua
Normal file
@ -0,0 +1,300 @@
|
||||
local keyboard = require("keyboard")
|
||||
local shell = require("shell")
|
||||
local term = require("term")
|
||||
local text = require("text")
|
||||
local unicode = require("unicode")
|
||||
local computer = require("computer")
|
||||
local tx = require("transforms")
|
||||
|
||||
local args = shell.parse(...)
|
||||
if #args > 1 then
|
||||
io.write("Usage: more <filename>\n")
|
||||
io.write("- or no args reads stdin\n")
|
||||
return 1
|
||||
end
|
||||
local arg = args[1] or "-"
|
||||
|
||||
local initial_offset
|
||||
|
||||
-- test validity of args
|
||||
do
|
||||
if arg == "-" then
|
||||
if not io.stdin then
|
||||
io.stderr:write("this process has no stdin\n")
|
||||
return 1
|
||||
end
|
||||
-- stdin may not be core_stdin
|
||||
initial_offset = io.stdin:seek("cur")
|
||||
else
|
||||
local file, reason = io.open(shell.resolve(arg))
|
||||
if not file then
|
||||
io.stderr:write(reason,'\n')
|
||||
return 1
|
||||
end
|
||||
initial_offset = file:seek("cur")
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
local width, height = term.getViewport()
|
||||
local max_display = height - 1
|
||||
|
||||
-- mgr is the data manager, it keeps track of what has been loaded
|
||||
-- keeps a reasonable buffer, and keeps track of file handles
|
||||
local mgr
|
||||
mgr =
|
||||
{
|
||||
lines = {}, -- current buffer
|
||||
chunk, -- temp from last read line that hasn't finished wrapping
|
||||
lines_released = 0,
|
||||
can_seek = initial_offset,
|
||||
capacity = math.max(1, math.min(max_display * 10, computer.freeMemory() / 2 / width)),
|
||||
size = 0,
|
||||
file = nil,
|
||||
path = arg ~= "-" and shell.resolve(arg) or nil,
|
||||
open = function()
|
||||
mgr.file = mgr.path and io.open(mgr.path) or io.stdin
|
||||
end,
|
||||
top_of_file = max_display,
|
||||
total_lines = nil, -- nil means unknown
|
||||
latest_line = nil, -- used for status improvements
|
||||
rollback = function()
|
||||
if not mgr.can_seek then
|
||||
return false
|
||||
end
|
||||
if not mgr.file then
|
||||
mgr.open()
|
||||
elseif not mgr.file:seek("set", 0) then
|
||||
mgr.close()
|
||||
return false
|
||||
end
|
||||
mgr.lines_released = 0
|
||||
mgr.lines = {}
|
||||
mgr.size = 0
|
||||
return true
|
||||
end,
|
||||
at = function(line_number)
|
||||
local index = line_number - mgr.lines_released
|
||||
if index < 1 then
|
||||
index = index + mgr.capacity
|
||||
if #mgr.lines ~= mgr.capacity or index <= mgr.size then
|
||||
return nil
|
||||
end
|
||||
elseif index > mgr.size then
|
||||
return nil
|
||||
end
|
||||
return mgr.lines[index] -- cached
|
||||
end,
|
||||
load = function(line_number)
|
||||
local index = line_number - mgr.lines_released
|
||||
if mgr.total_lines and mgr.total_lines < line_number then
|
||||
return nil
|
||||
end
|
||||
if mgr.at(line_number) then
|
||||
return true
|
||||
end
|
||||
-- lines[index] is line (lines_released + index) in the file
|
||||
-- thus index == line_number - lines_released
|
||||
if index <= 0 then
|
||||
-- we have previously freed some of the buffer, and now the user wants it back
|
||||
if not mgr.rollback() then
|
||||
-- TODO how to nicely fail if can_seek == false
|
||||
-- or if no more buffers
|
||||
error("cannot load prior data")
|
||||
end
|
||||
return mgr.load(line_number) -- retry
|
||||
end
|
||||
if mgr.read_next() then
|
||||
return mgr.load(line_number) -- retry
|
||||
end
|
||||
-- ran out of file, could not reach line_number
|
||||
end,
|
||||
write = function(line_number)
|
||||
local line = mgr.at(line_number)
|
||||
if not line then return false end
|
||||
term.write(line)
|
||||
end,
|
||||
close = function()
|
||||
if mgr.file then
|
||||
mgr.file:close()
|
||||
mgr.file = nil
|
||||
end
|
||||
end,
|
||||
last = function()
|
||||
-- return the last line_number available right now in the cache
|
||||
return mgr.size + mgr.lines_released
|
||||
end,
|
||||
check_capacity = function(release)
|
||||
-- if we have reached capacity
|
||||
if mgr.size >= mgr.capacity then
|
||||
if release then
|
||||
mgr.lines_released = mgr.lines_released + mgr.size
|
||||
mgr.size = 0
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
insert = function(line)
|
||||
if mgr.check_capacity() then return false end
|
||||
mgr.size = mgr.size + 1
|
||||
mgr.lines[mgr.size] = line
|
||||
-- latest_line is not used for computation, just for status reports
|
||||
mgr.latest_line = math.max(mgr.latest_line or 0, mgr.size + mgr.lines_released)
|
||||
return true
|
||||
end,
|
||||
read_next = function()
|
||||
-- total_lines indicates we've reached the end previously
|
||||
-- but have we just prior to this reached the end?
|
||||
if mgr.last() == mgr.total_lines then
|
||||
-- then there is no more after that point
|
||||
return nil
|
||||
end
|
||||
if not mgr.file then
|
||||
mgr.open()
|
||||
end
|
||||
mgr.check_capacity(true)
|
||||
if not mgr.chunk then
|
||||
mgr.chunk = mgr.file:read("*l")
|
||||
if not mgr.chunk then
|
||||
mgr.total_lines = mgr.size + mgr.lines_released -- now file length is known
|
||||
mgr.close()
|
||||
end
|
||||
end
|
||||
while mgr.chunk do
|
||||
local wrapped, next = text.wrap(text.detab(mgr.chunk), width, width)
|
||||
-- insert fails if capacity is full
|
||||
if not mgr.insert(wrapped) then
|
||||
return mgr.last()
|
||||
end
|
||||
mgr.chunk = next
|
||||
end
|
||||
|
||||
return mgr.last()
|
||||
end,
|
||||
scroll = function(num)
|
||||
if num < 0 then
|
||||
num = math.max(num, mgr.top_of_file)
|
||||
if num >= 0 then
|
||||
return true -- nothing to scroll
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursor(1, height)
|
||||
local y = height
|
||||
term.clearLine()
|
||||
|
||||
if num < 0 then
|
||||
term.scroll(num) -- push text down
|
||||
mgr.top_of_file = mgr.top_of_file - num
|
||||
y = 1
|
||||
term.setCursor(1, y) -- ready to write lines above
|
||||
num = -num -- now print forward
|
||||
end
|
||||
|
||||
local range
|
||||
while num > 0 do
|
||||
-- trigger load of data if needed
|
||||
local line_number = y - mgr.top_of_file
|
||||
|
||||
if not mgr.load(line_number) then -- nothing more to read from the file
|
||||
return range ~= nil -- first time it is nil
|
||||
end
|
||||
|
||||
-- print num range of what is available, scroll to show it (if bottom of screen)
|
||||
range = math.min(num, mgr.last() - line_number + 1)
|
||||
|
||||
if y == height then
|
||||
range = math.min(range, max_display)
|
||||
term.scroll(range)
|
||||
y = y - range
|
||||
term.setCursor(1, y)
|
||||
mgr.top_of_file = mgr.top_of_file - range
|
||||
end
|
||||
|
||||
for i=1,range do
|
||||
mgr.write(line_number + i - 1)
|
||||
term.setCursor(1, y + i)
|
||||
end
|
||||
y = y + range
|
||||
|
||||
num = num - range
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
print_status = function()
|
||||
local first = mgr.top_of_file >= 1 and 1 or 1 - mgr.top_of_file
|
||||
local perc = not mgr.total_lines and "--" or tostring((max_display - mgr.top_of_file) / mgr.total_lines * 100):gsub("%..*","")
|
||||
local last_plus = mgr.total_lines and "" or "+"
|
||||
local status = string.format("%s lines %d-%d/%s %s%%", mgr.path or "-", first, max_display - mgr.top_of_file, tostring(mgr.total_lines or mgr.latest_line)..last_plus, perc)
|
||||
|
||||
local gpu = term.gpu()
|
||||
local sf, sb = gpu.setForeground, gpu.setBackground
|
||||
local b_color, b_is_palette = gpu.getBackground()
|
||||
local f_color, f_is_palette = gpu.getForeground()
|
||||
sf(b_color, b_is_palette)
|
||||
sb(f_color, f_is_palette)
|
||||
term.write(status)
|
||||
sb(b_color, b_is_palette)
|
||||
sf(f_color, f_is_palette)
|
||||
end
|
||||
}
|
||||
|
||||
local function update(num)
|
||||
-- unexpected
|
||||
if num == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- if this a positive direction, and we didn't previously know this was the end of the stream, give the user a once chance
|
||||
local end_is_known = mgr.total_lines
|
||||
-- clear buttom line, for status
|
||||
local ok = mgr.scroll(num or max_display)
|
||||
|
||||
-- print status
|
||||
term.setCursor(1, height)
|
||||
-- we have to clear again in case we scrolled up
|
||||
term.clearLine()
|
||||
mgr.print_status()
|
||||
return not end_is_known or ok
|
||||
end
|
||||
|
||||
if not update() then
|
||||
return
|
||||
end
|
||||
|
||||
while true do
|
||||
local ename, address, char, code, dy = term.pull()
|
||||
local num
|
||||
if ename == "scroll" then
|
||||
if dy < 0 then
|
||||
num = 3
|
||||
else
|
||||
num = -3
|
||||
end
|
||||
elseif ename == "key_down" then
|
||||
num = 0
|
||||
if code == keyboard.keys.q or code == keyboard.keys.d and keyboard.isControlDown() then
|
||||
break
|
||||
elseif code == keyboard.keys.space or code == keyboard.keys.pageDown then
|
||||
num = max_display
|
||||
elseif code == keyboard.keys.pageUp then
|
||||
num = -max_display
|
||||
elseif code == keyboard.keys.enter or code == keyboard.keys.down then
|
||||
num = 1
|
||||
elseif code == keyboard.keys.up then
|
||||
num = -1
|
||||
elseif code == keyboard.keys.home then
|
||||
num = -math.huge
|
||||
elseif code == keyboard.keys["end"] then
|
||||
num = math.huge
|
||||
end
|
||||
elseif ename == "interrupted" then
|
||||
break
|
||||
end
|
||||
if num then
|
||||
update(num)
|
||||
end
|
||||
end
|
||||
|
||||
term.clearLine()
|
@ -16,6 +16,10 @@ else
|
||||
linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target))
|
||||
end
|
||||
|
||||
if fs.isDirectory(linkpath) then
|
||||
linkpath = fs.concat(linkpath, fs.name(target))
|
||||
end
|
||||
|
||||
local result, reason = fs.link(target, linkpath)
|
||||
if not result then
|
||||
io.stderr:write(reason..'\n')
|
||||
|
@ -9,8 +9,33 @@ if #args < 2 then
|
||||
return 1
|
||||
end
|
||||
|
||||
local function is_mount(path)
|
||||
if not fs.isDirectory(path) then return false end
|
||||
path = fs.canonical(path) .. '/'
|
||||
for driver, mount_point in fs.mounts() do
|
||||
if path == mount_point then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function is_readonly(path)
|
||||
return fs.get(path).isReadOnly()
|
||||
end
|
||||
|
||||
local from = shell.resolve(args[1])
|
||||
local to = shell.resolve(args[2])
|
||||
|
||||
if is_readonly(to) then
|
||||
io.stderr:write("cannot write to " .. to .. ", filesystem is readonly\n");
|
||||
return 1
|
||||
end
|
||||
|
||||
if is_mount(from) then
|
||||
io.stderr:write("cannot move " .. from .. ", it is a mount point\n");
|
||||
return 1
|
||||
end
|
||||
|
||||
if fs.isDirectory(to) then
|
||||
to = to .. "/" .. fs.name(from)
|
||||
end
|
||||
|
@ -32,7 +32,7 @@ local metas = {}
|
||||
local function _path(m) return shell.resolve(m.rel) end
|
||||
local function _link(m) return fs.isLink(_path(m)) end
|
||||
local function _exists(m) return _link(m) or fs.exists(_path(m)) end
|
||||
local function _dir(m) return fs.isDirectory(_path(m)) end
|
||||
local function _dir(m) return not _link(m) and fs.isDirectory(_path(m)) end
|
||||
local function _readonly(m) return not _exists(m) or fs.get(_path(m)).isReadOnly() end
|
||||
local function _empty(m) return _exists(m) and _dir(m) and (fs.list(_path(m))==nil) end
|
||||
|
||||
|
@ -6,7 +6,6 @@ alias copy=cp
|
||||
alias del=rm
|
||||
alias md=mkdir
|
||||
alias cls=clear
|
||||
alias less=more
|
||||
alias rs=redstone
|
||||
alias view=edit\ -r
|
||||
alias help=man
|
||||
|
@ -378,16 +378,23 @@ end
|
||||
function filesystem.remove(path)
|
||||
local function removeVirtual()
|
||||
local node, rest, vnode, vrest = findNode(filesystem.path(path))
|
||||
local name = filesystem.name(path)
|
||||
if vnode.children[name] then
|
||||
vnode.children[name] = nil
|
||||
removeEmptyNodes(vnode)
|
||||
return true
|
||||
elseif vnode.links[name] then
|
||||
vnode.links[name] = nil
|
||||
removeEmptyNodes(vnode)
|
||||
return true
|
||||
-- vrest represents the remaining path beyond vnode
|
||||
-- vrest is nil if vnode reaches the full path
|
||||
-- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
|
||||
if not vrest then
|
||||
local name = filesystem.name(path)
|
||||
if vnode.children[name] then
|
||||
vnode.children[name] = nil
|
||||
removeEmptyNodes(vnode)
|
||||
return true
|
||||
elseif vnode.links[name] then
|
||||
vnode.links[name] = nil
|
||||
removeEmptyNodes(vnode)
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- return false even if vrest is nil because this means it was a expected
|
||||
-- to be a real file
|
||||
return false
|
||||
end
|
||||
local function removePhysical()
|
||||
|
@ -28,7 +28,7 @@ end
|
||||
function guid.next()
|
||||
-- e.g. 3c44c8a9-0613-46a2-ad33-97b6ba2e9d9a
|
||||
-- 8-4-4-4-12
|
||||
local sets = {8, 4, 4, 12}
|
||||
local sets = {8, 4, 4, 4, 12}
|
||||
local result = ""
|
||||
|
||||
local i
|
||||
|
@ -64,9 +64,8 @@ local delay_tools = setmetatable({},{__mode="v"})
|
||||
package.delay_data = delay_data
|
||||
|
||||
function delay_data.__index(tbl,key)
|
||||
local lookup = delay_tools.lookup or loadfile("/lib/tools/delayLookup.lua")
|
||||
delay_tools.lookup = lookup
|
||||
return lookup(delay_data, tbl, key)
|
||||
delay_data.lookup = delay_data.lookup or loadfile("/lib/tools/delayLookup.lua")
|
||||
return delay_data.lookup(delay_data, tbl, key)
|
||||
end
|
||||
delay_data.__pairs = delay_data.__index -- nil key acts like pairs
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
local serialization = {}
|
||||
|
||||
-- delay loaded tables fail to deserialize cross [C] boundaries (such as when having to read files that cause yields)
|
||||
local local_pairs = function(tbl)
|
||||
local mt = getmetatable(tbl)
|
||||
return (mt and mt.__pairs or pairs)(tbl)
|
||||
end
|
||||
|
||||
-- Important: pretty formatting will allow presenting non-serializable values
|
||||
-- but may generate output that cannot be unserialized back.
|
||||
function serialization.serialize(value, pretty)
|
||||
@ -44,7 +50,7 @@ function serialization.serialize(value, pretty)
|
||||
local f
|
||||
if pretty then
|
||||
local ks, sks, oks = {}, {}, {}
|
||||
for k in pairs(v) do
|
||||
for k in local_pairs(v) do
|
||||
if type(k) == "number" then
|
||||
table.insert(ks, k)
|
||||
elseif type(k) == "string" then
|
||||
@ -72,7 +78,7 @@ function serialization.serialize(value, pretty)
|
||||
end
|
||||
end)
|
||||
else
|
||||
f = table.pack(pairs(v))
|
||||
f = table.pack(local_pairs(v))
|
||||
end
|
||||
for k, v in table.unpack(f) do
|
||||
if r then
|
||||
|
@ -217,9 +217,14 @@ function sh.internal.parseCommand(words)
|
||||
table.insert(evaluated_words, arg)
|
||||
end
|
||||
end
|
||||
local program, reason = shell.resolve(evaluated_words[1], "lua")
|
||||
local eword = evaluated_words[1]
|
||||
local possible_dir_path = shell.resolve(eword)
|
||||
if possible_dir_path and fs.isDirectory(possible_dir_path) then
|
||||
return nil, string.format("%s: is a directory", eword)
|
||||
end
|
||||
local program, reason = shell.resolve(eword, "lua")
|
||||
if not program then
|
||||
return nil, evaluated_words[1] .. ": " .. reason
|
||||
return nil, eword .. ": " .. reason
|
||||
end
|
||||
evaluated_words = tx.sub(evaluated_words, 2)
|
||||
return program, evaluated_words
|
||||
@ -420,6 +425,10 @@ function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads)
|
||||
if i < #threads then
|
||||
pipe = require("buffer").new("rw", sh.internal.newMemoryStream())
|
||||
pipe:setvbuf("no")
|
||||
-- buffer close flushes the buffer, but we have no buffer
|
||||
-- also, when the buffer is closed, read and writes don't pass through
|
||||
-- simply put, we don't want buffer:close
|
||||
pipe.close = function(self) self.stream:close() end
|
||||
pipe.stream.redirect[1] = rawget(pio, 1)
|
||||
pio[1] = pipe
|
||||
table.insert(data.handles, pipe)
|
||||
@ -507,7 +516,12 @@ function --[[@delayloaded-start@]] sh.getMatchingPrograms(baseName)
|
||||
return result
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] sh.getMatchingFiles(basePath, name)
|
||||
function --[[@delayloaded-start@]] sh.getMatchingFiles(partial_path)
|
||||
-- name: text of the partial file name being expanded
|
||||
local name = partial_path:gsub("^.*/", "")
|
||||
-- here we remove the name text from the partialPrefix
|
||||
local basePath = unicode.sub(partial_path, 1, -unicode.len(name) - 1)
|
||||
|
||||
local resolvedPath = shell.resolve(basePath)
|
||||
local result, baseName = {}
|
||||
|
||||
@ -525,7 +539,7 @@ function --[[@delayloaded-start@]] sh.getMatchingFiles(basePath, name)
|
||||
for file in fs.list(resolvedPath) do
|
||||
local match = file:match(baseName)
|
||||
if match then
|
||||
table.insert(result, basePath .. match)
|
||||
table.insert(result, basePath .. match:gsub("(%s)", "\\%1"))
|
||||
end
|
||||
end
|
||||
-- (cont.) but if there's only one match and it's a directory, *then* we
|
||||
@ -537,19 +551,57 @@ function --[[@delayloaded-start@]] sh.getMatchingFiles(basePath, name)
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] sh.internal.hintHandlerSplit(line)
|
||||
if line:sub(-1):find("%s") then
|
||||
return '', line
|
||||
end
|
||||
local splits = text.internal.tokenize(line)
|
||||
-- I do not plan on having text tokenizer parse error on
|
||||
-- trailiing \ in case of future support for multiple line
|
||||
-- input. But, there are also no hints for it
|
||||
if line:match("\\$") then return nil end
|
||||
|
||||
local splits, simple = text.internal.tokenize(line,{show_escapes=true})
|
||||
if not splits then -- parse error, e.g. unclosed quotes
|
||||
return nil -- no split, no hints
|
||||
end
|
||||
|
||||
local num_splits = #splits
|
||||
if num_splits == 1 or not isWordOf(splits[num_splits-1],{";","&&","||","|"}) then
|
||||
return '', line
|
||||
|
||||
-- search for last statement delimiters
|
||||
local last_close = 0
|
||||
for index = num_splits, 1, -1 do
|
||||
local word = splits[index]
|
||||
if isWordOf(word, {";","&&","||","|"}) then
|
||||
last_close = index
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- if the very last word of the line is a delimiter
|
||||
-- consider this a fresh new, empty line
|
||||
-- this captures edge cases with empty input as well (i.e. no splits)
|
||||
if last_close == num_splits then
|
||||
return nil -- no hints on empty command
|
||||
end
|
||||
|
||||
local last_word = splits[num_splits]
|
||||
local normal = text.internal.normalize({last_word})[1]
|
||||
|
||||
-- if there is white space following the words
|
||||
-- and we have at least one word following the last delimiter
|
||||
-- then in all cases we are looking for ANY arg
|
||||
if unicode.sub(line, -unicode.len(normal)) ~= normal then
|
||||
return line, nil, ""
|
||||
end
|
||||
|
||||
local prefix = unicode.sub(line, 1, -unicode.len(normal) - 1)
|
||||
|
||||
-- renormlizing the string will create 'printed' quality text
|
||||
normal = text.internal.normalize(text.internal.tokenize(normal), true)[1]
|
||||
|
||||
-- one word: cmd
|
||||
-- many: arg
|
||||
if last_close == num_splits - 1 then
|
||||
return prefix, normal, nil
|
||||
else
|
||||
return prefix, nil, normal
|
||||
end
|
||||
local l = text.internal.normalize({splits[num_splits]})[1]
|
||||
return line:sub(1,-unicode.len(l)-1), l
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] sh.internal.hintHandlerImpl(full_line, cursor)
|
||||
@ -557,52 +609,47 @@ function --[[@delayloaded-start@]] sh.internal.hintHandlerImpl(full_line, cursor
|
||||
local line = unicode.sub(full_line, 1, cursor - 1)
|
||||
-- suffix: text following the cursor (if any, else empty string) to append to the hints
|
||||
local suffix = unicode.sub(full_line, cursor)
|
||||
-- if there is no text to hint, there are no hints
|
||||
if not line or #line < 1 then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- hintHandlerSplit helps make the hints work even after delimiters such as ;
|
||||
-- it also catches parse errors such as unclosed quotes
|
||||
local prev,line = sh.internal.hintHandlerSplit(line)
|
||||
if not prev then -- failed to parse, e.g. unclosed quote, no hints
|
||||
-- prev: not needed for this hint
|
||||
-- cmd: the command needing hint
|
||||
-- arg: the argument needing hint
|
||||
local prev, cmd, arg = sh.internal.hintHandlerSplit(line)
|
||||
|
||||
-- also, if there is no text to hint, there are no hints
|
||||
if not prev then -- no hints e.g. unclosed quote, e.g. no text
|
||||
return {}
|
||||
end
|
||||
local result
|
||||
-- prefix: text (if any) that will not be expanded (such as a command word preceding a file name that we are expanding)
|
||||
-- partial: text that we want to expand
|
||||
-- this first match determines if partial comes after redirect symbols such as >
|
||||
local prefix, partial = line:match("^(.*[=><]%s*)(.*)$")
|
||||
-- if redirection was not found, partial could just be arguments following a command
|
||||
if not prefix then prefix, partial = line:match("^(.+%s+)(.*)$") end
|
||||
-- partialPrefix: text of the partial that will not be expanded (i.e. a diretory path ending with /)
|
||||
-- first, partialPrefix holds the whole text being expanded (we truncate later)
|
||||
local partialPrefix = (partial or line)
|
||||
-- name: text of the partial file name being expanded
|
||||
local name = partialPrefix:gsub("^.*/", "")
|
||||
-- here we remove the name text from the partialPrefix
|
||||
partialPrefix = partialPrefix:sub(1, -unicode.len(name) - 1)
|
||||
-- if no prefix was found and partialPrefix did not specify a closed directory path then we are expanding the first argument
|
||||
-- i.e. the command word (a program name)
|
||||
local searchInPath = not prefix and not partialPrefix:find("/")
|
||||
|
||||
local searchInPath = cmd and not cmd:find("/")
|
||||
if searchInPath then
|
||||
result = sh.getMatchingPrograms(line)
|
||||
result = sh.getMatchingPrograms(cmd)
|
||||
else
|
||||
result = sh.getMatchingFiles(partialPrefix, name)
|
||||
-- special arg issue, after equal sign
|
||||
if arg then
|
||||
local equal_index = arg:find("=[^=]*$")
|
||||
if equal_index then
|
||||
prev = prev .. unicode.sub(arg, 1, equal_index)
|
||||
arg = unicode.sub(arg, equal_index + 1)
|
||||
end
|
||||
end
|
||||
result = sh.getMatchingFiles(cmd or arg)
|
||||
end
|
||||
|
||||
-- in very special cases, the suffix should include a blank space to indicate to the user that the hint is discrete
|
||||
local resultSuffix = suffix
|
||||
if #result > 0 and unicode.sub(result[1], -1) ~= "/" and
|
||||
not suffix:sub(1,1):find('%s') and
|
||||
(#result == 1 or searchInPath or not prefix) then
|
||||
#result == 1 or searchInPath then
|
||||
resultSuffix = " " .. resultSuffix
|
||||
end
|
||||
-- prefix no longer needs to refer to just the expanding section of the text
|
||||
-- here we reintroduce the previous section of the text that hintHandlerSplit cut for us
|
||||
prefix = prev .. (prefix or "")
|
||||
|
||||
table.sort(result)
|
||||
for i = 1, #result do
|
||||
-- the hints define the whole line of text
|
||||
result[i] = prefix .. result[i] .. resultSuffix
|
||||
result[i] = prev .. result[i] .. resultSuffix
|
||||
end
|
||||
return result
|
||||
end --[[@delayloaded-end@]]
|
||||
@ -796,7 +843,7 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
|
||||
table.remove(self.result, 1)
|
||||
return self
|
||||
end
|
||||
return nil, 'stream closed'
|
||||
os.exit(0) -- abort the current process: SIGPIPE
|
||||
end
|
||||
|
||||
local stream = {closed = false, buffer = "",
|
||||
|
@ -127,7 +127,7 @@ function shell.resolveAlias(command, args)
|
||||
end
|
||||
|
||||
function shell.getWorkingDirectory()
|
||||
return os.getenv("PWD")
|
||||
return os.getenv("PWD") or "/"
|
||||
end
|
||||
|
||||
function shell.setWorkingDirectory(dir)
|
||||
|
@ -89,7 +89,7 @@ function term.internal.pull(input, c, off, t, ...)
|
||||
if gpu then
|
||||
gpu.set(x,y,c[3])
|
||||
end
|
||||
return select(2,unpack(a))
|
||||
return unpack(a)
|
||||
end
|
||||
local blinking = w.blink
|
||||
if input then blinking = input.blink end
|
||||
@ -99,7 +99,12 @@ end
|
||||
function term.pull(p,...)
|
||||
local a,t = {p,...}
|
||||
if type(p) == "number" then t = table.remove(a,1) end
|
||||
return term.internal.pull(nil,nil,nil,t,table.unpack(a))
|
||||
-- term.internal.pull captures hard interrupts to keep term.readKeyboard peaceful
|
||||
-- but other scripts may be using term.pull as an event.pull replacement
|
||||
-- in which case, term.pull need to abort on hard interrupt
|
||||
local packed = {term.internal.pull(nil,nil,nil,t,table.unpack(a))}
|
||||
assert(packed[1], "interrupted")
|
||||
return select(2, table.unpack(packed))
|
||||
end
|
||||
|
||||
function term.read(history,dobreak,hintHandler,pwchar,filter)
|
||||
@ -229,7 +234,7 @@ function term.readKeyboard(ops)
|
||||
term.internal.build_vertical_reader(input)
|
||||
end
|
||||
while true do
|
||||
local name, address, char, code = term.internal.pull(input)
|
||||
local killed, name, address, char, code = term.internal.pull(input)
|
||||
local c = nil
|
||||
local backup_cache = hints.cache
|
||||
if name =="interrupted" then draw("^C\n",true) return ""
|
||||
@ -251,6 +256,8 @@ function term.readKeyboard(ops)
|
||||
input:move(ctrl and term.internal.ctrl_movement(input, -1) or -1)
|
||||
elseif code==keys.right then
|
||||
input:move(ctrl and term.internal.ctrl_movement(input, 1) or 1)
|
||||
elseif ctrl and char=="w" then
|
||||
-- cut word
|
||||
elseif code==keys.up then
|
||||
term.internal.read_history(history,input,1)
|
||||
elseif code==keys.down then
|
||||
@ -383,6 +390,38 @@ function term.bind(gpu, screen, kb, window)
|
||||
end
|
||||
end
|
||||
|
||||
function --[[@delayloaded-start@]] term.scroll(number, window)
|
||||
-- if zero scroll length is requested, do nothing
|
||||
if number == 0 then return end
|
||||
-- window is optional, default to current active terminal
|
||||
window = window or W()
|
||||
-- gpu works with global coordinates
|
||||
local gpu,width,height,dx,dy,x,y = window.gpu,term.getViewport(w)
|
||||
|
||||
-- scroll request can be too large
|
||||
local abs_number = math.abs(number)
|
||||
if (abs_number >= height) then
|
||||
term.clear()
|
||||
return
|
||||
end
|
||||
|
||||
-- box positions to shift
|
||||
local box_height = height - abs_number
|
||||
local top = 0
|
||||
if number > 0 then
|
||||
top = number -- (e.g. 1 scroll moves box at 2)
|
||||
end
|
||||
|
||||
gpu.copy(dx + 1, dy + top + 1, width, box_height, 0, -number)
|
||||
|
||||
local fill_top = 0
|
||||
if number > 0 then
|
||||
fill_top = box_height
|
||||
end
|
||||
|
||||
gpu.fill(dx + 1, dy + fill_top + 1, width, abs_number, " ")
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] term.internal.ctrl_movement(input, dir)
|
||||
local index, data = input.index, input.data
|
||||
|
||||
|
@ -80,24 +80,21 @@ end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- tokenize allows nil for delimiters, quotes, and doNotNormalize
|
||||
-- always separates by whitespace
|
||||
-- default quote rules: '' and ""
|
||||
-- default delimiters: all
|
||||
-- default is to normalize, that is, no metadata is returned, just a list of tokens
|
||||
function text.tokenize(value, doNotNormalize, quotes, delimiters)
|
||||
-- separate string value into an array of words delimited by whitespace
|
||||
-- groups by quotes
|
||||
-- options is a table used for internal undocumented purposes
|
||||
function text.tokenize(value, options)
|
||||
checkArg(1, value, "string")
|
||||
checkArg(2, doNotNormalize, "boolean", "nil")
|
||||
checkArg(3, quotes, "table", "nil")
|
||||
checkArg(4, delimiters, "table", "nil")
|
||||
checkArg(2, options, "table", "nil")
|
||||
options = options or {}
|
||||
|
||||
local tokens, reason = text.internal.tokenize(value, quotes, delimiters)
|
||||
local tokens, reason = text.internal.tokenize(value, options)
|
||||
|
||||
if type(tokens) ~= "table" then
|
||||
return nil, reason
|
||||
end
|
||||
|
||||
if doNotNormalize then
|
||||
if options.doNotNormalize then
|
||||
return tokens
|
||||
end
|
||||
|
||||
@ -113,6 +110,9 @@ function text.removeEscapes(txt)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- like tokenize, but does not drop any text such as whitespace
|
||||
-- splits input into an array for sub strings delimited by delimiters
|
||||
-- delimiters are included in the result if not dropDelims
|
||||
function --[[@delayloaded-start@]] text.split(input, delimiters, dropDelims, di)
|
||||
checkArg(1, input, "string")
|
||||
checkArg(2, delimiters, "table")
|
||||
@ -153,14 +153,15 @@ end --[[@delayloaded-end@]]
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
function text.internal.tokenize(value, quotes, delimiters)
|
||||
function text.internal.tokenize(value, options)
|
||||
checkArg(1, value, "string")
|
||||
checkArg(2, quotes, "table", "nil")
|
||||
checkArg(3, delimiters, "table", "nil")
|
||||
local custom = not not delimiters
|
||||
checkArg(2, options, "table", "nil")
|
||||
options = options or {}
|
||||
local delimiters = options.delimiters
|
||||
local custom = not not options.delimiters
|
||||
delimiters = delimiters or text.syntax
|
||||
|
||||
local words, reason = text.internal.words(value, quotes)
|
||||
local words, reason = text.internal.words(value, options)
|
||||
|
||||
local splitter = text.escapeMagic(custom and table.concat(delimiters) or "<>|;&")
|
||||
if type(words) ~= "table" or
|
||||
@ -173,9 +174,12 @@ function text.internal.tokenize(value, quotes, delimiters)
|
||||
end
|
||||
|
||||
-- tokenize input by quotes and whitespace
|
||||
function text.internal.words(input, quotes)
|
||||
function text.internal.words(input, options)
|
||||
checkArg(1, input, "string")
|
||||
checkArg(2, quotes, "table", "nil")
|
||||
checkArg(2, options, "table", "nil")
|
||||
options = options or {}
|
||||
local quotes = options.quotes
|
||||
local show_escapes = options.show_escapes
|
||||
local qr = nil
|
||||
quotes = quotes or {{"'","'",true},{'"','"'},{'`','`'}}
|
||||
local function append(dst, txt, qr)
|
||||
@ -193,11 +197,12 @@ function text.internal.words(input, quotes)
|
||||
local char = unicode.sub(input, i, i)
|
||||
if escaped then -- escaped character
|
||||
escaped = false
|
||||
-- include escape char if
|
||||
-- include escape char if show_escapes
|
||||
-- or the followwing are all true
|
||||
-- 1. qr active
|
||||
-- 2. the char escaped is NOT the qr closure
|
||||
-- 3. qr is not literal
|
||||
if qr and not qr[3] and qr[2] ~= char then
|
||||
if show_escapes or (qr and not qr[3] and qr[2] ~= char) then
|
||||
append(token, '\\', qr)
|
||||
end
|
||||
append(token, char, qr)
|
||||
|
@ -0,0 +1,207 @@
|
||||
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 read = io.read
|
||||
|
||||
local args, options = shell.parse(...)
|
||||
|
||||
options.sources = {}
|
||||
options.targets = {}
|
||||
options.source_label = args[1]
|
||||
|
||||
local root_exception
|
||||
|
||||
if options.help then
|
||||
print([[Usage: install [OPTION]...
|
||||
--from=ADDR install filesystem at ADDR
|
||||
default: builds list of
|
||||
candidates and prompts user
|
||||
--to=ADDR same as --from but for target
|
||||
--fromDir=PATH install PATH from source
|
||||
--root=PATH same as --fromDir but target
|
||||
--toDir=PATH same as --root
|
||||
-u, --update update files interactively
|
||||
--label override label from .prop
|
||||
--nosetlabel do not label target
|
||||
--nosetboot do not use target for boot
|
||||
--noreboot do not reboot after install]])
|
||||
return nil -- exit success
|
||||
end
|
||||
|
||||
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 function cleanPath(path)
|
||||
if path then
|
||||
local rpath = shell.resolve(path)
|
||||
if fs.isDirectory(rpath) then
|
||||
return fs.canonical(rpath):gsub("/+$", "") .. '/'
|
||||
end
|
||||
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.source_dir = (options.fromDir or "") .. '.'
|
||||
options.target_dir = (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
|
||||
end
|
||||
end
|
||||
|
||||
for dev, path in fs.mounts() do
|
||||
local candidate = {dev=dev, path=path:gsub("/+$","")..'/'}
|
||||
|
||||
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 options.source_label or options.source_label:lower() == (candidate.prop.label or dev.getLabel()):lower() then
|
||||
table.insert(options.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 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
|
||||
io.stderr:write("Cannot install to " .. options.to .. ", it is read only\n")
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local source = options.sources[1]
|
||||
local target = options.targets[1]
|
||||
|
||||
if #options.sources ~= 1 or #options.targets ~= 1 then
|
||||
source, target = loadfile("/lib/tools/install_utils.lua", "bt", _G)('select', options)
|
||||
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
|
||||
|
||||
local installer_path = options.source_root .. "/.install"
|
||||
if fs.exists(installer_path) then
|
||||
return loadfile("/lib/tools/install_utils.lua", "bt", _G)('install', options)
|
||||
end
|
||||
|
||||
local cp_args =
|
||||
{
|
||||
"-vrx" .. (options.update and "ui" or ""),
|
||||
options.source_root .. options.source_dir,
|
||||
options.target_root .. options.target_dir
|
||||
}
|
||||
|
||||
local source_display = (source.prop or {}).label or source.dev.getLabel() or source.path
|
||||
local special_target = ""
|
||||
if #options.targets > 1 or options.to then
|
||||
special_target = " to " .. cp_args[3]
|
||||
end
|
||||
io.write("Install " .. source_display .. special_target .. "? [Y/n] ")
|
||||
local choice = read():lower()
|
||||
if choice ~= "y" and choice ~= "" then
|
||||
write("Installation cancelled\n")
|
||||
os.exit()
|
||||
end
|
||||
|
||||
return
|
||||
{
|
||||
setlabel = options.setlabel,
|
||||
label = options.label,
|
||||
setboot = options.setboot,
|
||||
reboot = options.reboot,
|
||||
target = target,
|
||||
cp_args = cp_args,
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
local cmd, options = ...
|
||||
|
||||
local function select_prompt(devs, direction)
|
||||
table.sort(devs, function(a, b) return a.path<b.path end)
|
||||
|
||||
local choice = devs[1]
|
||||
if #devs > 1 then
|
||||
io.write("Select the device to install " .. direction .. '\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()
|
||||
if result:sub(1, 1):lower() == "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: ")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return choice
|
||||
end
|
||||
|
||||
if cmd == 'select' then
|
||||
if #options.sources == 0 then
|
||||
if options.source_label then
|
||||
io.stderr:write("No install source matched given label: " .. options.source_label .. '\n')
|
||||
elseif options.from then
|
||||
io.stderr:write("No install source found: " .. options.from .. '\n')
|
||||
else
|
||||
io.stderr:write("Could not find any available installations\n")
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
if #options.targets == 0 then
|
||||
if options.to then
|
||||
io.stderr:write("No such filesystem to install to: " .. options.to .. '\n')
|
||||
else
|
||||
io.stderr:write("No writable disks found, aborting\n")
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
return select_prompt(options.sources, "from"), select_prompt(options.targets, "to")
|
||||
|
||||
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()
|
||||
end
|
||||
end
|
@ -54,16 +54,16 @@ function lib.first(tbl,pred,f,l)
|
||||
end
|
||||
|
||||
-- if value was made by lib.sub then find can find from whence
|
||||
function --[[@delayloaded-start@]] lib.find(tbl,v,f,l)
|
||||
checkArg(1,tbl,'table')
|
||||
checkArg(2,v,'table')
|
||||
local s=#v
|
||||
return lib.first(tbl,function(e,i,tbl)
|
||||
for n=0,s-1 do
|
||||
if tbl[n+i]~=v[n+1] then return nil end
|
||||
function --[[@delayloaded-start@]] lib.find(tbl, sub, first, last)
|
||||
checkArg(1, tbl, 'table')
|
||||
checkArg(2, sub, 'table')
|
||||
local sub_len = #sub
|
||||
return lib.first(tbl, function(element, index, projected_table)
|
||||
for n=0,sub_len-1 do
|
||||
if projected_table[n + index] ~= sub[n + 1] then return nil end
|
||||
end
|
||||
return 1,s
|
||||
end,f,l)
|
||||
return 1, sub_len
|
||||
end, first, last)
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
-- Returns a list of subsets of tbl where partitioner acts as a delimiter.
|
||||
@ -150,12 +150,14 @@ function lib.foreach(tbl,c,f,l)
|
||||
return r
|
||||
end
|
||||
lib.select=lib.foreach
|
||||
|
||||
function --[[@delayloaded-start@]] lib.where(tbl,p,f,l)
|
||||
return lib.foreach(tbl,
|
||||
function(e,i,tbl)
|
||||
return p(e,i,tbl)and e or nil
|
||||
end,f,l)
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function lib.concat(...)
|
||||
local r,rn,k={},0
|
||||
for _,tbl in ipairs({...})do
|
||||
@ -172,4 +174,19 @@ function lib.concat(...)
|
||||
return r
|
||||
end
|
||||
|
||||
-- works with pairs on tables
|
||||
-- returns the kv pair, or nil and the number of pairs iterated
|
||||
function --[[@delayloaded-start@]] lib.at(tbl, index)
|
||||
checkArg(1, tbl, "table")
|
||||
checkArg(2, index, "number", "nil")
|
||||
local current_index = 1
|
||||
for k,v in pairs(tbl) do
|
||||
if current_index == index then
|
||||
return k,v
|
||||
end
|
||||
current_index = current_index + 1
|
||||
end
|
||||
return nil, current_index - 1 -- went one too far
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
return lib,{adjust=adjust,view=view}
|
||||
|
@ -1,13 +1,13 @@
|
||||
NAME
|
||||
head - Print the first 10 lines of each FILE to stdout.
|
||||
|
||||
SYNOPSIS
|
||||
head [OPTION]... [FILE]...
|
||||
|
||||
DESCRIPTION
|
||||
NAME
|
||||
head - Print the first 10 lines of each FILE to stdout.
|
||||
|
||||
SYNOPSIS
|
||||
head [OPTION]... [FILE]...
|
||||
|
||||
DESCRIPTION
|
||||
Print the first 10 lines of each FILE to stdout.
|
||||
With no FILE, or when FILE is -, read stdin.
|
||||
|
||||
|
||||
--bytes=[-]n print the first n bytes of each file'
|
||||
with the leading '-', print all but the last
|
||||
n bytes of each file
|
||||
@ -18,14 +18,14 @@ DESCRIPTION
|
||||
-v, --verbose always print headers giving file names
|
||||
--help print help message
|
||||
|
||||
EXAMPLES
|
||||
head
|
||||
head -
|
||||
Read next 10 lines from standard in and print to standard out, then close.
|
||||
|
||||
head file.txt
|
||||
Print first 10 lines of file.txt and print to stdout
|
||||
|
||||
head -n 32 file.txt
|
||||
Print first 32 lines of file.txt and print to stdout
|
||||
|
||||
EXAMPLES
|
||||
head
|
||||
head -
|
||||
Read next 10 lines from standard in and print to standard out, then close.
|
||||
|
||||
head file.txt
|
||||
Print first 10 lines of file.txt and print to stdout
|
||||
|
||||
head -n 32 file.txt
|
||||
Print first 32 lines of file.txt and print to stdout
|
||||
|
||||
|
@ -0,0 +1,103 @@
|
||||
NAME
|
||||
install - installs files from a source filesystem to a target filesystem
|
||||
|
||||
SYNOPSIS
|
||||
install [name] [OPTIONS]...
|
||||
|
||||
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. 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
|
||||
--from=ADDR
|
||||
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
|
||||
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
|
||||
Install PATH from source. PATH is relative to the root of the source filesystem or path given by --from. The default is .
|
||||
|
||||
--root=PATH
|
||||
Same as --fromDir but for the target filesystem.
|
||||
|
||||
--toDir=PATH
|
||||
Same as --root. Either can be used. It is meaningless to specify both and is not documented which takes precedence in such a case.
|
||||
|
||||
-u, --update
|
||||
Indicates that install should prompt the user before modifying files. This invokes -i and -u for /bin/cp.
|
||||
|
||||
The following can override settings defined in .prop in the source filesystem.
|
||||
|
||||
--label=NAME
|
||||
use NAME for label instead of any value specified by .prop, --name is deprecated
|
||||
|
||||
--nosetlabel
|
||||
do not set target label. --nolabelset is deprecated
|
||||
|
||||
--nosetboot
|
||||
do not set target as default boot device when rebooting. --noboot is deprecated
|
||||
|
||||
--noreboot
|
||||
do not reboot after install
|
||||
|
||||
.prop
|
||||
.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
|
||||
|
||||
ENV_.install.from:
|
||||
This is the path of the selected source filesystem to install from. It should be the path to the executing .install
|
||||
example: /mnt/ABC
|
||||
|
||||
ENV_.install.to:
|
||||
This is the path of the selected target filesystem to install to.
|
||||
example: /
|
||||
|
||||
_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 "."
|
||||
example: .
|
||||
|
||||
_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 "."
|
||||
example: .
|
||||
|
||||
_ENV.install.update
|
||||
Assigned value of --update, see OPTIONS
|
||||
|
||||
_ENV.install.label
|
||||
Assigned value of --name or .prop's label, see OPTIONS
|
||||
|
||||
_ENV.install.setlabel
|
||||
Assigned value of .prop's setlabel unless --nolabelset, see OPTIONS
|
||||
|
||||
_ENV.install.setboot
|
||||
Assigned value of .prop's boot unless --nosetboot, see OPTIONS
|
||||
|
||||
_ENV.install.reboot
|
||||
Assigned value of .prop's reboot unless --noreboot, see OPTIONS
|
||||
|
||||
EXAMPLES
|
||||
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 .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.
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
os.execute(install.from.."/oppm.lua")
|
Loading…
x
Reference in New Issue
Block a user