mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-29 15:24:19 +01:00
3048 lines
79 KiB
Plaintext
3048 lines
79 KiB
Plaintext
--@luaPackageFileSignature
|
|
--@luaPackageFileSeparator
|
|
--@address.lua
|
|
local computer = require("computer")
|
|
|
|
io.write(computer.address())
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@alias.lua
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
|
|
if #args == 0 then
|
|
for name, value in shell.aliases() do
|
|
io.write(name .. " " .. value .. "\n")
|
|
end
|
|
elseif #args == 1 then
|
|
local value = shell.getAlias(args[1])
|
|
if value then
|
|
io.write(value)
|
|
else
|
|
io.stderr:write("no such alias")
|
|
end
|
|
else
|
|
shell.setAlias(args[1], args[2])
|
|
io.write("alias created: " .. args[1] .. " -> " .. args[2])
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@archive.lua
|
|
local archive = require("lib/archive")
|
|
local shell = require("shell")
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local args = {...}
|
|
|
|
archive.debugMode = true
|
|
|
|
if args[1] == "pack" then
|
|
if not args[2] or not args[3] then
|
|
print(" ")
|
|
print("Использование: archive pack <имя архива> <архивируемая папка>")
|
|
print(" ")
|
|
return
|
|
end
|
|
archive.pack(args[2], args[3])
|
|
elseif args[1] == "unpack" then
|
|
if not args[2] or not args[3] then
|
|
print(" ")
|
|
print("Использование: archive unpack <путь к архиву> <папка для сохранения файлов>")
|
|
print(" ")
|
|
return
|
|
end
|
|
archive.unpack(args[2], args[3])
|
|
elseif args[1] == "download" then
|
|
if not args[2] or not args[3] then
|
|
print(" ")
|
|
print("Использование: archive download <URL-ссылка на архив> <папка для сохранения файлов>")
|
|
print(" ")
|
|
return
|
|
end
|
|
print(" ")
|
|
print("Загрузка файла по ссылке \"" .. args[2] .. "\"")
|
|
shell.execute("wget " .. args[2] .. "TempFile.pkg -fq")
|
|
archive.unpack("TempFile.pkg", args[3])
|
|
shell.execute("rm TempFile.pkg")
|
|
else
|
|
print(" ")
|
|
print("Использование: archive <pack/unpack/download> ...")
|
|
print(" ")
|
|
return
|
|
end
|
|
|
|
archive.debugMode = false
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@cat.lua
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
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 file, reason = io.open(shell.resolve(args[i]))
|
|
if not file then
|
|
io.stderr:write(tostring(reason) .. "\n")
|
|
os.exit(false)
|
|
end
|
|
repeat
|
|
local line = file:read("*L")
|
|
if line then
|
|
io.write(line)
|
|
end
|
|
until not line
|
|
file:close()
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@cd.lua
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: cd <dirname>")
|
|
else
|
|
local result, reason = shell.setWorkingDirectory(shell.resolve(args[1]))
|
|
if not result then
|
|
io.stderr:write(reason)
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@clear.lua
|
|
local ecs = require("ECSAPI")
|
|
ecs.prepareToExit()
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@components.lua
|
|
local component = require("component")
|
|
local shell = require("shell")
|
|
local text = require("text")
|
|
|
|
local args, options = shell.parse(...)
|
|
local count = tonumber(options.limit) or math.huge
|
|
|
|
local components = {}
|
|
local padTo = 1
|
|
|
|
if #args == 0 then -- get all components if no filters given.
|
|
args[1] = ""
|
|
end
|
|
for _, filter in ipairs(args) do
|
|
for address, name in component.list(filter) do
|
|
if name:len() > padTo then
|
|
padTo = name:len() + 2
|
|
end
|
|
components[address] = name
|
|
end
|
|
end
|
|
|
|
padTo = padTo + 8 - padTo % 8
|
|
for address, name in pairs(components) do
|
|
io.write(text.padRight(name, padTo) .. address .. '\n')
|
|
|
|
if options.l then
|
|
local proxy = component.proxy(address)
|
|
local padTo = 1
|
|
local methods = {}
|
|
for name, member in pairs(proxy) do
|
|
if type(member) == "table" or type(member) == "function" then
|
|
if name:len() > padTo then
|
|
padTo = name:len() + 2
|
|
end
|
|
table.insert(methods, name)
|
|
end
|
|
end
|
|
table.sort(methods)
|
|
padTo = padTo + 8 - padTo % 8
|
|
|
|
for _, name in ipairs(methods) do
|
|
local doc = tostring(proxy[name])
|
|
io.write(" " .. text.padRight(name, padTo) .. doc .. '\n')
|
|
end
|
|
end
|
|
|
|
count = count - 1
|
|
if count <= 0 then
|
|
break
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@cp.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args < 2 then
|
|
io.write("Usage: cp [-inrv] <from...> <to>\n")
|
|
io.write(" -i: prompt before overwrite (overrides -n option).\n")
|
|
io.write(" -n: do not overwrite an existing file.\n")
|
|
io.write(" -r: copy directories recursively.\n")
|
|
io.write(" -u: copy only when the SOURCE file differs from the destination\n")
|
|
io.write(" file or when the destination file is missing.\n")
|
|
io.write(" -v: verbose output.\n")
|
|
io.write(" -x: stay on original source file system.")
|
|
return
|
|
end
|
|
|
|
local from = {}
|
|
for i = 1, #args - 1 do
|
|
table.insert(from, shell.resolve(args[i]))
|
|
end
|
|
local to = shell.resolve(args[#args])
|
|
|
|
local function status(from, to)
|
|
if options.v then
|
|
io.write(from .. " -> " .. to .. "\n")
|
|
end
|
|
os.sleep(0) -- allow interrupting
|
|
end
|
|
|
|
local result, reason
|
|
|
|
local function prompt(message)
|
|
io.write(message .. " [Y/n]\n")
|
|
local result = io.read()
|
|
return result and (result == "" or result:sub(1, 1):lower() == "y")
|
|
end
|
|
|
|
local function areEqual(path1, path2)
|
|
local f1 = io.open(path1, "rb")
|
|
if not f1 then
|
|
return nil, "could not open `" .. path1 .. "' for update test"
|
|
end
|
|
local f2 = io.open(path2, "rb")
|
|
if not f2 then
|
|
f1:close()
|
|
return nil, "could not open `" .. path2 .. "' for update test"
|
|
end
|
|
local result = true
|
|
local chunkSize = 4 * 1024
|
|
repeat
|
|
local s1, s2 = f1:read(chunkSize), f2:read(chunkSize)
|
|
if s1 ~= s2 then
|
|
result = false
|
|
break
|
|
end
|
|
until not s1 or not s2
|
|
f1:close()
|
|
f2:close()
|
|
return result
|
|
end
|
|
|
|
local function isMount(path)
|
|
path = fs.canonical(path)
|
|
for _, mountPath in fs.mounts() do
|
|
if path == fs.canonical(mountPath) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function recurse(fromPath, toPath)
|
|
status(fromPath, toPath)
|
|
if fs.isDirectory(fromPath) then
|
|
if not options.r then
|
|
io.write("omitting directory `" .. fromPath .. "'\n")
|
|
return true
|
|
end
|
|
if fs.canonical(fromPath) == fs.canonical(fs.path(toPath)) then
|
|
return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'\n"
|
|
end
|
|
if fs.exists(toPath) and not fs.isDirectory(toPath) then
|
|
-- my real cp always does this, even with -f, -n or -i.
|
|
return nil, "cannot overwrite non-directory `" .. toPath .. "' with directory `" .. fromPath .. "'"
|
|
end
|
|
if options.x and isMount(fromPath) then
|
|
return true
|
|
end
|
|
fs.makeDirectory(toPath)
|
|
for file in fs.list(fromPath) do
|
|
local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file))
|
|
if not result then
|
|
return nil, reason
|
|
end
|
|
end
|
|
return true
|
|
else
|
|
if fs.exists(toPath) then
|
|
if fs.canonical(fromPath) == fs.canonical(toPath) 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
|
|
end
|
|
fs.remove(toPath)
|
|
end
|
|
return fs.copy(fromPath, toPath)
|
|
end
|
|
end
|
|
for _, fromPath in ipairs(from) do
|
|
local toPath = to
|
|
if fs.isDirectory(toPath) then
|
|
toPath = fs.concat(toPath, fs.name(fromPath))
|
|
end
|
|
result, reason = recurse(fromPath, toPath)
|
|
if not result then
|
|
error(reason, 0)
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@date.lua
|
|
io.write(os.date("%F %T"))
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@df.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
local text = require("text")
|
|
|
|
local args, options = shell.parse(...)
|
|
|
|
local function formatSize(size)
|
|
if not options.h then
|
|
return tostring(size)
|
|
end
|
|
local sizes = {"", "K", "M", "G"}
|
|
local unit = 1
|
|
local power = options.si and 1000 or 1024
|
|
while size > power and unit < #sizes do
|
|
unit = unit + 1
|
|
size = size / power
|
|
end
|
|
return math.floor(size * 10) / 10 .. sizes[unit]
|
|
end
|
|
|
|
local mounts = {}
|
|
if #args == 0 then
|
|
for proxy, path in fs.mounts() do
|
|
mounts[path] = proxy
|
|
end
|
|
else
|
|
for i = 1, #args do
|
|
local proxy, path = fs.get(args[i])
|
|
if not proxy then
|
|
io.stderr:write(args[i], ": no such file or directory\n")
|
|
else
|
|
mounts[path] = proxy
|
|
end
|
|
end
|
|
end
|
|
|
|
local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}}
|
|
for path, proxy in pairs(mounts) do
|
|
local label = proxy.getLabel() or proxy.address
|
|
local used, total = proxy.spaceUsed(), proxy.spaceTotal()
|
|
local available, percent
|
|
if total == math.huge then
|
|
used = used or "N/A"
|
|
available = "unlimited"
|
|
percent = "0%"
|
|
else
|
|
available = total - used
|
|
percent = used / total
|
|
if percent ~= percent then -- NaN
|
|
available = "N/A"
|
|
percent = "N/A"
|
|
else
|
|
percent = math.ceil(percent * 100) .. "%"
|
|
end
|
|
end
|
|
table.insert(result, {label, formatSize(used), formatSize(available), tostring(percent), path})
|
|
end
|
|
|
|
local m = {}
|
|
for _, row in ipairs(result) do
|
|
for col, value in ipairs(row) do
|
|
m[col] = math.max(m[col] or 1, value:len())
|
|
end
|
|
end
|
|
|
|
for _, row in ipairs(result) do
|
|
for col, value in ipairs(row) do
|
|
io.write(text.padRight(value, m[col] + 2))
|
|
end
|
|
io.write("\n")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@dmesg.lua
|
|
local event = require "event"
|
|
local component = require "component"
|
|
local keyboard = require "keyboard"
|
|
|
|
local args = {...}
|
|
|
|
local interactive = io.output() == io.stdout
|
|
local color, isPal, evt
|
|
if interactive then
|
|
color, isPal = component.gpu.getForeground()
|
|
end
|
|
io.write("Press 'Ctrl-C' to exit\n")
|
|
pcall(function()
|
|
repeat
|
|
if #args > 0 then
|
|
evt = table.pack(event.pullMultiple("interrupted", table.unpack(args)))
|
|
else
|
|
evt = table.pack(event.pull())
|
|
end
|
|
if interactive then component.gpu.setForeground(0xCC2200) end
|
|
io.write("[" .. os.date("%T") .. "] ")
|
|
if interactive then component.gpu.setForeground(0x44CC00) end
|
|
io.write(tostring(evt[1]) .. string.rep(" ", math.max(10 - #tostring(evt[1]), 0) + 1))
|
|
if interactive then component.gpu.setForeground(0xB0B00F) end
|
|
io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2])))
|
|
if interactive then component.gpu.setForeground(0xFFFFFF) end
|
|
if evt.n > 2 then
|
|
for i = 3, evt.n do
|
|
io.write(" " .. tostring(evt[i]))
|
|
end
|
|
end
|
|
|
|
io.write("\n")
|
|
until evt[1] == "interrupted"
|
|
end)
|
|
if interactive then
|
|
component.gpu.setForeground(color, isPal)
|
|
end
|
|
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@echo.lua
|
|
local args = table.pack(...)
|
|
for i = 1, #args do
|
|
if i > 1 then
|
|
io.write(" ")
|
|
end
|
|
io.write(args[i])
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@edit.lua
|
|
local component = require("component")
|
|
local event = require("event")
|
|
local fs = require("filesystem")
|
|
local keyboard = require("keyboard")
|
|
local shell = require("shell")
|
|
local term = require("term")
|
|
local text = require("text")
|
|
local unicode = require("unicode")
|
|
|
|
if not term.isAvailable() then
|
|
return
|
|
end
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: edit <filename>")
|
|
return
|
|
end
|
|
|
|
local filename = shell.resolve(args[1])
|
|
|
|
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
|
|
|
|
if not fs.exists(filename) then
|
|
if fs.isDirectory(filename) then
|
|
io.stderr:write("file is a directory")
|
|
return
|
|
elseif readonly then
|
|
io.stderr:write("file system is read only")
|
|
return
|
|
end
|
|
end
|
|
|
|
local function loadConfig()
|
|
-- Try to load user settings.
|
|
local env = {}
|
|
local config = loadfile("/etc/edit.cfg", nil, env)
|
|
if config then
|
|
pcall(config)
|
|
end
|
|
-- Fill in defaults.
|
|
env.keybinds = env.keybinds or {
|
|
left = {{"left"}},
|
|
right = {{"right"}},
|
|
up = {{"up"}},
|
|
down = {{"down"}},
|
|
home = {{"home"}},
|
|
eol = {{"end"}},
|
|
pageUp = {{"pageUp"}},
|
|
pageDown = {{"pageDown"}},
|
|
|
|
backspace = {{"back"}},
|
|
delete = {{"delete"}},
|
|
deleteLine = {{"control", "delete"}, {"shift", "delete"}},
|
|
newline = {{"enter"}},
|
|
|
|
save = {{"control", "s"}},
|
|
close = {{"control", "w"}},
|
|
find = {{"control", "f"}},
|
|
findnext = {{"control", "g"}, {"control", "n"}, {"f3"}}
|
|
}
|
|
-- Generate config file if it didn't exist.
|
|
if not config then
|
|
local root = fs.get("/")
|
|
if root and not root.isReadOnly() then
|
|
fs.makeDirectory("/etc")
|
|
local f = io.open("/etc/edit.cfg", "w")
|
|
if f then
|
|
local serialization = require("serialization")
|
|
for k, v in pairs(env) do
|
|
f:write(k.."="..tostring(serialization.serialize(v, math.huge)).."\n")
|
|
end
|
|
f:close()
|
|
end
|
|
end
|
|
end
|
|
return env
|
|
end
|
|
|
|
term.clear()
|
|
term.setCursorBlink(true)
|
|
|
|
local running = true
|
|
local buffer = {}
|
|
local scrollX, scrollY = 0, 0
|
|
local config = loadConfig()
|
|
|
|
local getKeyBindHandler -- forward declaration for refind()
|
|
|
|
local function helpStatusText()
|
|
local function prettifyKeybind(label, command)
|
|
local keybind = type(config.keybinds) == "table" and config.keybinds[command]
|
|
if type(keybind) ~= "table" or type(keybind[1]) ~= "table" then return "" end
|
|
local alt, control, shift, key
|
|
for _, value in ipairs(keybind[1]) do
|
|
if value == "alt" then alt = true
|
|
elseif value == "control" then control = true
|
|
elseif value == "shift" then shift = true
|
|
else key = value end
|
|
end
|
|
if not key then return "" end
|
|
return label .. ": [" ..
|
|
(control and "Ctrl+" or "") ..
|
|
(alt and "Alt+" or "") ..
|
|
(shift and "Shift+" or "") ..
|
|
unicode.upper(key) ..
|
|
"] "
|
|
end
|
|
return prettifyKeybind("Save", "save") ..
|
|
prettifyKeybind("Close", "close") ..
|
|
prettifyKeybind("Find", "find")
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local function setStatus(value)
|
|
local w, h = component.gpu.getResolution()
|
|
component.gpu.set(1, h, text.padRight(unicode.sub(value, 1, w - 10), w - 10))
|
|
end
|
|
|
|
local function getSize()
|
|
local w, h = component.gpu.getResolution()
|
|
return w, h - 1
|
|
end
|
|
|
|
local function getCursor()
|
|
local cx, cy = term.getCursor()
|
|
return cx + scrollX, cy + scrollY
|
|
end
|
|
|
|
local function line()
|
|
local cbx, cby = getCursor()
|
|
return buffer[cby]
|
|
end
|
|
|
|
local function setCursor(nbx, nby)
|
|
local w, h = getSize()
|
|
nby = math.max(1, math.min(#buffer, nby))
|
|
|
|
local ncy = nby - scrollY
|
|
if ncy > h then
|
|
term.setCursorBlink(false)
|
|
local sy = nby - h
|
|
local dy = math.abs(scrollY - sy)
|
|
scrollY = sy
|
|
component.gpu.copy(1, 1 + dy, w, h - dy, 0, -dy)
|
|
for by = nby - (dy - 1), nby do
|
|
local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
|
|
component.gpu.set(1, by - scrollY, str)
|
|
end
|
|
elseif ncy < 1 then
|
|
term.setCursorBlink(false)
|
|
local sy = nby - 1
|
|
local dy = math.abs(scrollY - sy)
|
|
scrollY = sy
|
|
component.gpu.copy(1, 1, w, h - dy, 0, dy)
|
|
for by = nby, nby + (dy - 1) do
|
|
local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
|
|
component.gpu.set(1, by - scrollY, str)
|
|
end
|
|
end
|
|
term.setCursor(term.getCursor(), nby - scrollY)
|
|
|
|
nbx = math.max(1, math.min(unicode.len(line()) + 1, nbx))
|
|
local ncx = nbx - scrollX
|
|
if ncx > w then
|
|
term.setCursorBlink(false)
|
|
local sx = nbx - w
|
|
local dx = math.abs(scrollX - sx)
|
|
scrollX = sx
|
|
component.gpu.copy(1 + dx, 1, w - dx, h, -dx, 0)
|
|
for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
|
|
local str = unicode.sub(buffer[by], nbx - (dx - 1), nbx)
|
|
str = text.padRight(str, dx)
|
|
component.gpu.set(1 + (w - dx), by - scrollY, str)
|
|
end
|
|
elseif ncx < 1 then
|
|
term.setCursorBlink(false)
|
|
local sx = nbx - 1
|
|
local dx = math.abs(scrollX - sx)
|
|
scrollX = sx
|
|
component.gpu.copy(1, 1, w - dx, h, dx, 0)
|
|
for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
|
|
local str = unicode.sub(buffer[by], nbx, nbx + dx)
|
|
--str = text.padRight(str, dx)
|
|
component.gpu.set(1, by - scrollY, str)
|
|
end
|
|
end
|
|
term.setCursor(nbx - scrollX, nby - scrollY)
|
|
|
|
component.gpu.set(w - 9, h + 1, text.padLeft(string.format("%d,%d", nby, nbx), 10))
|
|
end
|
|
|
|
local function highlight(bx, by, length, enabled)
|
|
local w, h = getSize()
|
|
local cx, cy = bx - scrollX, by - scrollY
|
|
cx = math.max(1, math.min(w, cx))
|
|
cy = math.max(1, math.min(h, cy))
|
|
length = math.max(1, math.min(w - cx, length))
|
|
|
|
local fg, fgp = component.gpu.getForeground()
|
|
local bg, bgp = component.gpu.getBackground()
|
|
if enabled then
|
|
component.gpu.setForeground(bg, bgp)
|
|
component.gpu.setBackground(fg, fgp)
|
|
end
|
|
local value = ""
|
|
for x = cx, cx + length - 1 do
|
|
value = value .. component.gpu.get(x, cy)
|
|
end
|
|
component.gpu.set(cx, cy, value)
|
|
if enabled then
|
|
component.gpu.setForeground(fg, fgp)
|
|
component.gpu.setBackground(bg, bgp)
|
|
end
|
|
end
|
|
|
|
local function home()
|
|
local cbx, cby = getCursor()
|
|
setCursor(1, cby)
|
|
end
|
|
|
|
local function ende()
|
|
local cbx, cby = getCursor()
|
|
setCursor(unicode.len(line()) + 1, cby)
|
|
end
|
|
|
|
local function left()
|
|
local cbx, cby = getCursor()
|
|
if cbx > 1 then
|
|
setCursor(cbx - 1, cby)
|
|
return true -- for backspace
|
|
elseif cby > 1 then
|
|
setCursor(cbx, cby - 1)
|
|
ende()
|
|
return true -- again, for backspace
|
|
end
|
|
end
|
|
|
|
local function right(n)
|
|
n = n or 1
|
|
local cbx, cby = getCursor()
|
|
local be = unicode.len(line()) + 1
|
|
if cbx < be then
|
|
setCursor(cbx + n, cby)
|
|
elseif cby < #buffer then
|
|
setCursor(1, cby + 1)
|
|
end
|
|
end
|
|
|
|
local function up(n)
|
|
n = n or 1
|
|
local cbx, cby = getCursor()
|
|
if cby > 1 then
|
|
setCursor(cbx, cby - n)
|
|
if getCursor() > unicode.len(line()) then
|
|
ende()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function down(n)
|
|
n = n or 1
|
|
local cbx, cby = getCursor()
|
|
if cby < #buffer then
|
|
setCursor(cbx, cby + n)
|
|
if getCursor() > unicode.len(line()) then
|
|
ende()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function delete(fullRow)
|
|
local cx, cy = term.getCursor()
|
|
local cbx, cby = getCursor()
|
|
local w, h = getSize()
|
|
local function deleteRow(row)
|
|
local content = table.remove(buffer, row)
|
|
local rcy = cy + (row - cby)
|
|
if rcy <= h then
|
|
component.gpu.copy(1, rcy + 1, w, h - rcy, 0, -1)
|
|
component.gpu.set(1, h, text.padRight(buffer[row + (h - rcy)], w))
|
|
end
|
|
return content
|
|
end
|
|
if fullRow then
|
|
term.setCursorBlink(false)
|
|
if #buffer > 1 then
|
|
deleteRow(cby)
|
|
else
|
|
buffer[cby] = ""
|
|
component.gpu.fill(1, cy, w, 1, " ")
|
|
end
|
|
setCursor(1, cby)
|
|
elseif cbx <= unicode.len(line()) then
|
|
term.setCursorBlink(false)
|
|
buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
|
|
unicode.sub(line(), cbx + 1)
|
|
component.gpu.copy(cx + 1, cy, w - cx, 1, -1, 0)
|
|
local br = cbx + (w - cx)
|
|
local char = unicode.sub(line(), br, br)
|
|
if not char or unicode.len(char) == 0 then
|
|
char = " "
|
|
end
|
|
component.gpu.set(w, cy, char)
|
|
elseif cby < #buffer then
|
|
term.setCursorBlink(false)
|
|
local append = deleteRow(cby + 1)
|
|
buffer[cby] = buffer[cby] .. append
|
|
component.gpu.set(cx, cy, append)
|
|
else
|
|
return
|
|
end
|
|
setStatus(helpStatusText())
|
|
end
|
|
|
|
local function insert(value)
|
|
if not value or unicode.len(value) < 1 then
|
|
return
|
|
end
|
|
term.setCursorBlink(false)
|
|
local cx, cy = term.getCursor()
|
|
local cbx, cby = getCursor()
|
|
local w, h = getSize()
|
|
buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
|
|
value ..
|
|
unicode.sub(line(), cbx)
|
|
local len = unicode.len(value)
|
|
local n = w - (cx - 1) - len
|
|
if n > 0 then
|
|
component.gpu.copy(cx, cy, n, 1, len, 0)
|
|
end
|
|
component.gpu.set(cx, cy, value)
|
|
right(len)
|
|
setStatus(helpStatusText())
|
|
end
|
|
|
|
local function enter()
|
|
term.setCursorBlink(false)
|
|
local cx, cy = term.getCursor()
|
|
local cbx, cby = getCursor()
|
|
local w, h = getSize()
|
|
table.insert(buffer, cby + 1, unicode.sub(buffer[cby], cbx))
|
|
buffer[cby] = unicode.sub(buffer[cby], 1, cbx - 1)
|
|
component.gpu.fill(cx, cy, w - (cx - 1), 1, " ")
|
|
if cy < h then
|
|
if cy < h - 1 then
|
|
component.gpu.copy(1, cy + 1, w, h - (cy + 1), 0, 1)
|
|
end
|
|
component.gpu.set(1, cy + 1, text.padRight(buffer[cby + 1], w))
|
|
end
|
|
setCursor(1, cby + 1)
|
|
setStatus(helpStatusText())
|
|
end
|
|
|
|
local findText = ""
|
|
|
|
local function find()
|
|
local w, h = getSize()
|
|
local cx, cy = term.getCursor()
|
|
local cbx, cby = getCursor()
|
|
local ibx, iby = cbx, cby
|
|
while running do
|
|
if unicode.len(findText) > 0 then
|
|
local sx, sy
|
|
for syo = 1, #buffer do -- iterate lines with wraparound
|
|
sy = (iby + syo - 1 + #buffer - 1) % #buffer + 1
|
|
sx = string.find(buffer[sy], findText, syo == 1 and ibx or 1, true)
|
|
if sx and (sx >= ibx or syo > 1) then
|
|
break
|
|
end
|
|
end
|
|
if not sx then -- special case for single matches
|
|
sy = iby
|
|
sx = string.find(buffer[sy], findText, nil, true)
|
|
end
|
|
if sx then
|
|
cbx, cby = sx, sy
|
|
setCursor(cbx, cby)
|
|
highlight(cbx, cby, unicode.len(findText), true)
|
|
end
|
|
end
|
|
term.setCursor(7 + unicode.len(findText), h + 1)
|
|
setStatus("Find: " .. findText)
|
|
|
|
local _, _, char, code = event.pull("key_down")
|
|
local handler, name = getKeyBindHandler(code)
|
|
highlight(cbx, cby, unicode.len(findText), false)
|
|
if name == "newline" then
|
|
break
|
|
elseif name == "close" then
|
|
handler()
|
|
elseif name == "backspace" then
|
|
findText = unicode.sub(findText, 1, -2)
|
|
elseif name == "find" or name == "findnext" then
|
|
ibx = cbx + 1
|
|
iby = cby
|
|
elseif not keyboard.isControl(char) then
|
|
findText = findText .. unicode.char(char)
|
|
end
|
|
end
|
|
setCursor(cbx, cby)
|
|
setStatus(helpStatusText())
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local keyBindHandlers = {
|
|
left = left,
|
|
right = right,
|
|
up = up,
|
|
down = down,
|
|
home = home,
|
|
eol = ende,
|
|
pageUp = function()
|
|
local w, h = getSize()
|
|
up(h - 1)
|
|
end,
|
|
pageDown = function()
|
|
local w, h = getSize()
|
|
down(h - 1)
|
|
end,
|
|
|
|
backspace = function()
|
|
if not readonly and left() then
|
|
delete()
|
|
end
|
|
end,
|
|
delete = function()
|
|
if not readonly then
|
|
delete()
|
|
end
|
|
end,
|
|
deleteLine = function()
|
|
if not readonly then
|
|
delete(true)
|
|
end
|
|
end,
|
|
newline = function()
|
|
if not readonly then
|
|
enter()
|
|
end
|
|
end,
|
|
|
|
save = function()
|
|
if readonly then return end
|
|
local new = not fs.exists(filename)
|
|
local backup
|
|
if not new then
|
|
backup = filename .. "~"
|
|
for i = 1, math.huge do
|
|
if not fs.exists(backup) then
|
|
break
|
|
end
|
|
backup = filename .. "~" .. i
|
|
end
|
|
fs.copy(filename, backup)
|
|
end
|
|
local f, reason = io.open(filename, "w")
|
|
if f then
|
|
local chars, firstLine = 0, true
|
|
for _, line in ipairs(buffer) do
|
|
if not firstLine then
|
|
line = "\n" .. line
|
|
end
|
|
firstLine = false
|
|
f:write(line)
|
|
chars = chars + unicode.len(line)
|
|
end
|
|
f:close()
|
|
local format
|
|
if new then
|
|
format = [["%s" [New] %dL,%dC written]]
|
|
else
|
|
format = [["%s" %dL,%dC written]]
|
|
end
|
|
setStatus(string.format(format, fs.name(filename), #buffer, chars))
|
|
else
|
|
setStatus(reason)
|
|
end
|
|
if not new then
|
|
fs.remove(backup)
|
|
end
|
|
end,
|
|
close = function()
|
|
-- TODO ask to save if changed
|
|
running = false
|
|
end,
|
|
find = function()
|
|
findText = ""
|
|
find()
|
|
end,
|
|
findnext = find
|
|
}
|
|
|
|
getKeyBindHandler = function(code)
|
|
if type(config.keybinds) ~= "table" then return end
|
|
-- Look for matches, prefer more 'precise' keybinds, e.g. prefer
|
|
-- ctrl+del over del.
|
|
local result, resultName, resultWeight = nil, nil, 0
|
|
for command, keybinds in pairs(config.keybinds) do
|
|
if type(keybinds) == "table" and keyBindHandlers[command] then
|
|
for _, keybind in ipairs(keybinds) do
|
|
if type(keybind) == "table" then
|
|
local alt, control, shift, key
|
|
for _, value in ipairs(keybind) do
|
|
if value == "alt" then alt = true
|
|
elseif value == "control" then control = true
|
|
elseif value == "shift" then shift = true
|
|
else key = value end
|
|
end
|
|
if (not alt or keyboard.isAltDown()) and
|
|
(not control or keyboard.isControlDown()) and
|
|
(not shift or keyboard.isShiftDown()) and
|
|
code == keyboard.keys[key] and
|
|
#keybind > resultWeight
|
|
then
|
|
resultWeight = #keybind
|
|
resultName = command
|
|
result = keyBindHandlers[command]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return result, resultName
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local function onKeyDown(char, code)
|
|
local handler = getKeyBindHandler(code)
|
|
if handler then
|
|
handler()
|
|
elseif readonly and code == keyboard.keys.q then
|
|
running = false
|
|
elseif not readonly then
|
|
if not keyboard.isControl(char) then
|
|
insert(unicode.char(char))
|
|
elseif unicode.char(char) == "\t" then
|
|
insert(" ")
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onClipboard(value)
|
|
value = value:gsub("\r\n", "\n")
|
|
local cbx, cby = getCursor()
|
|
local start = 1
|
|
local l = value:find("\n", 1, true)
|
|
if l then
|
|
repeat
|
|
local line = string.sub(value, start, l - 1)
|
|
line = text.detab(line, 2)
|
|
insert(line)
|
|
enter()
|
|
start = l + 1
|
|
l = value:find("\n", start, true)
|
|
until not l
|
|
end
|
|
insert(string.sub(value, start))
|
|
end
|
|
|
|
local function onClick(x, y)
|
|
setCursor(x + scrollX, y + scrollY)
|
|
end
|
|
|
|
local function onScroll(direction)
|
|
local cbx, cby = getCursor()
|
|
setCursor(cbx, cby - direction * 12)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
do
|
|
local f = io.open(filename)
|
|
if f then
|
|
local w, h = getSize()
|
|
local chars = 0
|
|
for line in f:lines() do
|
|
if line:sub(-1) == "\r" then
|
|
line = line:sub(1, -2)
|
|
end
|
|
table.insert(buffer, line)
|
|
chars = chars + unicode.len(line)
|
|
if #buffer <= h then
|
|
component.gpu.set(1, #buffer, line)
|
|
end
|
|
end
|
|
f:close()
|
|
if #buffer == 0 then
|
|
table.insert(buffer, "")
|
|
end
|
|
local format
|
|
if readonly then
|
|
format = [["%s" [readonly] %dL,%dC]]
|
|
else
|
|
format = [["%s" %dL,%dC]]
|
|
end
|
|
setStatus(string.format(format, fs.name(filename), #buffer, chars))
|
|
else
|
|
table.insert(buffer, "")
|
|
setStatus(string.format([["%s" [New File] ]], fs.name(filename)))
|
|
end
|
|
setCursor(1, 1)
|
|
end
|
|
|
|
while running do
|
|
local event, address, arg1, arg2, arg3 = event.pull()
|
|
if type(address) == "string" and component.isPrimary(address) then
|
|
local blink = true
|
|
if event == "key_down" then
|
|
onKeyDown(arg1, arg2)
|
|
elseif event == "clipboard" and not readonly then
|
|
onClipboard(arg1)
|
|
elseif event == "touch" or event == "drag" then
|
|
onClick(arg1, arg2)
|
|
elseif event == "scroll" then
|
|
onScroll(arg3)
|
|
else
|
|
blink = false
|
|
end
|
|
if blink then
|
|
term.setCursorBlink(true)
|
|
term.setCursorBlink(true) -- force toggle to caret
|
|
end
|
|
end
|
|
end
|
|
|
|
term.clear()
|
|
term.setCursorBlink(false)
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@event.lua
|
|
local event = require "event"
|
|
|
|
while true do
|
|
local cyka = {event.pull()}
|
|
print("Ивент: "..cyka[1])
|
|
for i=2,#cyka do
|
|
print("Аргумент "..(i).." = "..cyka[i])
|
|
end
|
|
print(" ")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@flash.lua
|
|
local component = require("component")
|
|
local shell = require("shell")
|
|
local fs = require("filesystem")
|
|
|
|
local args, options = shell.parse(...)
|
|
|
|
if #args < 1 and not options.l then
|
|
io.write("Usage: flash [-qlr] [<bios.lua>] [label]\n")
|
|
io.write(" q: quiet mode, don't ask questions.\n")
|
|
io.write(" l: print current contents of installed EEPROM.\n")
|
|
io.write(" r: save the current contents of installed EEPROM to file.")
|
|
return
|
|
end
|
|
|
|
local function printRom()
|
|
local eeprom = component.eeprom
|
|
io.write(eeprom.get())
|
|
end
|
|
|
|
local function readRom()
|
|
local eeprom = component.eeprom
|
|
fileName = shell.resolve(args[1])
|
|
if not options.q then
|
|
if fs.exists(fileName) then
|
|
io.write("Are you sure you want to overwrite " .. fileName .. "?\n")
|
|
io.write("Type `y` to confirm.\n")
|
|
repeat
|
|
local response = io.read()
|
|
until response and response:lower():sub(1, 1) == "y"
|
|
end
|
|
io.write("Reading EEPROM " .. eeprom.address .. ".\n" )
|
|
end
|
|
local bios = eeprom.get()
|
|
local file = assert(io.open(fileName, "wb"))
|
|
file:write(bios)
|
|
file:close()
|
|
if not options.q then
|
|
io.write("All done!\nThe label is '" .. eeprom.getLabel() .. "'.\n")
|
|
end
|
|
end
|
|
|
|
local function writeRom()
|
|
local file = assert(io.open(args[1], "rb"))
|
|
local bios = file:read("*a")
|
|
file:close()
|
|
|
|
if not options.q then
|
|
io.write("Insert the EEPROM you would like to flash.\n")
|
|
io.write("When ready to write, type `y` to confirm.\n")
|
|
repeat
|
|
local response = io.read()
|
|
until response and response:lower():sub(1, 1) == "y"
|
|
io.write("Beginning to flash EEPROM.\n")
|
|
end
|
|
|
|
local eeprom = component.eeprom
|
|
|
|
if not options.q then
|
|
io.write("Flashing EEPROM " .. eeprom.address .. ".\n")
|
|
io.write("Please do NOT power down or restart your computer during this operation!\n")
|
|
end
|
|
|
|
eeprom.set(bios)
|
|
|
|
local label = args[2]
|
|
if not options.q and not label then
|
|
io.write("Enter new label for this EEPROM. Leave input blank to leave the label unchanged.\n")
|
|
label = io.read()
|
|
end
|
|
if label and #label > 0 then
|
|
eeprom.setLabel(label)
|
|
if not options.q then
|
|
io.write("Set label to '" .. eeprom.getLabel() .. "'.\n")
|
|
end
|
|
end
|
|
|
|
if not options.q then
|
|
io.write("All done! You can remove the EEPROM and re-insert the previous one now.\n")
|
|
end
|
|
end
|
|
|
|
if options.l then
|
|
printRom()
|
|
elseif options.r then
|
|
readRom()
|
|
else
|
|
writeRom()
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@hostname.lua
|
|
local args = {...}
|
|
if args[1] then
|
|
local file, reason = io.open("/etc/hostname", "w")
|
|
if not file then
|
|
io.stderr:write(reason .. "\n")
|
|
else
|
|
file:write(args[1])
|
|
file:close()
|
|
os.setenv("HOSTNAME", args[1])
|
|
os.setenv("PS1", "$HOSTNAME:$PWD# ")
|
|
end
|
|
else
|
|
local file = io.open("/etc/hostname")
|
|
if file then
|
|
io.write(file:read("*l"), "\n")
|
|
file:close()
|
|
else
|
|
io.stderr:write("Hostname not set\n")
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@install.lua
|
|
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 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
|
|
end
|
|
|
|
if #candidates == 0 then
|
|
io.write("No writable disks found, aborting.\n")
|
|
os.exit()
|
|
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")
|
|
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
|
|
end
|
|
|
|
local function findMount(address)
|
|
for fs, path in filesystem.mounts() do
|
|
if fs.address == component.get(address) then
|
|
return path
|
|
end
|
|
end
|
|
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 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")
|
|
computer.shutdown(true)
|
|
end
|
|
end
|
|
io.write("Returning to shell.\n")
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@label.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args < 1 then
|
|
io.write("Usage: label [-a] <fs> [<label>]\n")
|
|
io.write(" -a File system is specified via label or address instead of by path.")
|
|
return
|
|
end
|
|
|
|
local proxy, reason
|
|
if options.a then
|
|
proxy, reason = fs.proxy(args[1])
|
|
else
|
|
proxy, reason = fs.get(args[1])
|
|
end
|
|
if not proxy then
|
|
io.stderr:write(reason)
|
|
return
|
|
end
|
|
|
|
if #args < 2 then
|
|
io.stderr:write(proxy.getLabel() or "no label")
|
|
else
|
|
local result, reason = proxy.setLabel(args[2])
|
|
if not result then
|
|
io.stderr:write(reason or "could not set label")
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@ln.lua
|
|
local component = require("component")
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local dirs = shell.parse(...)
|
|
if #dirs == 0 then
|
|
io.write("Usage: ln <target> [<name>]")
|
|
return
|
|
end
|
|
|
|
local target = shell.resolve(dirs[1])
|
|
local linkpath
|
|
if #dirs > 1 then
|
|
linkpath = shell.resolve(dirs[2])
|
|
else
|
|
linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target))
|
|
end
|
|
|
|
local result, reason = fs.link(target, linkpath)
|
|
if not result then
|
|
io.stderr:write(reason)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@ls.lua
|
|
local component = require("component")
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
local text = require('text')
|
|
|
|
local dirs, options = shell.parse(...)
|
|
if #dirs == 0 then
|
|
table.insert(dirs, ".")
|
|
end
|
|
|
|
local function formatOutput()
|
|
return component.isAvailable("gpu") and io.output() == io.stdout
|
|
end
|
|
|
|
io.output():setvbuf("line")
|
|
for i = 1, #dirs do
|
|
local path = shell.resolve(dirs[i])
|
|
if #dirs > 1 then
|
|
if i > 1 then
|
|
io.write("\n")
|
|
end
|
|
io.write(path, ":\n")
|
|
end
|
|
local list, reason = fs.list(path)
|
|
if not list then
|
|
io.write(reason .. "\n")
|
|
else
|
|
local function setColor(c)
|
|
if formatOutput() and component.gpu.getForeground() ~= c then
|
|
io.stdout:flush()
|
|
component.gpu.setForeground(c)
|
|
end
|
|
end
|
|
local lsd = {}
|
|
local lsf = {}
|
|
local m = 1
|
|
for f in list do
|
|
m = math.max(m, f:len() + 2)
|
|
if f:sub(-1) == "/" then
|
|
if options.p then
|
|
table.insert(lsd, f)
|
|
else
|
|
table.insert(lsd, f:sub(1, -2))
|
|
end
|
|
else
|
|
table.insert(lsf, f)
|
|
end
|
|
end
|
|
table.sort(lsd)
|
|
table.sort(lsf)
|
|
setColor(0x00cc00)
|
|
|
|
local col = 1
|
|
local columns = math.huge
|
|
if formatOutput() then
|
|
columns = math.max(1, math.floor((component.gpu.getResolution() - 1) / m))
|
|
end
|
|
|
|
for _, d in ipairs(lsd) do
|
|
if options.a or d:sub(1, 1) ~= "." then
|
|
if options.l or not formatOutput() or col % columns == 0 then
|
|
io.write(d .. "\n")
|
|
else
|
|
io.write(text.padRight(d, m))
|
|
end
|
|
col = col + 1
|
|
end
|
|
end
|
|
|
|
for _, f in ipairs(lsf) do
|
|
if fs.isLink(fs.concat(path, f)) then
|
|
setColor(0xffff00)
|
|
elseif f:sub(-4) == ".lua" then
|
|
setColor(0xff5555)
|
|
else
|
|
setColor(0xcccccc)
|
|
end
|
|
if options.a or f:sub(1, 1) ~= "." then
|
|
if not formatOutput() then
|
|
io.write(f)
|
|
if options.l then
|
|
io.write(" " .. fs.size(fs.concat(path, f)))
|
|
end
|
|
io.write("\n")
|
|
else
|
|
io.write(text.padRight(f, m))
|
|
if options.l then
|
|
setColor(0xcccccc)
|
|
io.write(fs.size(fs.concat(path, f)), "\n")
|
|
elseif col % columns == 0 then
|
|
io.write("\n")
|
|
end
|
|
end
|
|
col = col + 1
|
|
end
|
|
end
|
|
|
|
setColor(0xcccccc)
|
|
if options.M then
|
|
io.write("\n" .. tostring(#lsf) .. " File(s)")
|
|
io.write("\n" .. tostring(#lsd) .. " Dir(s)")
|
|
end
|
|
if not options.l then
|
|
io.write("\n")
|
|
end
|
|
end
|
|
end
|
|
io.output():setvbuf("no")
|
|
io.output():flush()
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@lua.lua
|
|
local component = require("component")
|
|
local package = require("package")
|
|
local term = require("term")
|
|
local serialization = require("serialization")
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
local env = setmetatable({}, {__index = _ENV})
|
|
|
|
if #args > 0 then
|
|
local script, reason = loadfile(args[1], nil, env)
|
|
if not script then
|
|
io.stderr:write(tostring(reason) .. "\n")
|
|
os.exit(false)
|
|
end
|
|
local result, reason = pcall(script, table.unpack(args, 2))
|
|
if not result then
|
|
io.stderr:write(reason)
|
|
os.exit(false)
|
|
end
|
|
end
|
|
|
|
if #args == 0 or options.i then
|
|
local function optrequire(...)
|
|
local success, module = pcall(require, ...)
|
|
if success then
|
|
return module
|
|
end
|
|
end
|
|
setmetatable(env, {
|
|
__index = function(t, k)
|
|
_ENV[k] = _ENV[k] or optrequire(k)
|
|
return _ENV[k]
|
|
end,
|
|
__pairs = function(self)
|
|
local t = self
|
|
return function(_, key)
|
|
local k, v = next(t, key)
|
|
if not k and t == env then
|
|
t = _ENV
|
|
k, v = next(t)
|
|
end
|
|
if not k and t == _ENV then
|
|
t = package.loaded
|
|
k, v = next(t)
|
|
end
|
|
return k, v
|
|
end
|
|
end
|
|
})
|
|
|
|
local history = {}
|
|
|
|
local function findTable(t, path)
|
|
if type(t) ~= "table" then return nil end
|
|
if not path or #path == 0 then return t end
|
|
local name = string.match(path, "[^.]+")
|
|
for k, v in pairs(t) do
|
|
if k == name then
|
|
return findTable(v, string.sub(path, #name + 2))
|
|
end
|
|
end
|
|
local mt = getmetatable(t)
|
|
if t == env then mt = {__index=_ENV} end
|
|
if mt then
|
|
return findTable(mt.__index, path)
|
|
end
|
|
return nil
|
|
end
|
|
local function findKeys(t, r, prefix, name)
|
|
if type(t) ~= "table" then return end
|
|
for k, v in pairs(t) do
|
|
if string.match(k, "^"..name) then
|
|
local postfix = ""
|
|
if type(v) == "function" then postfix = "()"
|
|
elseif type(v) == "table" and getmetatable(v) and getmetatable(v).__call then postfix = "()"
|
|
elseif type(v) == "table" then postfix = "."
|
|
end
|
|
r[prefix..k..postfix] = true
|
|
end
|
|
end
|
|
local mt = getmetatable(t)
|
|
if t == env then mt = {__index=_ENV} end
|
|
if mt then
|
|
return findKeys(mt.__index, r, prefix, name)
|
|
end
|
|
end
|
|
local function hint(line, index)
|
|
line = (line or ""):sub(1, index - 1)
|
|
local path = string.match(line, "[a-zA-Z_][a-zA-Z0-9_.]*$")
|
|
if not path then return nil end
|
|
local suffix = string.match(path, "[^.]+$") or ""
|
|
local prefix = string.sub(path, 1, #path - #suffix)
|
|
local t = findTable(env, prefix)
|
|
if not t then return nil end
|
|
local r1, r2 = {}, {}
|
|
findKeys(t, r1, string.sub(line, 1, #line - #suffix), suffix)
|
|
for k in pairs(r1) do
|
|
table.insert(r2, k)
|
|
end
|
|
table.sort(r2)
|
|
return r2
|
|
end
|
|
|
|
component.gpu.setForeground(0xFFFFFF)
|
|
term.write(_VERSION .. " Copyright (C) 1994-2015 Lua.org, PUC-Rio\n")
|
|
component.gpu.setForeground(0xFFFF00)
|
|
term.write("Enter a statement and hit enter to evaluate it.\n")
|
|
term.write("Prefix an expression with '=' to show its value.\n")
|
|
term.write("Press Ctrl+C to exit the interpreter.\n")
|
|
component.gpu.setForeground(0xFFFFFF)
|
|
|
|
while term.isAvailable() do
|
|
local foreground = component.gpu.setForeground(0x00FF00)
|
|
term.write(tostring(env._PROMPT or "lua> "))
|
|
component.gpu.setForeground(foreground)
|
|
local command = term.read(history, nil, hint)
|
|
if command == nil then -- eof
|
|
return
|
|
end
|
|
while #history > 10 do
|
|
table.remove(history, 1)
|
|
end
|
|
local code, reason
|
|
if string.sub(command, 1, 1) == "=" then
|
|
code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env)
|
|
else
|
|
code, reason = load(command, "=stdin", "t", env)
|
|
end
|
|
if code then
|
|
local result = table.pack(xpcall(code, debug.traceback))
|
|
if not result[1] then
|
|
if type(result[2]) == "table" and result[2].reason == "terminated" then
|
|
os.exit(result[2].code)
|
|
end
|
|
io.stderr:write(tostring(result[2]) .. "\n")
|
|
else
|
|
for i = 2, result.n do
|
|
term.write(serialization.serialize(result[i], true) .. "\t", true)
|
|
end
|
|
if term.getCursor() > 1 then
|
|
term.write("\n")
|
|
end
|
|
end
|
|
else
|
|
io.stderr:write(tostring(reason) .. "\n")
|
|
end
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@man.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: man <topic>\n")
|
|
io.write("Where `topic` will usually be the name of a program or library.")
|
|
return
|
|
end
|
|
|
|
local topic = args[1]
|
|
for path in string.gmatch(os.getenv("MANPATH"), "[^:]+") do
|
|
path = shell.resolve(fs.concat(path, topic), "man")
|
|
if path and fs.exists(path) and not fs.isDirectory(path) then
|
|
os.execute(os.getenv("PAGER") .. " " .. path)
|
|
os.exit()
|
|
end
|
|
end
|
|
io.stderr:write("No manual entry for " .. topic)
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@mkdir.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: mkdir <dirname1> [<dirname2> [...]]")
|
|
return
|
|
end
|
|
|
|
for i = 1, #args do
|
|
local path = shell.resolve(args[i])
|
|
local result, reason = fs.makeDirectory(path)
|
|
if not result then
|
|
if not reason then
|
|
if fs.exists(path) then
|
|
reason = "file or folder with that name already exists"
|
|
else
|
|
reason = "unknown reason"
|
|
end
|
|
end
|
|
io.stderr:write(path .. ": " .. reason .. "\n")
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@more.lua
|
|
local component = require("component")
|
|
local event = require("event")
|
|
local keyboard = require("keyboard")
|
|
local shell = require("shell")
|
|
local term = require("term")
|
|
local text = require("text")
|
|
local unicode = require("unicode")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: more <filename1>")
|
|
return
|
|
end
|
|
|
|
local file, reason = io.open(shell.resolve(args[1]))
|
|
if not file then
|
|
io.stderr:write(reason)
|
|
return
|
|
end
|
|
|
|
local line = nil
|
|
local function readlines(num)
|
|
local w, h = component.gpu.getResolution()
|
|
num = num or (h - 1)
|
|
term.setCursorBlink(false)
|
|
for _ = 1, num do
|
|
if not line then
|
|
line = file:read("*l")
|
|
if not line then -- eof
|
|
return nil
|
|
end
|
|
end
|
|
local wrapped
|
|
wrapped, line = text.wrap(text.detab(line), w, w)
|
|
io.write(wrapped .. "\n")
|
|
end
|
|
term.setCursor(1, h)
|
|
term.write(":")
|
|
term.setCursorBlink(true)
|
|
return true
|
|
end
|
|
|
|
while true do
|
|
term.clear()
|
|
if not readlines() then
|
|
return
|
|
end
|
|
while true do
|
|
local event, address, char, code = event.pull("key_down")
|
|
if component.isPrimary(address) then
|
|
if code == keyboard.keys.q then
|
|
term.setCursorBlink(false)
|
|
term.clearLine()
|
|
return
|
|
elseif code == keyboard.keys.space or code == keyboard.keys.pageDown then
|
|
break
|
|
elseif code == keyboard.keys.enter or code == keyboard.keys.down then
|
|
term.clearLine()
|
|
if not readlines(1) then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@mount.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
for proxy, path in fs.mounts() do
|
|
local label = proxy.getLabel() or proxy.address
|
|
local mode = proxy.isReadOnly() and "ro" or "rw"
|
|
io.write(string.format("%s on %s (%s)\n", label, path, mode))
|
|
end
|
|
return
|
|
end
|
|
if #args < 2 then
|
|
io.write("Usage: mount [<label|address> <path>]\n")
|
|
io.write("Note that the address may be abbreviated.")
|
|
return
|
|
end
|
|
|
|
local proxy, reason = fs.proxy(args[1])
|
|
if not proxy then
|
|
io.stderr:write(reason)
|
|
return
|
|
end
|
|
|
|
local result, reason = fs.mount(proxy, shell.resolve(args[2]))
|
|
if not result then
|
|
io.stderr:write(reason)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@mv.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args < 2 then
|
|
io.write("Usage: mv [-f] <from> <to>\n")
|
|
io.write(" -f: overwrite file if it already exists.")
|
|
return
|
|
end
|
|
|
|
local from = shell.resolve(args[1])
|
|
local to = shell.resolve(args[2])
|
|
if fs.isDirectory(to) then
|
|
to = to .. "/" .. fs.name(from)
|
|
end
|
|
if fs.exists(to) then
|
|
if not options.f then
|
|
io.stderr:write("target file exists")
|
|
return
|
|
end
|
|
fs.remove(to)
|
|
end
|
|
local result, reason = os.rename(from, to)
|
|
if not result then
|
|
io.stderr:write(reason or "unknown error")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@pastebin.lua
|
|
--[[ This program allows downloading and uploading from and to pastebin.com.
|
|
Authors: Sangar, Vexatos ]]
|
|
local component = require("component")
|
|
local fs = require("filesystem")
|
|
local internet = require("internet")
|
|
local shell = require("shell")
|
|
|
|
if not component.isAvailable("internet") then
|
|
io.stderr:write("This program requires an internet card to run.")
|
|
return
|
|
end
|
|
|
|
local args, options = shell.parse(...)
|
|
|
|
-- This gets code from the website and stores it in the specified file.
|
|
local function get(pasteId, filename)
|
|
local f, reason = io.open(filename, "w")
|
|
if not f then
|
|
io.stderr:write("Failed opening file for writing: " .. reason)
|
|
return
|
|
end
|
|
|
|
io.write("Downloading from pastebin.com... ")
|
|
local url = "http://pastebin.com/raw.php?i=" .. pasteId
|
|
local result, response = pcall(internet.request, url)
|
|
if result then
|
|
io.write("success.\n")
|
|
for chunk in response do
|
|
if not options.k then
|
|
string.gsub(chunk, "\r\n", "\n")
|
|
end
|
|
f:write(chunk)
|
|
end
|
|
|
|
f:close()
|
|
io.write("Saved data to " .. filename .. "\n")
|
|
else
|
|
io.write("failed.\n")
|
|
f:close()
|
|
fs.remove(filename)
|
|
io.stderr:write("HTTP request failed: " .. response .. "\n")
|
|
end
|
|
end
|
|
|
|
-- This makes a string safe for being used in a URL.
|
|
function encode(code)
|
|
if code then
|
|
code = string.gsub(code, "([^%w ])", function (c)
|
|
return string.format("%%%02X", string.byte(c))
|
|
end)
|
|
code = string.gsub(code, " ", "+")
|
|
end
|
|
return code
|
|
end
|
|
|
|
-- This stores the program in a temporary file, which it will
|
|
-- delete after the program was executed.
|
|
function run(pasteId, ...)
|
|
local tmpFile = os.tmpname()
|
|
get(pasteId, tmpFile)
|
|
io.write("Running...\n")
|
|
|
|
local success, reason = shell.execute(tmpFile, nil, ...)
|
|
if not success then
|
|
io.stderr:write(reason)
|
|
end
|
|
fs.remove(tmpFile)
|
|
end
|
|
|
|
-- Uploads the specified file as a new paste to pastebin.com.
|
|
function put(path)
|
|
local config = {}
|
|
local configFile = loadfile("/etc/pastebin.conf", "t", config)
|
|
if configFile then
|
|
local result, reason = pcall(configFile)
|
|
if not result then
|
|
io.stderr:write("Failed loading config: " .. reason)
|
|
end
|
|
end
|
|
config.key = config.key or "fd92bd40a84c127eeb6804b146793c97"
|
|
local file, reason = io.open(path, "r")
|
|
|
|
if not file then
|
|
io.stderr:write("Failed opening file for reading: " .. reason)
|
|
return
|
|
end
|
|
|
|
local data = file:read("*a")
|
|
file:close()
|
|
|
|
io.write("Uploading to pastebin.com... ")
|
|
local result, response = pcall(internet.request,
|
|
"http://pastebin.com/api/api_post.php",
|
|
"api_option=paste&" ..
|
|
"api_dev_key=" .. config.key .. "&" ..
|
|
"api_paste_format=lua&" ..
|
|
"api_paste_expire_date=N&" ..
|
|
"api_paste_name=" .. encode(fs.name(path)) .. "&" ..
|
|
"api_paste_code=" .. encode(data))
|
|
|
|
if result then
|
|
local info = ""
|
|
for chunk in response do
|
|
info = info .. chunk
|
|
end
|
|
if string.match(info, "^Bad API request, ") then
|
|
io.write("failed.\n")
|
|
io.write(info)
|
|
else
|
|
io.write("success.\n")
|
|
local pasteId = string.match(info, "[^/]+$")
|
|
io.write("Uploaded as " .. info .. "\n")
|
|
io.write('Run "pastebin get ' .. pasteId .. '" to download anywhere.')
|
|
end
|
|
else
|
|
io.write("failed.\n")
|
|
io.stderr:write(response)
|
|
end
|
|
end
|
|
|
|
local command = args[1]
|
|
if command == "put" then
|
|
if #args == 2 then
|
|
put(shell.resolve(args[2]))
|
|
return
|
|
end
|
|
elseif command == "get" then
|
|
if #args == 3 then
|
|
local path = shell.resolve(args[3])
|
|
if fs.exists(path) then
|
|
if not options.f or not os.remove(path) then
|
|
io.stderr:write("file already exists")
|
|
return
|
|
end
|
|
end
|
|
get(args[2], path)
|
|
return
|
|
end
|
|
elseif command == "run" then
|
|
if #args >= 2 then
|
|
run(args[2], table.unpack(args, 3))
|
|
return
|
|
end
|
|
end
|
|
|
|
-- If we come here there was some invalid input.
|
|
io.write("Usages:\n")
|
|
io.write("pastebin put [-f] <file>\n")
|
|
io.write("pastebin get [-f] <id> <file>\n")
|
|
io.write("pastebin run [-f] <id> [<arguments...>]\n")
|
|
io.write(" -f: Force overwriting existing files.\n")
|
|
io.write(" -k: keep line endings as-is (will convert\n")
|
|
io.write(" Windows line endings to Unix otherwise).")
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@primary.lua
|
|
local component = require("component")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: primary <type> [<address>]\n")
|
|
io.write("Note that the address may be abbreviated.")
|
|
return
|
|
end
|
|
|
|
local componentType = args[1]
|
|
|
|
if #args > 1 then
|
|
local address = args[2]
|
|
if not component.get(address) then
|
|
io.stderr:write("no component with this address")
|
|
return
|
|
else
|
|
component.setPrimary(componentType, address)
|
|
os.sleep(0.1) -- allow signals to be processed
|
|
end
|
|
end
|
|
if component.isAvailable(componentType) then
|
|
io.write(component.getPrimary(componentType).address)
|
|
else
|
|
io.stderr:write("no primary component for this type")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@pwd.lua
|
|
local shell = require("shell")
|
|
|
|
io.write(shell.getWorkingDirectory())
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@rc.lua
|
|
local rc = require('rc')
|
|
|
|
local args = table.pack(...)
|
|
if args.n < 1 then
|
|
io.write("Usage: rc <service> [command] [args...]")
|
|
return
|
|
end
|
|
|
|
local result, reason = rc.runCommand(table.unpack(args))
|
|
|
|
if not result then
|
|
io.stderr:write(reason .. "\n")
|
|
end
|
|
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@reboot.lua
|
|
local computer = require("computer")
|
|
|
|
io.write("Rebooting...")
|
|
computer.shutdown(true)
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@redstone.lua
|
|
local colors = require("colors")
|
|
local component = require("component")
|
|
local shell = require("shell")
|
|
local sides = require("sides")
|
|
|
|
if not component.isAvailable("redstone") then
|
|
io.stderr:write("This program requires a redstone card or redstone I/O block.")
|
|
return
|
|
end
|
|
local rs = component.redstone
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args == 0 and not options.w and not options.f then
|
|
io.write("Usage:\n")
|
|
io.write(" redstone <side> [<value>]\n")
|
|
if rs.setBundledOutput then
|
|
io.write(" redstone -b <side> <color> [<value>]\n")
|
|
end
|
|
if rs.setWirelessOutput then
|
|
io.write(" redstone -w [<value>]\n")
|
|
io.write(" redstone -f [<frequency>]\n")
|
|
end
|
|
return
|
|
end
|
|
|
|
if options.w then
|
|
if not rs.setWirelessOutput then
|
|
io.stderr:write("wireless redstone not available")
|
|
return
|
|
end
|
|
if #args > 0 then
|
|
local value = args[1]
|
|
if tonumber(value) then
|
|
value = tonumber(value) > 0
|
|
else
|
|
value = ({["true"]=true,["on"]=true,["yes"]=true})[value] ~= nil
|
|
end
|
|
rs.setWirelessOutput(value)
|
|
end
|
|
io.write("in: " .. tostring(rs.getWirelessInput()) .. "\n")
|
|
io.write("out: " .. tostring(rs.getWirelessOutput()))
|
|
elseif options.f then
|
|
if not rs.setWirelessOutput then
|
|
io.stderr:write("wireless redstone not available")
|
|
return
|
|
end
|
|
if #args > 0 then
|
|
local value = args[1]
|
|
if not tonumber(value) then
|
|
io.stderr:write("invalid frequency")
|
|
return
|
|
end
|
|
rs.setWirelessFrequency(tonumber(value))
|
|
end
|
|
io.write("freq: " .. tostring(rs.getWirelessFrequency()) .. "\n")
|
|
else
|
|
local side = sides[args[1]]
|
|
if not side then
|
|
io.stderr:write("invalid side")
|
|
return
|
|
end
|
|
if type(side) == "string" then
|
|
side = sides[side]
|
|
end
|
|
|
|
if options.b then
|
|
if not rs.setBundledOutput then
|
|
io.stderr:write("bundled redstone not available")
|
|
return
|
|
end
|
|
local color = colors[args[2]]
|
|
if not color then
|
|
io.stderr:write("invalid color")
|
|
return
|
|
end
|
|
if type(color) == "string" then
|
|
color = colors[color]
|
|
end
|
|
if #args > 2 then
|
|
local value = args[3]
|
|
if tonumber(value) then
|
|
value = tonumber(value)
|
|
else
|
|
value = ({["true"]=true,["on"]=true,["yes"]=true})[value] and 255 or 0
|
|
end
|
|
rs.setBundledOutput(side, color, value)
|
|
end
|
|
io.write("in: " .. rs.getBundledInput(side, color) .. "\n")
|
|
io.write("out: " .. rs.getBundledOutput(side, color))
|
|
else
|
|
if #args > 1 then
|
|
local value = args[2]
|
|
if tonumber(value) then
|
|
value = tonumber(value)
|
|
else
|
|
value = ({["true"]=true,["on"]=true,["yes"]=true})[value] and 15 or 0
|
|
end
|
|
rs.setOutput(side, value)
|
|
end
|
|
io.write("in: " .. rs.getInput(side) .. "\n")
|
|
io.write("out: " .. rs.getOutput(side))
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@resolution.lua
|
|
local component = require("component")
|
|
local shell = require("shell")
|
|
local term = require("term")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
local w, h = component.gpu.getResolution()
|
|
io.write(w .. " " .. h)
|
|
return
|
|
end
|
|
|
|
if #args < 2 then
|
|
io.write("Usage: resolution [<width> <height>]")
|
|
return
|
|
end
|
|
|
|
local w = tonumber(args[1])
|
|
local h = tonumber(args[2])
|
|
if not w or not h then
|
|
io.stderr:write("invalid width or height")
|
|
return
|
|
end
|
|
|
|
local result, reason = component.gpu.setResolution(w, h)
|
|
if not result then
|
|
if reason then -- otherwise we didn't change anything
|
|
io.stderr:write(reason)
|
|
end
|
|
return
|
|
end
|
|
term.clear()
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@rm.lua
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: rm [-v] <filename1> [<filename2> [...]]\n")
|
|
io.write(" -v: verbose output.")
|
|
return
|
|
end
|
|
|
|
for i = 1, #args do
|
|
local path = shell.resolve(args[i])
|
|
if not os.remove(path) then
|
|
io.stderr:write(path .. ": no such file, or permission denied\n")
|
|
end
|
|
if options.v then
|
|
io.write("removed '" .. path .. "'\n")
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@scale.lua
|
|
local ecs = require("ECSAPI")
|
|
local gpu = require("component").gpu
|
|
|
|
local arg = {...}
|
|
if arg[1] == "get" or arg[1] == "show" or arg[1] == "print" or arg[1] == "write" or arg[1] == "info" or arg[1] == "help" then
|
|
local max1, max2 = gpu.maxResolution()
|
|
local cur1, cur2 = gpu.getResolution()
|
|
local scale = cur1/max1*100
|
|
print(" ")
|
|
print("Максимальное поддерживаемое разрешение: " .. max1 .. "x".. max2)
|
|
print("Текущее разрешение: " .. cur1.."x"..cur2)
|
|
print(" ")
|
|
print("Масштаб: "..scale.."%")
|
|
print(" ")
|
|
else
|
|
ecs.setScale(tonumber(arg[1]) or 1, true)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@set.lua
|
|
local args = {...}
|
|
|
|
if #args < 1 then
|
|
for k,v in pairs(os.getenv()) do
|
|
io.write(k .. "='" .. string.gsub(v, "'", [['"'"']]) .. "'\n")
|
|
end
|
|
else
|
|
local count = 0
|
|
for _, expr in ipairs(args) do
|
|
local k, v = string.match(expr, "(.-)=(.*)")
|
|
if v then
|
|
os.setenv(k, v)
|
|
else
|
|
if count == 0 then
|
|
for i = 1, os.getenv('#') do
|
|
os.setenv(i, nil)
|
|
end
|
|
end
|
|
count = count + 1
|
|
os.setenv(count, expr)
|
|
end
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@sh.lua
|
|
local component = require("component")
|
|
local computer = require("computer")
|
|
local event = require("event")
|
|
local fs = require("filesystem")
|
|
local process = require("process")
|
|
local shell = require("shell")
|
|
local term = require("term")
|
|
local text = require("text")
|
|
local unicode = require("unicode")
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local memoryStream = {}
|
|
|
|
function memoryStream:close()
|
|
self.closed = true
|
|
end
|
|
|
|
function memoryStream:seek()
|
|
return nil, "bad file descriptor"
|
|
end
|
|
|
|
function memoryStream:read(n)
|
|
if self.closed then
|
|
if self.buffer == "" and self.redirect.read then
|
|
return self.redirect.read:read(n)
|
|
else
|
|
return nil -- eof
|
|
end
|
|
end
|
|
if self.buffer == "" then
|
|
self.args = table.pack(coroutine.yield(table.unpack(self.result)))
|
|
end
|
|
local result = string.sub(self.buffer, 1, n)
|
|
self.buffer = string.sub(self.buffer, n + 1)
|
|
return result
|
|
end
|
|
|
|
function memoryStream:write(value)
|
|
if not self.redirect.write and self.closed then
|
|
-- if next is dead, ignore all writes
|
|
if coroutine.status(self.next) ~= "dead" then
|
|
error("attempt to use a closed stream")
|
|
end
|
|
return true
|
|
end
|
|
if self.redirect.write then
|
|
self.redirect.write:write(value)
|
|
end
|
|
if not self.closed then
|
|
self.buffer = self.buffer .. value
|
|
self.result = table.pack(coroutine.resume(self.next, table.unpack(self.args)))
|
|
if coroutine.status(self.next) == "dead" then
|
|
self:close()
|
|
end
|
|
if not self.result[1] then
|
|
error(self.result[2], 0)
|
|
end
|
|
table.remove(self.result, 1)
|
|
end
|
|
return true
|
|
end
|
|
|
|
function memoryStream.new()
|
|
local stream = {closed = false, buffer = "",
|
|
redirect = {}, result = {}, args = {}}
|
|
local metatable = {__index = memoryStream,
|
|
__gc = memoryStream.close,
|
|
__metatable = "memorystream"}
|
|
return setmetatable(stream, metatable)
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local function expand(value)
|
|
local result = value:gsub("%$(%w+)", os.getenv):gsub("%$%b{}",
|
|
function(match) return os.getenv(expand(match:sub(3, -2))) or match end)
|
|
return result
|
|
end
|
|
|
|
local function glob(value)
|
|
if not value:find("*", 1, true) and not value:find("?", 1, true) then
|
|
-- Nothing to do here.
|
|
return {expand(value)}
|
|
end
|
|
local segments = fs.segments(value)
|
|
local paths = {value:sub(1, 1) == "/" and "/" or shell.getWorkingDirectory()}
|
|
for i, segment in ipairs(segments) do
|
|
local nextPaths = {}
|
|
local pattern = segment:gsub("*", ".*"):gsub("?", ".")
|
|
if pattern == segment then
|
|
-- Nothing to do, concatenate as-is.
|
|
for _, path in ipairs(paths) do
|
|
table.insert(nextPaths, fs.concat(path, segment))
|
|
end
|
|
else
|
|
pattern = "^(" .. pattern .. ")/?$"
|
|
for _, path in ipairs(paths) do
|
|
for file in fs.list(path) do
|
|
if file:match(pattern) then
|
|
table.insert(nextPaths, fs.concat(path, file))
|
|
end
|
|
end
|
|
end
|
|
if #nextPaths == 0 then
|
|
error("no matches found: " .. segment)
|
|
end
|
|
end
|
|
paths = nextPaths
|
|
end
|
|
for i, path in ipairs(paths) do
|
|
paths[i] = expand(path)
|
|
end
|
|
return paths
|
|
end
|
|
|
|
local function evaluate(value)
|
|
local init, results = 1, {""}
|
|
repeat
|
|
local match = value:match("^%b''", init)
|
|
if match then -- single quoted string. no variable expansion.
|
|
match = match:sub(2, -2)
|
|
init = init + 2
|
|
for i, result in ipairs(results) do
|
|
results[i] = result .. match
|
|
end
|
|
else
|
|
match = value:match('^%b""', init)
|
|
if match then -- double quoted string.
|
|
match = match:sub(2, -2)
|
|
init = init + 2
|
|
else
|
|
-- plaintext?
|
|
match = value:match("^([^']+)%b''", init)
|
|
if not match then -- unmatched single quote.
|
|
match = value:match('^([^"]+)%b""', init)
|
|
if not match then -- unmatched double quote.
|
|
match = value:sub(init)
|
|
end
|
|
end
|
|
end
|
|
local newResults = {}
|
|
for _, globbed in ipairs(glob(match)) do
|
|
for i, result in ipairs(results) do
|
|
table.insert(newResults, result .. globbed)
|
|
end
|
|
end
|
|
results = newResults
|
|
end
|
|
init = init + #match
|
|
until init > #value
|
|
return results
|
|
end
|
|
|
|
local function parseCommand(tokens, ...)
|
|
if #tokens == 0 then
|
|
return
|
|
end
|
|
|
|
local program, args = shell.resolveAlias(tokens[1], table.pack(select(2, table.unpack(tokens))))
|
|
|
|
local eargs = {}
|
|
program = evaluate(program)
|
|
for i = 2, #program do
|
|
table.insert(eargs, program[i])
|
|
end
|
|
local program, reason = shell.resolve(program[1], "lua")
|
|
if not program then
|
|
return nil, reason
|
|
end
|
|
for i = 1, #args do
|
|
for _, arg in ipairs(evaluate(args[i])) do
|
|
table.insert(eargs, arg)
|
|
end
|
|
end
|
|
args = eargs
|
|
|
|
-- Find redirects.
|
|
local input, output, mode = nil, nil, "write"
|
|
tokens = args
|
|
args = {}
|
|
local function smt(call) -- state metatable factory
|
|
local function index(_, token)
|
|
if token == "<" or token == ">" or token == ">>" then
|
|
return "parse error near " .. token
|
|
end
|
|
call(token)
|
|
return "args" -- default, return to normal arg parsing
|
|
end
|
|
return {__index=index}
|
|
end
|
|
local sm = { -- state machine for redirect parsing
|
|
args = setmetatable({["<"]="input", [">"]="output", [">>"]="append"},
|
|
smt(function(token)
|
|
table.insert(args, token)
|
|
end)),
|
|
input = setmetatable({}, smt(function(token)
|
|
input = token
|
|
end)),
|
|
output = setmetatable({}, smt(function(token)
|
|
output = token
|
|
mode = "write"
|
|
end)),
|
|
append = setmetatable({}, smt(function(token)
|
|
output = token
|
|
mode = "append"
|
|
end))
|
|
}
|
|
-- Run state machine over tokens.
|
|
local state = "args"
|
|
for i = 1, #tokens do
|
|
local token = tokens[i]
|
|
state = sm[state][token]
|
|
if not sm[state] then
|
|
return nil, state
|
|
end
|
|
end
|
|
return program, args, input, output, mode
|
|
end
|
|
|
|
local function parseCommands(command)
|
|
local tokens, reason = text.tokenize(command)
|
|
if not tokens then
|
|
return nil, reason
|
|
elseif #tokens == 0 then
|
|
return true
|
|
end
|
|
|
|
local commands, command = {}, {}
|
|
for i = 1, #tokens do
|
|
if tokens[i] == "|" then
|
|
if #command == 0 then
|
|
return nil, "parse error near '|'"
|
|
end
|
|
table.insert(commands, command)
|
|
command = {}
|
|
else
|
|
table.insert(command, tokens[i])
|
|
end
|
|
end
|
|
if #command > 0 then -- push tail command
|
|
table.insert(commands, command)
|
|
end
|
|
|
|
for i = 1, #commands do
|
|
commands[i] = table.pack(parseCommand(commands[i]))
|
|
if commands[i][1] == nil then
|
|
return nil, commands[i][2]
|
|
end
|
|
end
|
|
|
|
return commands
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
local function execute(env, command, ...)
|
|
checkArg(1, command, "string")
|
|
local commands, reason = parseCommands(command)
|
|
if not commands then
|
|
return false, reason
|
|
end
|
|
if #commands == 0 then
|
|
return true
|
|
end
|
|
|
|
-- Piping data between programs works like so:
|
|
-- program1 gets its output replaced with our custom stream.
|
|
-- program2 gets its input replaced with our custom stream.
|
|
-- repeat for all programs
|
|
-- custom stream triggers execution of 'next' program after write.
|
|
-- custom stream triggers yield before read if buffer is empty.
|
|
-- custom stream may have 'redirect' entries for fallback/duplication.
|
|
local threads, pipes, inputs, outputs = {}, {}, {}, {}
|
|
for i = 1, #commands do
|
|
local program, args, input, output, mode = table.unpack(commands[i])
|
|
local reason
|
|
threads[i], reason = process.load(program, env, function()
|
|
os.setenv("_", program)
|
|
if input then
|
|
local file, reason = io.open(shell.resolve(input))
|
|
if not file then
|
|
error("could not open '" .. input .. "': " .. reason, 0)
|
|
end
|
|
table.insert(inputs, file)
|
|
if pipes[i - 1] then
|
|
pipes[i - 1].stream.redirect.read = file
|
|
io.input(pipes[i - 1])
|
|
else
|
|
io.input(file)
|
|
end
|
|
elseif pipes[i - 1] then
|
|
io.input(pipes[i - 1])
|
|
end
|
|
if output then
|
|
local file, reason = io.open(shell.resolve(output), mode == "append" and "a" or "w")
|
|
if not file then
|
|
error("could not open '" .. output .. "': " .. reason, 0)
|
|
end
|
|
if mode == "append" then
|
|
io.write("\n")
|
|
end
|
|
table.insert(outputs, file)
|
|
if pipes[i] then
|
|
pipes[i].stream.redirect.write = file
|
|
io.output(pipes[i])
|
|
else
|
|
io.output(file)
|
|
end
|
|
elseif pipes[i] then
|
|
io.output(pipes[i])
|
|
end
|
|
io.write('')
|
|
end, command)
|
|
if not threads[i] then
|
|
return false, reason
|
|
end
|
|
|
|
if i < #commands then
|
|
pipes[i] = require("buffer").new("rw", memoryStream.new())
|
|
pipes[i]:setvbuf("no")
|
|
end
|
|
if i > 1 then
|
|
pipes[i - 1].stream.next = threads[i]
|
|
pipes[i - 1].stream.args = args
|
|
end
|
|
end
|
|
|
|
local args = select(2, table.unpack(commands[1]))
|
|
for _, arg in ipairs(table.pack(...)) do
|
|
table.insert(args, arg)
|
|
end
|
|
table.insert(args, 1, true)
|
|
args.n = #args
|
|
local result = nil
|
|
for i = 1, #threads do
|
|
-- Emulate CC behavior by making yields a filtered event.pull()
|
|
while args[1] and coroutine.status(threads[i]) ~= "dead" do
|
|
result = table.pack(coroutine.resume(threads[i], table.unpack(args, 2, args.n)))
|
|
if coroutine.status(threads[i]) ~= "dead" then
|
|
args = table.pack(pcall(event.pull, table.unpack(result, 2, result.n)))
|
|
end
|
|
end
|
|
if pipes[i] then
|
|
pcall(pipes[i].close, pipes[i])
|
|
end
|
|
if not result[1] then
|
|
if type(result[2]) == "table" and result[2].reason == "terminated" then
|
|
if result[2].code then
|
|
result[1] = true
|
|
result.n = 1
|
|
else
|
|
result[2] = "terminated"
|
|
end
|
|
elseif type(result[2]) == "string" then
|
|
result[2] = debug.traceback(threads[i], result[2])
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
-- copy env vars from last process; mostly to ensure stuff like cd.lua works
|
|
local lastVars = rawget(process.info(threads[#threads]).data, "vars")
|
|
if lastVars then
|
|
local localVars = process.info().data.vars
|
|
for k,v in pairs(lastVars) do
|
|
localVars[k] = v
|
|
end
|
|
end
|
|
|
|
for _, input in ipairs(inputs) do
|
|
input:close()
|
|
end
|
|
for _, output in ipairs(outputs) do
|
|
output:close()
|
|
end
|
|
|
|
if not args[1] then
|
|
return false, args[2]
|
|
end
|
|
return table.unpack(result, 1, result.n)
|
|
end
|
|
|
|
local args, options = shell.parse(...)
|
|
local history = {}
|
|
|
|
local function escapeMagic(text)
|
|
return text:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1')
|
|
end
|
|
|
|
local function getMatchingPrograms(baseName)
|
|
local result = {}
|
|
-- TODO only matching files with .lua extension for now, might want to
|
|
-- extend this to other extensions at some point? env var? file attrs?
|
|
if not baseName or #baseName == 0 then
|
|
baseName = "^(.*)%.lua$"
|
|
else
|
|
baseName = "^(" .. escapeMagic(baseName) .. ".*)%.lua$"
|
|
end
|
|
for basePath in string.gmatch(os.getenv("PATH"), "[^:]+") do
|
|
for file in fs.list(basePath) do
|
|
local match = file:match(baseName)
|
|
if match then
|
|
table.insert(result, match)
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function getMatchingFiles(partialPrefix, name)
|
|
local baseName = shell.resolve(partialPrefix .. name)
|
|
local result, basePath = {}
|
|
-- note: we strip the trailing / to make it easier to navigate through
|
|
-- directories using tab completion (since entering the / will then serve
|
|
-- as the intention to go into the currently hinted one).
|
|
-- if we have a directory but no trailing slash there may be alternatives
|
|
-- on the same level, so don't look inside that directory... (cont.)
|
|
if fs.isDirectory(baseName) and baseName:sub(-1) == "/" then
|
|
basePath = baseName
|
|
baseName = "^(.-)/?$"
|
|
else
|
|
basePath = fs.path(baseName) or "/"
|
|
baseName = "^(" .. escapeMagic(fs.name(baseName)) .. ".-)/?$"
|
|
end
|
|
for file in fs.list(basePath) do
|
|
local match = file:match(baseName)
|
|
if match then
|
|
table.insert(result, partialPrefix .. match)
|
|
end
|
|
end
|
|
-- (cont.) but if there's only one match and it's a directory, *then* we
|
|
-- do want to add the trailing slash here.
|
|
if #result == 1 and fs.isDirectory(result[1]) then
|
|
result[1] = result[1] .. "/"
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function hintHandler(line, cursor)
|
|
local line = unicode.sub(line, 1, cursor - 1)
|
|
if not line or #line < 1 then
|
|
return nil
|
|
end
|
|
local result
|
|
local prefix, partial = string.match(line, "^(.+%s)(.+)$")
|
|
local searchInPath = not prefix and not line:find("/")
|
|
if searchInPath then
|
|
-- first part and no path, look for programs in the $PATH
|
|
result = getMatchingPrograms(line)
|
|
else -- just look normal files
|
|
local partialPrefix = (partial or line)
|
|
local name = fs.name(partialPrefix)
|
|
partialPrefix = partialPrefix:sub(1, -name:len() - 1)
|
|
result = getMatchingFiles(partialPrefix, name)
|
|
end
|
|
local resultSuffix = ""
|
|
if searchInPath then
|
|
resultSuffix = " "
|
|
elseif #result == 1 and result[1]:sub(-1) ~= '/' then
|
|
resultSuffix = " "
|
|
end
|
|
prefix = prefix or ""
|
|
for i = 1, #result do
|
|
result[i] = prefix .. result[i] .. resultSuffix
|
|
end
|
|
table.sort(result)
|
|
return result
|
|
end
|
|
|
|
if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then
|
|
-- interactive shell.
|
|
while true do
|
|
if not term.isAvailable() then -- don't clear unless we lost the term
|
|
while not term.isAvailable() do
|
|
event.pull("term_available")
|
|
end
|
|
term.clear()
|
|
end
|
|
while term.isAvailable() do
|
|
local foreground = component.gpu.setForeground(0xFF0000)
|
|
term.write(expand(os.getenv("PS1") or "$ "))
|
|
component.gpu.setForeground(foreground)
|
|
local command = term.read(history, nil, hintHandler)
|
|
if not command then
|
|
term.write("exit\n")
|
|
return -- eof
|
|
end
|
|
while #history > (tonumber(os.getenv("HISTSIZE")) or 10) do
|
|
table.remove(history, 1)
|
|
end
|
|
command = text.trim(command)
|
|
if command == "exit" then
|
|
return
|
|
elseif command ~= "" then
|
|
local result, reason = os.execute(command)
|
|
if term.getCursor() > 1 then
|
|
term.write("\n")
|
|
end
|
|
if not result then
|
|
io.stderr:write((reason and tostring(reason) or "unknown error") .. "\n")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif #args == 0 and (io.input() ~= io.stdin) then
|
|
while true do
|
|
io.write(expand(os.getenv("PS1") or "$ "))
|
|
local command = io.read("*l")
|
|
if not command then
|
|
io.write("exit\n")
|
|
end
|
|
command = text.trim(command)
|
|
if command == "exit" then
|
|
return
|
|
elseif command ~= "" then
|
|
local result, reason = os.execute(command)
|
|
if not result then
|
|
io.stderr:write((reason and tostring(reason) or "unknown error") .. "\n")
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- execute command.
|
|
local result = table.pack(execute(...))
|
|
if not result[1] then
|
|
error(result[2], 0)
|
|
end
|
|
return table.unpack(result, 2)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@shutdown.lua
|
|
local computer = require("computer")
|
|
local term = require("term")
|
|
|
|
term.clear()
|
|
computer.shutdown()
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@umount.lua
|
|
local fs = require("filesystem")
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
|
|
if #args < 1 then
|
|
io.write("Usage: umount [-a] <mount>\n")
|
|
io.write(" -a Remove any mounts by file system label or address instead of by path. Note that the address may be abbreviated.")
|
|
return
|
|
end
|
|
|
|
local proxy, reason
|
|
if options.a then
|
|
proxy, reason = fs.proxy(args[1])
|
|
if proxy then
|
|
proxy = proxy.address
|
|
end
|
|
else
|
|
local path = shell.resolve(args[1])
|
|
proxy, reason = fs.get(path)
|
|
if proxy then
|
|
proxy = reason -- = path
|
|
if proxy ~= path then
|
|
io.stderr:write("not a mount point")
|
|
return
|
|
end
|
|
end
|
|
end
|
|
if not proxy then
|
|
io.stderr:write(reason)
|
|
return
|
|
end
|
|
|
|
if not fs.umount(proxy) then
|
|
io.stderr:write("nothing to unmount here")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@unalias.lua
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args < 1 then
|
|
io.write("Usage: unalias <name>")
|
|
return
|
|
end
|
|
|
|
local result = shell.getAlias(args[1])
|
|
if not result then
|
|
io.stderr:write("no such alias")
|
|
else
|
|
shell.setAlias(args[1], nil)
|
|
io.write("alias removed: " .. args[1] .. " -> " .. result)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@unset.lua
|
|
local args = {...}
|
|
|
|
if #args < 1 then
|
|
io.write("Usage: unset <varname>[ <varname2> [...]]\n")
|
|
else
|
|
for _, k in ipairs(args) do
|
|
os.setenv(k, nil)
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@uptime.lua
|
|
local computer = require("computer")
|
|
|
|
local seconds = math.floor(computer.uptime())
|
|
local minutes, hours = 0, 0
|
|
if seconds >= 60 then
|
|
minutes = math.floor(seconds / 60)
|
|
seconds = seconds % 60
|
|
end
|
|
if minutes >= 60 then
|
|
hours = math.floor(minutes / 60)
|
|
minutes = minutes % 60
|
|
end
|
|
io.write(string.format("%02d:%02d:%02d", hours, minutes, seconds))
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@useradd.lua
|
|
local computer = require("computer")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args < 1 then
|
|
io.write("Usage: useradd <name>")
|
|
return
|
|
end
|
|
|
|
local result, reason = computer.addUser(args[1])
|
|
if not result then
|
|
io.stderr:write(reason)
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@userdel.lua
|
|
local computer = require("computer")
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args < 1 then
|
|
io.write("Usage: userdel <name>")
|
|
return
|
|
end
|
|
|
|
if not computer.removeUser(args[1]) then
|
|
io.stderr:write("no such user")
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@wget.lua
|
|
local component = require("component")
|
|
local fs = require("filesystem")
|
|
local internet = require("internet")
|
|
local shell = require("shell")
|
|
local text = require("text")
|
|
|
|
if not component.isAvailable("internet") then
|
|
io.stderr:write("This program requires an internet card to run.")
|
|
return
|
|
end
|
|
|
|
local args, options = shell.parse(...)
|
|
options.q = options.q or options.Q
|
|
|
|
if #args < 1 then
|
|
io.write("Usage: wget [-fq] <url> [<filename>]\n")
|
|
io.write(" -f: Force overwriting existing files.\n")
|
|
io.write(" -q: Quiet mode - no status messages.\n")
|
|
io.write(" -Q: Superquiet mode - no error messages.")
|
|
return
|
|
end
|
|
|
|
local url = text.trim(args[1])
|
|
local filename = args[2]
|
|
if not filename then
|
|
filename = url
|
|
local index = string.find(filename, "/[^/]*$")
|
|
if index then
|
|
filename = string.sub(filename, index + 1)
|
|
end
|
|
index = string.find(filename, "?", 1, true)
|
|
if index then
|
|
filename = string.sub(filename, 1, index - 1)
|
|
end
|
|
end
|
|
filename = text.trim(filename)
|
|
if filename == "" then
|
|
if not options.Q then
|
|
io.stderr:write("could not infer filename, please specify one")
|
|
end
|
|
return nil, "missing target filename" -- for programs using wget as a function
|
|
end
|
|
filename = shell.resolve(filename)
|
|
|
|
if fs.exists(filename) then
|
|
if not options.f or not os.remove(filename) then
|
|
if not options.Q then
|
|
io.stderr:write("file already exists")
|
|
end
|
|
return nil, "file already exists" -- for programs using wget as a function
|
|
end
|
|
end
|
|
|
|
local f, reason = io.open(filename, "wb")
|
|
if not f then
|
|
if not options.Q then
|
|
io.stderr:write("failed opening file for writing: " .. reason)
|
|
end
|
|
return nil, "failed opening file for writing: " .. reason -- for programs using wget as a function
|
|
end
|
|
|
|
if not options.q then
|
|
io.write("Downloading... ")
|
|
end
|
|
local result, response = pcall(internet.request, url)
|
|
if result then
|
|
local result, reason = pcall(function()
|
|
for chunk in response do
|
|
f:write(chunk)
|
|
end
|
|
end)
|
|
if not result then
|
|
if not options.q then
|
|
io.stderr:write("failed.\n")
|
|
end
|
|
f:close()
|
|
fs.remove(filename)
|
|
if not options.Q then
|
|
io.stderr:write("HTTP request failed: " .. reason .. "\n")
|
|
end
|
|
return nil, reason -- for programs using wget as a function
|
|
end
|
|
if not options.q then
|
|
io.write("success.\n")
|
|
end
|
|
|
|
f:close()
|
|
if not options.q then
|
|
io.write("Saved data to " .. filename .. "\n")
|
|
end
|
|
else
|
|
if not options.q then
|
|
io.write("failed.\n")
|
|
end
|
|
f:close()
|
|
fs.remove(filename)
|
|
if not options.Q then
|
|
io.stderr:write("HTTP request failed: " .. response .. "\n")
|
|
end
|
|
return nil, response -- for programs using wget as a function
|
|
end
|
|
return true -- for programs using wget as a function
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@which.lua
|
|
local shell = require("shell")
|
|
|
|
local args = shell.parse(...)
|
|
if #args == 0 then
|
|
io.write("Usage: which <program>")
|
|
return
|
|
end
|
|
|
|
for i = 1, #args do
|
|
local result, reason = shell.getAlias(args[i])
|
|
if result then
|
|
result = args[i] .. ": aliased to " .. result
|
|
else
|
|
result, reason = shell.resolve(args[i], "lua")
|
|
end
|
|
if result then
|
|
io.write(result .. "\n")
|
|
else
|
|
io.stderr:write(args[i] .. ": " .. reason .. "\n")
|
|
end
|
|
end
|
|
--@luaPackageFileEnd
|
|
--@luaPackageFileSeparator
|
|
--@yes.lua
|
|
--[[Lua implementation of the UN*X yes command--]]
|
|
local shell = require("shell")
|
|
|
|
local args, options = shell.parse(...)
|
|
|
|
if options.V or options.version then
|
|
io.write("yes v:1.0-2\n")
|
|
io.write("Inspired by functionality of yes from GNU coreutils\n")
|
|
return 0
|
|
end
|
|
|
|
if options.h or options.help then
|
|
io.write("Usage: yes [string]...\n")
|
|
io.write("OR: yes [-V/h]\n")
|
|
io.write("\n")
|
|
io.write("yes prints the command line arguments, or 'y', until is killed.\n")
|
|
io.write("\n")
|
|
io.write("Options:\n")
|
|
io.write(" -V, --version Version\n")
|
|
io.write(" -h, --help This help\n")
|
|
return 0
|
|
end
|
|
|
|
-- If there are no arguments, print 'y' and new line, if there is print it.
|
|
if #args == 0 then
|
|
while pcall(io.write, "y\n") do
|
|
os.sleep(0)
|
|
end
|
|
else
|
|
repeat
|
|
local ok = true
|
|
for i=1, #args, 1 do
|
|
ok = ok and pcall(io.write, args[i], " ")
|
|
end
|
|
pcall(io.write, "\n")
|
|
os.sleep(0)
|
|
until not ok
|
|
end
|
|
return 0
|
|
--@luaPackageFileEnd
|