testing version of LuaBIOS and OpenOS

people were having issues getting them to work so now we promote consistency
This commit is contained in:
2025-06-28 20:41:49 +02:00
parent 8210e20939
commit 687cfebd00
182 changed files with 14016 additions and 1 deletions

View File

@@ -0,0 +1,2 @@
local computer = require("computer")
io.write(computer.address(),"\n")

62
data/OpenOS/bin/alias.lua Normal file
View File

@@ -0,0 +1,62 @@
local shell = require("shell")
local args, options = shell.parse(...)
local ec, error_prefix = 0, "alias:"
if options.help then
print(string.format("Usage: alias: [name[=value] ... ]"))
return
end
local function validAliasName(k)
return k:match("[/%$`=|&;%(%)<> \t]") == nil
end
local function setAlias(k, v)
if not validAliasName(k) then
io.stderr:write(string.format("%s `%s': invalid alias name\n", error_prefix, k))
else
shell.setAlias(k, v)
end
end
local function printAlias(k)
local v = shell.getAlias(k)
if not v then
io.stderr:write(string.format("%s %s: not found\n", error_prefix, k))
ec = 1
else
io.write(string.format("alias %s='%s'\n", k, v))
end
end
local function splitPair(arg)
local matchBegin, matchEnd = arg:find("=")
if matchBegin == nil or matchBegin == 1 then
return arg
else
return arg:sub(1, matchBegin - 1), arg:sub(matchEnd + 1)
end
end
local function handlePair(k, v)
if v then
return setAlias(k, v)
else
return printAlias(k)
end
end
if not next(args) then -- no args
-- print all aliases
for k,v in shell.aliases() do
print(string.format("alias %s='%s'", k, v))
end
else
for _,v in ipairs(args) do
checkArg(1,v,"string")
handlePair(splitPair(v))
end
end
return ec

39
data/OpenOS/bin/cat.lua Normal file
View File

@@ -0,0 +1,39 @@
local shell = require("shell")
local fs = require("filesystem")
local args = shell.parse(...)
if #args == 0 then
args = {"-"}
end
local input_method, input_param = "read", require("tty").getViewport()
for i = 1, #args do
local arg = shell.resolve(args[i])
if fs.isDirectory(arg) then
io.stderr:write(string.format('cat %s: Is a directory\n', arg))
os.exit(1)
else
local file, reason
if args[i] == "-" then
file, reason = io.stdin, "missing stdin"
input_method, input_param = "readLine", false
else
file, reason = fs.open(arg)
end
if not file then
io.stderr:write(string.format("cat: %s: %s\n", args[i], tostring(reason)))
os.exit(1)
else
repeat
local chunk = file[input_method](file, input_param)
if chunk then
io.write(chunk)
end
until not chunk
file:close()
end
end
end
io.stdout:close()

51
data/OpenOS/bin/cd.lua Normal file
View File

@@ -0,0 +1,51 @@
local shell = require("shell")
local fs = require("filesystem")
local args, ops = shell.parse(...)
local path = nil
local verbose = false
if ops.help then
print(
[[Usage cd [dir]
For more options, run: man cd]])
return
end
if #args == 0 then
local home = os.getenv("HOME")
if not home then
io.stderr:write("cd: HOME not set\n")
return 1
end
path = home
elseif args[1] == '-' then
verbose = true
local oldpwd = os.getenv("OLDPWD");
if not oldpwd then
io.stderr:write("cd: OLDPWD not set\n")
return 1
end
path = oldpwd
else
path = args[1]
end
local resolved = shell.resolve(path)
if not fs.exists(resolved) then
io.stderr:write("cd: ",path,": No such file or directory\n")
return 1
end
path = resolved
local oldpwd = shell.getWorkingDirectory()
local result, reason = shell.setWorkingDirectory(path)
if not result then
io.stderr:write("cd: ", path, ": ", reason)
return 1
else
os.setenv("OLDPWD", oldpwd)
end
if verbose then
os.execute("pwd")
end

View File

@@ -0,0 +1,2 @@
local tty = require("tty")
tty.clear()

View File

@@ -0,0 +1,52 @@
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 = component.doc(address, name) or tostring(proxy[name])
io.write(" " .. text.padRight(name, padTo) .. doc .. '\n')
end
end
count = count - 1
if count <= 0 then
break
end
end

36
data/OpenOS/bin/cp.lua Normal file
View File

@@ -0,0 +1,36 @@
local shell = require("shell")
local transfer = require("tools/transfer")
local args, options = shell.parse(...)
options.h = options.h or options.help
if #args < 2 or options.h then
io.write([[Usage: cp [OPTIONS] <from...> <to>
-i: prompt before overwrite (overrides -n option).
-n: do not overwrite an existing file.
-r: copy directories recursively.
-u: copy only when the SOURCE file differs from the destination
file or when the destination file is missing.
-P: preserve attributes, e.g. symbolic links.
-v: verbose output.
-x: stay on original source file system.
--skip=P: skip files matching lua regex P
]])
return not not options.h
end
-- clean options for copy (as opposed to move)
options =
{
cmd = "cp",
i = options.i,
f = options.f,
n = options.n,
r = options.r,
u = options.u,
P = options.P,
v = options.v,
x = options.x,
skip = {options.skip},
}
return transfer.batch(args, options)

1
data/OpenOS/bin/date.lua Normal file
View File

@@ -0,0 +1 @@
io.write(os.date("%F %T").."\n")

76
data/OpenOS/bin/df.lua Normal file
View File

@@ -0,0 +1,76 @@
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)
elseif type(size) == "string" then
return 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
if not mounts[proxy] or mounts[proxy]:len() > path:len() then
mounts[proxy] = path
end
end
else
for i = 1, #args do
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
mounts[proxy] = path
end
end
end
local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}}
for proxy, path 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
local padding = col == #row and 0 or 2
io.write(text.padRight(value, m[col] + padding))
end
print()
end

38
data/OpenOS/bin/dmesg.lua Normal file
View File

@@ -0,0 +1,38 @@
local event = require("event")
local tty = require("tty")
local args = {...}
local gpu = tty.gpu()
local interactive = io.output().tty
local color, isPal, evt
if interactive then
color, isPal = 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 gpu.setForeground(0xCC2200) end
io.write("[" .. os.date("%T") .. "] ")
if interactive then gpu.setForeground(0x44CC00) end
io.write(tostring(evt[1]) .. string.rep(" ", math.max(10 - #tostring(evt[1]), 0) + 1))
if interactive then gpu.setForeground(0xB0B00F) end
io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2])))
if interactive then 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
gpu.setForeground(color, isPal)
end

128
data/OpenOS/bin/du.lua Normal file
View File

@@ -0,0 +1,128 @@
local shell = require("shell")
local fs = require("filesystem")
local args, options, reason = shell.parse(...)
if #args == 0 then
args[1] = '.'
end
local TRY=[[
Try 'du --help' for more information.]]
local VERSION=[[
du (OpenOS bin) 1.0
Written by payonel, patterned after GNU coreutils du]]
local HELP=[[
Usage: du [OPTION]... [FILE]...
Summarize disk usage of each FILE, recursively for directories.
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)
-s, --summarize display only a total for each argument
--help display this help and exit
--version output version information and exit]]
if options.help then
print(HELP)
return true
end
if options.version then
print(VERSION)
return true
end
local function addTrailingSlash(path)
if path:sub(-1) ~= '/' then
return path .. '/'
else
return path
end
end
local function opCheck(shortName, longName)
local enabled = options[shortName] or options[longName]
options[shortName] = nil
options[longName] = nil
return enabled
end
local bHuman = opCheck('h', 'human-readable')
local bSummary = opCheck('s', 'summarize')
if next(options) then
for op,v in pairs(options) do
io.stderr:write(string.format("du: invalid option -- '%s'\n", op))
end
io.stderr:write(TRY..'\n')
return 1
end
local function formatSize(size)
if not bHuman 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 function printSize(size, rpath)
local displaySize = formatSize(size)
io.write(string.format("%s%s\n", string.format("%-12s", displaySize), rpath))
end
local function visitor(rpath)
local subtotal = 0
local dirs = 0
local spath = shell.resolve(rpath)
if fs.isDirectory(spath) then
local list_result = fs.list(spath)
for list_item in list_result do
local vtotal, vdirs = visitor(addTrailingSlash(rpath) .. list_item)
subtotal = subtotal + vtotal
dirs = dirs + vdirs
end
if dirs == 0 then -- no child dirs
if not bSummary then
printSize(subtotal, rpath)
end
end
elseif not fs.isLink(spath) then
subtotal = fs.size(spath)
end
return subtotal, dirs
end
for i,arg in ipairs(args) do
local path = shell.resolve(arg)
if not fs.exists(path) then
io.stderr:write(string.format("du: cannot access '%s': no such file or directory\n", arg))
return 1
else
if fs.isDirectory(path) then
local total = visitor(arg)
if bSummary then
printSize(total, arg)
end
elseif fs.isLink(path) then
printSize(0, arg)
else
printSize(fs.size(path), arg)
end
end
end
return true

22
data/OpenOS/bin/echo.lua Normal file
View File

@@ -0,0 +1,22 @@
local args, options = require("shell").parse(...)
if options.help then
io.write([[
`echo` writes the provided string(s) to the standard output.
-n do not output the trialing newline
-e enable interpretation of backslash escapes
--help display this help and exit
]])
return
end
if options.e then
for index,arg in ipairs(args) do
-- use lua load here to interpret escape sequences such as \27
-- instead of writing my own language to interpret them myself
-- note that in a real terminal, \e is used for \27
args[index] = assert(load("return \"" .. arg:gsub('"', [[\"]]) .. "\""))()
end
end
io.write(table.concat(args," "))
if not options.n then
io.write("\n")
end

723
data/OpenOS/bin/edit.lua Normal file
View File

@@ -0,0 +1,723 @@
local fs = require("filesystem")
local keyboard = require("keyboard")
local shell = require("shell")
local term = require("term") -- TODO use tty and cursor position instead of global area and gpu
local text = require("text")
local unicode = require("unicode")
if not term.isAvailable() then
return
end
local gpu = term.gpu()
local args, options = shell.parse(...)
if #args == 0 then
io.write("Usage: edit <filename>")
return
end
local filename = shell.resolve(args[1])
local file_parentpath = fs.path(filename)
if fs.exists(file_parentpath) and not fs.isDirectory(file_parentpath) then
io.stderr:write(string.format("Not a directory: %s\n", file_parentpath))
return 1
end
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
if fs.isDirectory(filename) then
io.stderr:write("file is a directory\n")
return 1
elseif not fs.exists(filename) and readonly then
io.stderr:write("file system is read only\n")
return 1
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"}, {"shift", "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"}},
cut = {{"control", "k"}},
uncut = {{"control", "u"}}
}
-- 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 cutBuffer = {}
-- cutting is true while we're in a cutting operation and set to false when cursor changes lines
-- basically, whenever you change lines, the cutting operation ends, so the next time you cut a new buffer will be created
local cutting = false
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") ..
prettifyKeybind("Cut", "cut") ..
prettifyKeybind("Uncut", "uncut")
end
-------------------------------------------------------------------------------
local function setStatus(value)
local x, y, w, h = term.getGlobalArea()
value = unicode.wlen(value) > w - 10 and unicode.wtrunc(value, w - 9) or value
value = text.padRight(value, w - 10)
gpu.set(x, y + h - 1, value)
end
local function getArea()
local x, y, w, h = term.getGlobalArea()
return x, y, w, h - 1
end
local function removePrefix(line, length)
if length >= unicode.wlen(line) then
return ""
else
local prefix = unicode.wtrunc(line, length + 1)
local suffix = unicode.sub(line, unicode.len(prefix) + 1)
length = length - unicode.wlen(prefix)
if length > 0 then
suffix = (" "):rep(unicode.charWidth(suffix) - length) .. unicode.sub(suffix, 2)
end
return suffix
end
end
local function lengthToChars(line, length)
if length > unicode.wlen(line) then
return unicode.len(line) + 1
else
local prefix = unicode.wtrunc(line, length)
return unicode.len(prefix) + 1
end
end
local function isWideAtPosition(line, x)
local index = lengthToChars(line, x)
if index > unicode.len(line) then
return false, false
end
local prefix = unicode.sub(line, 1, index)
local char = unicode.sub(line, index, index)
--isWide, isRight
return unicode.isWide(char), unicode.wlen(prefix) == x
end
local function drawLine(x, y, w, h, lineNr)
local yLocal = lineNr - scrollY
if yLocal > 0 and yLocal <= h then
local str = removePrefix(buffer[lineNr] or "", scrollX)
str = unicode.wlen(str) > w and unicode.wtrunc(str, w + 1) or str
str = text.padRight(str, w)
gpu.set(x, y - 1 + lineNr - scrollY, str)
end
end
local function getCursor()
local cx, cy = term.getCursor()
return cx + scrollX, cy + scrollY
end
local function line()
local _, cby = getCursor()
return buffer[cby] or ""
end
local function getNormalizedCursor()
local cbx, cby = getCursor()
local wide, right = isWideAtPosition(buffer[cby], cbx)
if wide and right then
cbx = cbx - 1
end
return cbx, cby
end
local function setCursor(nbx, nby)
local x, y, w, h = getArea()
nbx, nby = math.floor(nbx), math.floor(nby)
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
if h > dy then
gpu.copy(x, y + dy, w, h - dy, 0, -dy)
end
for lineNr = nby - (math.min(dy, h) - 1), nby do
drawLine(x, y, w, h, lineNr)
end
elseif ncy < 1 then
term.setCursorBlink(false)
local sy = nby - 1
local dy = math.abs(scrollY - sy)
scrollY = sy
if h > dy then
gpu.copy(x, y, w, h - dy, 0, dy)
end
for lineNr = nby, nby + (math.min(dy, h) - 1) do
drawLine(x, y, w, h, lineNr)
end
end
term.setCursor(term.getCursor(), nby - scrollY)
nbx = math.max(1, math.min(unicode.wlen(line()) + 1, nbx))
local wide, right = isWideAtPosition(line(), nbx)
local ncx = nbx - scrollX
if ncx > w or (ncx + 1 > w and wide and not right) then
term.setCursorBlink(false)
scrollX = nbx - w + ((wide and not right) and 1 or 0)
for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do
drawLine(x, y, w, h, lineNr)
end
elseif ncx < 1 or (ncx - 1 < 1 and wide and right) then
term.setCursorBlink(false)
scrollX = nbx - 1 - ((wide and right) and 1 or 0)
for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do
drawLine(x, y, w, h, lineNr)
end
end
term.setCursor(nbx - scrollX, nby - scrollY)
--update with term lib
nbx, nby = getCursor()
local locstring = string.format("%d,%d", nby, nbx)
if #cutBuffer > 0 then
locstring = string.format("(#%d) %s", #cutBuffer, locstring)
end
locstring = text.padLeft(locstring, 10)
gpu.set(x + w - #locstring, y + h, locstring)
end
local function highlight(bx, by, length, enabled)
local x, y, w, h = getArea()
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 = gpu.getForeground()
local bg, bgp = gpu.getBackground()
if enabled then
gpu.setForeground(bg, bgp)
gpu.setBackground(fg, fgp)
end
local indexFrom = lengthToChars(buffer[by], bx)
local value = unicode.sub(buffer[by], indexFrom)
if unicode.wlen(value) > length then
value = unicode.wtrunc(value, length + 1)
end
gpu.set(x - 1 + cx, y - 1 + cy, value)
if enabled then
gpu.setForeground(fg, fgp)
gpu.setBackground(bg, bgp)
end
end
local function home()
local _, cby = getCursor()
setCursor(1, cby)
end
local function ende()
local _, cby = getCursor()
setCursor(unicode.wlen(line()) + 1, cby)
end
local function left()
local cbx, cby = getNormalizedCursor()
if cbx > 1 then
local wideTarget, rightTarget = isWideAtPosition(line(), cbx - 1)
if wideTarget and rightTarget then
setCursor(cbx - 2, cby)
else
setCursor(cbx - 1, cby)
end
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 = getNormalizedCursor()
local be = unicode.wlen(line()) + 1
local wide, isRight = isWideAtPosition(line(), cbx + n)
if wide and isRight then
n = n + 1
end
if cbx + n <= 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)
end
cutting = false
end
local function down(n)
n = n or 1
local cbx, cby = getCursor()
if cby < #buffer then
setCursor(cbx, cby + n)
end
cutting = false
end
local function delete(fullRow)
local _, cy = term.getCursor()
local cbx, cby = getCursor()
local x, y, w, h = getArea()
local function deleteRow(row)
local content = table.remove(buffer, row)
local rcy = cy + (row - cby)
if rcy <= h then
gpu.copy(x, y + rcy, w, h - rcy, 0, -1)
drawLine(x, y, w, h, row + (h - rcy))
end
return content
end
if fullRow then
term.setCursorBlink(false)
if #buffer > 1 then
deleteRow(cby)
else
buffer[cby] = ""
gpu.fill(x, y - 1 + cy, w, 1, " ")
end
setCursor(1, cby)
elseif cbx <= unicode.wlen(line()) then
term.setCursorBlink(false)
local index = lengthToChars(line(), cbx)
buffer[cby] = unicode.sub(line(), 1, index - 1) ..
unicode.sub(line(), index + 1)
drawLine(x, y, w, h, cby)
elseif cby < #buffer then
term.setCursorBlink(false)
local append = deleteRow(cby + 1)
buffer[cby] = buffer[cby] .. append
drawLine(x, y, w, h, cby)
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 cbx, cby = getCursor()
local x, y, w, h = getArea()
local index = lengthToChars(line(), cbx)
buffer[cby] = unicode.sub(line(), 1, index - 1) ..
value ..
unicode.sub(line(), index)
drawLine(x, y, w, h, cby)
right(unicode.wlen(value))
setStatus(helpStatusText())
end
local function enter()
term.setCursorBlink(false)
local _, cy = term.getCursor()
local cbx, cby = getCursor()
local x, y, w, h = getArea()
local index = lengthToChars(line(), cbx)
table.insert(buffer, cby + 1, unicode.sub(buffer[cby], index))
buffer[cby] = unicode.sub(buffer[cby], 1, index - 1)
drawLine(x, y, w, h, cby)
if cy < h then
if cy < h - 1 then
gpu.copy(x, y + cy, w, h - (cy + 1), 0, 1)
end
drawLine(x, y, w, h, cby + 1)
end
setCursor(1, cby + 1)
setStatus(helpStatusText())
cutting = false
end
local findText = ""
local function find()
local _, _, _, h = getArea()
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
sx = unicode.wlen(string.sub(buffer[sy], 1, sx - 1)) + 1
cbx, cby = sx, sy
setCursor(cbx, cby)
highlight(cbx, cby, unicode.wlen(findText), true)
end
end
term.setCursor(7 + unicode.wlen(findText), h + 1)
setStatus("Find: " .. findText)
local _, address, char, code = term.pull("key_down")
if address == term.keyboard() then
local handler, name = getKeyBindHandler(code)
highlight(cbx, cby, unicode.wlen(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
end
setCursor(cbx, cby)
setStatus(helpStatusText())
end
local function cut()
if not cutting then
cutBuffer = {}
end
local cbx, cby = getCursor()
table.insert(cutBuffer, buffer[cby])
delete(true)
cutting = true
home()
end
local function uncut()
home()
for _, line in ipairs(cutBuffer) do
insert(line)
enter()
end
end
-------------------------------------------------------------------------------
local keyBindHandlers = {
left = left,
right = right,
up = up,
down = down,
home = home,
eol = ende,
pageUp = function()
local _, _, _, h = getArea()
up(h - 1)
end,
pageDown = function()
local _, _, _, h = getArea()
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
if not fs.exists(file_parentpath) then
fs.makeDirectory(file_parentpath)
end
local f, reason = io.open(filename, "w")
if f then
local chars, firstLine = 0, true
for _, bline in ipairs(buffer) do
if not firstLine then
bline = "\n" .. bline
end
firstLine = false
f:write(bline)
chars = chars + unicode.len(bline)
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,
cut = cut,
uncut = uncut
}
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 = false, false, false
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
local keyboardAddress = term.keyboard()
if (alt == not not keyboard.isAltDown(keyboardAddress)) and
(control == not not keyboard.isControlDown(keyboardAddress)) and
(shift == not not keyboard.isShiftDown(keyboardAddress)) 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 start = 1
local l = value:find("\n", 1, true)
if l then
repeat
local next_line = string.sub(value, start, l - 1)
next_line = text.detab(next_line, 2)
insert(next_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 x, y, w, h = getArea()
local chars = 0
for fline in f:lines() do
table.insert(buffer, fline)
chars = chars + unicode.len(fline)
if #buffer <= h then
drawLine(x, y, w, h, #buffer)
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 = term.pull()
if address == term.keyboard() or address == term.screen() 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
local x, y, w, h = getArea()
arg1 = arg1 - x + 1
arg2 = arg2 - y + 1
if arg1 >= 1 and arg2 >= 1 and arg1 <= w and arg2 <= h then
onClick(arg1, arg2)
end
elseif event == "scroll" then
onScroll(arg3)
else
blink = false
end
if blink then
term.setCursorBlink(true)
end
end
end
term.clear()
term.setCursorBlink(true)

132
data/OpenOS/bin/find.lua Normal file
View File

@@ -0,0 +1,132 @@
local shell = require("shell")
local fs = require("filesystem")
local text = require("text")
local USAGE =
[===[Usage: find [path] [--type=[dfs]] [--[i]name=EXPR]
--path if not specified, path is assumed to be current working directory
--type returns results of a given type, d:directory, f:file, and s:symlinks
--name specify the file name pattern. Use quote to include *. iname is
case insensitive
--help display this help and exit]===]
local args, options = shell.parse(...)
if (not args or not options) or options.help then
print(USAGE)
if not options.help then
return 1
else
return -- nil return, meaning no error
end
end
if #args > 1 then
io.stderr:write(USAGE..'\n')
return 1
end
local path = #args == 1 and args[1] or "."
local bDirs = true
local bFiles = true
local bSyms = true
local fileNamePattern = ""
local bCaseSensitive = true
if options.iname and options.name then
io.stderr:write("find cannot define both iname and name\n")
return 1
end
if options.type then
bDirs = false
bFiles = false
bSyms = false
if options.type == "f" then
bFiles = true
elseif options.type == "d" then
bDirs = true
elseif options.type == "s" then
bSyms = true
else
io.stderr:write(string.format("find: Unknown argument to type: %s\n", options.type))
io.stderr:write(USAGE..'\n')
return 1
end
end
if options.iname or options.name then
bCaseSensitive = options.iname ~= nil
fileNamePattern = options.iname or options.name
if type(fileNamePattern) ~= "string" then
io.stderr:write('find: missing argument to `name\'\n')
return 1
end
if not bCaseSensitive then
fileNamePattern = fileNamePattern:lower()
end
-- prefix any * with . for gnu find glob matching
fileNamePattern = text.escapeMagic(fileNamePattern)
fileNamePattern = fileNamePattern:gsub("%%%*", ".*")
end
local function isValidType(spath)
if not fs.exists(spath) then
return false
end
if fileNamePattern:len() > 0 then
local fileName = spath:gsub('.*/','')
if fileName:len() == 0 then
return false
end
local caseFileName = fileName
if not bCaseSensitive then
caseFileName = caseFileName:lower()
end
local s, e = caseFileName:find(fileNamePattern)
if not s or not e then
return false
end
if s ~= 1 or e ~= caseFileName:len() then
return false
end
end
if fs.isDirectory(spath) then
return bDirs
elseif fs.isLink(spath) then
return bSyms
else
return bFiles
end
end
local function visit(rpath)
local spath = shell.resolve(rpath)
if isValidType(spath) then
local result = rpath:gsub('/+$','')
print(result)
end
if fs.isDirectory(spath) then
local list_result = fs.list(spath)
for list_item in list_result do
visit(rpath:gsub('/+$', '') .. '/' .. list_item)
end
end
end
visit(path)

88
data/OpenOS/bin/flash.lua Normal file
View File

@@ -0,0 +1,88 @@
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.\n")
return
end
local function printRom()
local eeprom = component.eeprom
io.write(eeprom.get())
end
local function readRom()
local eeprom = component.eeprom
local 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

8
data/OpenOS/bin/free.lua Normal file
View File

@@ -0,0 +1,8 @@
local computer = require("computer")
local total = computer.totalMemory()
local max = 0
for _=1,40 do
max = math.max(max, computer.freeMemory())
os.sleep(0) -- invokes gc
end
io.write(string.format("Total%12d\nUsed%13d\nFree%13d\n", total, total - max, max))

323
data/OpenOS/bin/grep.lua Normal file
View File

@@ -0,0 +1,323 @@
--[[
An adaptation of Wobbo's grep
https://raw.githubusercontent.com/OpenPrograms/Wobbo-Programs/master/grep/grep.lua
]]--
-- POSIX grep for OpenComputers
-- one difference is that this version uses Lua regex, not POSIX regex.
local fs = require("filesystem")
local shell = require("shell")
local tty = require("tty")
local computer = require("computer")
-- Process the command line arguments
local args, options = shell.parse(...)
local gpu = tty.gpu()
local function printUsage(ostream, msg)
local s = ostream or io.stdout
if msg then
s:write(msg,'\n')
end
s:write([[Usage: grep [OPTION]... PATTERN [FILE]...
Example: grep -i "hello world" menu.lua main.lua
for more information, run: man grep
]])
end
local PATTERNS = {args[1]}
local FILES = {select(2, table.unpack(args))}
local LABEL_COLOR = 0xb000b0
local LINE_NUM_COLOR = 0x00FF00
local MATCH_COLOR = 0xFF0000
local COLON_COLOR = 0x00FFFF
local function pop(...)
local result
for _,key in ipairs({...}) do
result = options[key] or result
options[key] = nil
end
return result
end
-- Specify the variables for the options
local plain = pop('F','fixed-strings')
plain = not pop('e','--lua-regexp') and plain
local pattern_file = pop('file')
local match_whole_word = pop('w','word-regexp')
local match_whole_line = pop('x','line-regexp')
local ignore_case = pop('i','ignore-case')
local stdin_label = pop('label') or '(standard input)'
local stderr = pop('s','no-messages') and {write=function()end} or io.stderr
local invert_match = not not pop('v','invert-match')
-- no version output, just help
if pop('V','version','help') then
printUsage()
return 0
end
local max_matches = tonumber(pop('max-count')) or math.huge
local print_line_num = pop('n','line-number')
local search_recursively = pop('r','recursive')
-- Table with patterns to check for
if pattern_file then
local pattern_file_path = shell.resolve(pattern_file)
if not fs.exists(pattern_file_path) then
stderr:write('grep: ',pattern_file,': file not found')
return 2
end
table.insert(FILES, 1, PATTERNS[1])
PATTERNS = {}
for line in io.lines(pattern_file_path) do
PATTERNS[#PATTERNS+1] = line
end
end
if #PATTERNS == 0 then
printUsage(stderr)
return 2
end
if #FILES == 0 then
FILES = search_recursively and {'.'} or {'-'}
end
if not options.h and search_recursively then
options.H = true
end
if #FILES < 2 then
options.h = true
end
local f_only = pop('l','files-with-matches')
local no_only = pop('L','files-without-match') and not f_only
local include_filename = pop('H','with-filename')
include_filename = not pop('h','no-filename') or include_filename
local m_only = pop('o','only-matching')
local quiet = pop('q','quiet','silent')
local print_count = pop('c','count')
local colorize = pop('color','colour') and io.output().tty and tty.isAvailable()
local noop = function(...)return ...;end
local setc = colorize and gpu.setForeground or noop
local getc = colorize and gpu.getForeground or noop
local trim = pop('t','trim')
local trim_front = trim and function(s)return s:gsub('^%s+','')end or noop
local trim_back = trim and function(s)return s:gsub('%s+$','')end or noop
if next(options) then
if not quiet then
printUsage(stderr, 'unexpected option: '..next(options))
return 2
end
return 0
end
-- Resolve the location of a file, without searching the path
local function resolve(file)
if file:sub(1,1) == '/' then
return fs.canonical(file)
else
if file:sub(1,2) == './' then
file = file:sub(3, -1)
end
return fs.canonical(fs.concat(shell.getWorkingDirectory(), file))
end
end
--- Builds a case insensitive patterns, code from stackoverflow
--- (questions/11401890/case-insensitive-lua-pattern-matching)
if ignore_case then
for i=1,#PATTERNS do
-- find an optional '%' (group 1) followed by any character (group 2)
PATTERNS[i] = PATTERNS[i]:gsub("(%%?)(.)", function(percent, letter)
if percent ~= "" or not letter:match("%a") then
-- if the '%' matched, or `letter` is not a letter, return "as is"
return percent .. letter
else -- case-insensitive
return string.format("[%s%s]", letter:lower(), letter:upper())
end
end)
end
end
local function getAllFiles(dir, file_list)
for node in fs.list(shell.resolve(dir)) do
local rel_path = dir:gsub("/+$","") .. '/' .. node
local resolved_path = shell.resolve(rel_path)
if fs.isDirectory(resolved_path) then
getAllFiles(rel_path, file_list)
else
file_list[#file_list+1] = rel_path
end
end
end
if search_recursively then
local files = {}
for i,arg in ipairs(FILES) do
if fs.isDirectory(arg) then
getAllFiles(arg, files)
else
files[#files+1]=arg
end
end
FILES=files
end
-- Prepare an iterator for reading files
local function readLines()
local curHand = nil
local curFile = nil
local meta = nil
return function()
if not curFile then
local file = table.remove(FILES, 1)
if not file then
return
end
meta = {line_num=0,hits=0}
if file == "-" then
curFile = file
meta.label = stdin_label
curHand = io.input()
else
meta.label = file
local file, reason = resolve(file)
if fs.exists(file) then
curHand, reason = io.open(file, 'r')
if not curHand then
local msg = string.format("failed to read from %s: %s", meta.label, reason)
stderr:write("grep: ",msg,"\n")
return false, 2
else
curFile = meta.label
end
else
stderr:write("grep: ",file,": file not found\n")
return false, 2
end
end
end
meta.line = nil
if not meta.close and curHand then
meta.line_num = meta.line_num + 1
meta.line = curHand:read("*l")
end
if not meta.line then
curFile = nil
if curHand then
curHand:close()
end
return false, meta
else
return meta, curFile
end
end
end
local function write(part, color)
local prev_color = color and getc()
if color then setc(color) end
io.write(part)
if color then setc(prev_color) end
end
local flush=(f_only or no_only or print_count) and function(m)
if no_only and m.hits == 0 or f_only and m.hits ~= 0 then
write(m.label, LABEL_COLOR)
write('\n')
elseif print_count then
if include_filename then
write(m.label, LABEL_COLOR)
write(':', COLON_COLOR)
end
write(m.hits)
write('\n')
end
end
local ec = nil
local any_hit_ec = 1
local function test(m,p)
local empty_line = true
local last_index, slen = 1, #m.line
local needs_filename, needs_line_num = include_filename, print_line_num
local hit_value = 1
while last_index <= slen and not m.close do
local i, j = m.line:find(p, last_index, plain)
local word_fail, line_fail =
match_whole_word and not (i and not (m.line:sub(i-1,i-1)..m.line:sub(j+1,j+1)):find("[%a_]")),
match_whole_line and not (i==1 and j==slen)
local matched = not ((m_only or last_index==1) and not i)
if (hit_value == 1 and word_fail) or line_fail then
matched,i,j = false
end
if invert_match == matched then break end
if max_matches == 0 then os.exit(1) end
any_hit_ec = 0
m.hits, hit_value = m.hits + hit_value, 0
if f_only or no_only then
m.close = true
end
if flush or quiet then return end
if needs_filename then
write(m.label, LABEL_COLOR)
write(':', COLON_COLOR)
needs_filename = nil
end
if needs_line_num then
write(m.line_num, LINE_NUM_COLOR)
write(':', COLON_COLOR)
needs_line_num = nil
end
local s=m_only and '' or m.line:sub(last_index,(i or 0)-1)
local g=i and m.line:sub(i,j) or ''
if i==1 then g=trim_front(g) elseif last_index==1 then s=trim_front(s) end
if j==slen then g=trim_back(g) elseif not i then s=trim_back(s) end
write(s)
write(g, MATCH_COLOR)
empty_line = false
last_index = (j or slen)+1
if m_only or last_index>slen then
write("\n")
empty_line = true
needs_filename, needs_line_num = include_filename, print_line_num
elseif p:find("^^") and not plain then p="^$" end
end
if not empty_line then write("\n") end
if max_matches ~= math.huge and max_matches >= m.hits then
m.close = true
end
end
local uptime = computer.uptime
local last_sleep = uptime()
for meta,status in readLines() do
if uptime() - last_sleep > 1 then
os.sleep(0)
last_sleep = uptime()
end
if not meta then
if type(status) == 'table' then if flush then
flush(status) end -- this was the last object, closing out
elseif status then
ec = status or ec
end
else
for _,p in ipairs(PATTERNS) do
test(meta,p)
end
end
end
return ec or any_hit_ec

131
data/OpenOS/bin/head.lua Normal file
View File

@@ -0,0 +1,131 @@
local shell = require("shell")
local args, options = shell.parse(...)
local error_code = 0
local function pop(key, convert)
local result = options[key]
options[key] = nil
if result and convert then
local c = tonumber(result)
if not c then
io.stderr:write(string.format("use --%s=n where n is a number\n", key))
options.help = true
error_code = 1
end
result = c
end
return result
end
local bytes = pop('bytes', true)
local lines = pop('lines', true)
local quiet = {pop('q'), pop('quiet'), pop('silent')}
quiet = quiet[1] or quiet[2] or quiet[3]
local verbose = {pop('v'), pop('verbose')}
verbose = verbose[1] or verbose[2]
local help = pop('help')
if help or next(options) then
local invalid_key = next(options)
if invalid_key then
invalid_key = string.format('invalid option: %s\n', invalid_key)
error_code = 1
else
invalid_key = ''
end
print(invalid_key .. [[Usage: head [--lines=n] file
Print the first 10 lines of each FILE to stdout.
For more info run: man head]])
os.exit(error_code)
end
if #args == 0 then
args = {'-'}
end
if quiet and verbose then
quiet = false
end
local function new_stream()
return
{
open=true,
capacity=math.abs(lines or bytes or 10),
bytes=bytes,
buffer=(lines and lines < 0 and {}) or (bytes and bytes < 0 and '')
}
end
local function close(stream)
if stream.buffer then
if type(stream.buffer) == 'table' then
stream.buffer = table.concat(stream.buffer)
end
io.stdout:write(stream.buffer)
stream.buffer = nil
end
stream.open = false
end
local function push(stream, line)
if not line then
return close(stream)
end
local cost = stream.bytes and line:len() or 1
stream.capacity = stream.capacity - cost
if not stream.buffer then
if stream.bytes and stream.capacity < 0 then
line = line:sub(1,stream.capacity-1)
end
io.write(line)
if stream.capacity <= 0 then
return close(stream)
end
else
if type(stream.buffer) == 'table' then -- line storage
stream.buffer[#stream.buffer+1] = line
if stream.capacity < 0 then
table.remove(stream.buffer, 1)
stream.capacity = 0 -- zero out
end
else -- byte storage
stream.buffer = stream.buffer .. line
if stream.capacity < 0 then
stream.buffer = stream.buffer:sub(-stream.capacity+1)
stream.capacity = 0 -- zero out
end
end
end
end
for i=1,#args do
local arg = args[i]
local file, reason
if arg == '-' then
arg = 'standard input'
file = io.stdin
else
file, reason = io.open(arg, 'r')
if not file then
io.stderr:write(string.format([[head: cannot open '%s' for reading: %s]], arg, reason))
end
end
if file then
if verbose or #args > 1 then
io.write(string.format('==> %s <==\n', arg))
end
local stream = new_stream()
while stream.open do
push(stream, file:read('*L'))
end
file:close()
end
end

View File

@@ -0,0 +1,30 @@
local shell = require("shell")
local args, ops = shell.parse(...)
local hostname = args[1]
if hostname then
local file, reason = io.open("/etc/hostname", "w")
if not file then
io.stderr:write("failed to open for writing: ", reason, "\n")
return 1
end
file:write(hostname)
file:close()
ops.update = true
else
local file = io.open("/etc/hostname")
if file then
hostname = file:read("*l")
file:close()
end
end
if ops.update then
os.setenv("HOSTNAME_SEPARATOR", hostname and #hostname > 0 and ":" or "")
os.setenv("HOSTNAME", hostname)
elseif hostname then
print(hostname)
else
io.stderr:write("Hostname not set\n")
return 1
end

View File

@@ -0,0 +1,53 @@
local computer = require("computer")
local options
do
local basic, reason = loadfile("/lib/core/install_basics.lua", "bt", _G)
if not basic then
io.stderr:write("failed to load install: " .. tostring(reason) .. "\n")
return 1
end
options = basic(...)
end
if not options then
return
end
if computer.freeMemory() < 50000 then
print("Low memory, collecting garbage")
for i = 1, 20 do
os.sleep(0)
end
end
local transfer = require("tools/transfer")
for _, inst in ipairs(options.cp_args) do
local ec = transfer.batch(table.unpack(inst))
if ec ~= nil and ec ~= 0 then
return ec
end
end
print("Installation complete!")
if options.setlabel then
pcall(options.target.dev.setLabel, options.label)
end
if options.setboot then
local address = options.target.dev.address
if computer.setBootAddress(address) then
print("Boot address set to " .. address)
end
end
if options.reboot then
io.write("Reboot now? [Y/n] ")
if ((io.read() or "n") .. "y"):match("^%s*[Yy]") then
print("\nRebooting now!\n")
computer.shutdown(true)
end
end
print("Returning to shell.\n")

49
data/OpenOS/bin/label.lua Normal file
View File

@@ -0,0 +1,49 @@
local shell = require("shell")
local devfs = require("devfs")
local comp = require("component")
local args, options = shell.parse(...)
if #args < 1 then
io.write("Usage: label [-a] <device> [<label>]\n")
io.write(" -a Device is specified via label or address instead of by path.\n")
return 1
end
local filter = args[1]
local label = args[2]
local proxy, reason
if options.a then
for addr in comp.list() do
if addr:sub(1, filter:len()) == filter then
proxy, reason = comp.proxy(addr)
break
end
local tmp_proxy = comp.proxy(addr)
local tmp_label = devfs.getDeviceLabel(tmp_proxy)
if tmp_label == filter then
proxy = tmp_proxy
break
end
end
else
proxy, reason = devfs.getDevice(filter)
end
if not proxy then
io.stderr:write(reason..'\n')
return 1
end
if #args < 2 then
local label = devfs.getDeviceLabel(proxy)
if label then
print(label)
else
io.stderr:write("no label\n")
return 1
end
else
devfs.setDeviceLabel(proxy, label)
end

149
data/OpenOS/bin/less.lua Normal file
View File

@@ -0,0 +1,149 @@
local keys = require("keyboard").keys
local shell = require("shell")
local unicode = require("unicode")
local term = require("term") -- using term for negative scroll feature
local args, ops = shell.parse(...)
if #args > 1 then
io.write("Usage: ", os.getenv("_"):match("/([^/]+)%.lua$"), " <filename>\n")
io.write("- or no args reads stdin\n")
return 1
end
local cat_cmd = table.concat({"cat", ...}, " ")
if not io.output().tty then
return os.execute(cat_cmd)
end
local preader = io.popen(cat_cmd)
local scrollback = not ops.noback and {}
local bottom = 0
local end_of_buffer = false
local width, height = term.getViewport()
local function split(full_line)
local index = 1
local parts = {}
while true do
local sub = full_line:sub(index, index + width*3)
-- checking #sub < width first is faster, save a unicode call
if #sub < width or unicode.wlen(sub) <= width then
parts[#parts + 1] = sub
break
end
parts[#parts + 1] = unicode.wtrunc(sub, width + 1)
index = index + #parts[#parts]
if index > #full_line then
break
end
end
return parts
end
local function scan(num)
local result = {}
local line_count = 0
for i=1, num do
local lines = {}
if scrollback and (bottom + i) <= #scrollback then
lines = {scrollback[bottom + i]}
else
local full_line = preader:read()
if not full_line then preader:close() break end
-- with buffering, we can buffer ahead too, and read more smoothly
local buffering = false
for _,line in ipairs(split(full_line)) do
if not buffering then
lines[#lines + 1] = line
end
if scrollback then
buffering = true
scrollback[#scrollback + 1] = line
end
end
end
for _,line in ipairs(lines) do
result[#result + 1] = line
line_count = line_count + 1
if #result > height then
table.remove(result, 1)
end
end
if line_count >= num then
break
end
end
return result, line_count
end
local function status()
if end_of_buffer then
if ops.noback then
os.exit()
end
io.write("(END)")
end
io.write(":")
end
local function goback(n)
if not scrollback then return end
local current_top = bottom - height + 1
n = math.min(current_top, n)
if n < 1 then return end
local top = current_top - n + 1
term.scroll(-n)
term.setCursor(1, 1)
for i=1, n do
if i >= height then
break
end
print(scrollback[top + i - 1])
end
term.setCursor(1, height)
bottom = bottom - n
end_of_buffer = false
end
local function goforward(n)
term.clearLine()
local update, line_count = scan(n)
for _,line in ipairs(update) do
print(line)
end
if line_count < n then
end_of_buffer = true
end
bottom = bottom + line_count
end
goforward(height - 1)
while true do
term.clearLine()
status()
local e, _, _, code = term.pull()
if e == "interrupted" then
break
elseif e == "key_down" then
if code == keys.q then
term.clearLine()
os.exit() -- abort
elseif code == keys["end"] then
goforward(math.huge)
elseif code == keys.space or code == keys.pageDown then
goforward(height - 1)
elseif code == keys.enter or code == keys.down then
goforward(1)
elseif code == keys.up then
goback(1)
elseif code == keys.pageUp then
goback(height - 1)
elseif code == keys.home then
goback(math.huge)
end
end
end

33
data/OpenOS/bin/list.lua Normal file
View File

@@ -0,0 +1,33 @@
local fs = require("filesystem")
local shell = require("shell")
local args, ops = shell.parse(...)
if #args == 0 then
table.insert(args, ".")
end
local arg = args[1]
local path = shell.resolve(arg)
if ops.help then
io.write([[Usage: list [path]
path:
optional argument (defaults to ./)
Displays a list of files in the given path with no added formatting
Intended for low memory systems
]])
return 0
end
local real, why = fs.realPath(path)
if real and not fs.exists(real) then
why = "no such file or directory"
end
if why then
io.stderr:write(string.format("cannot access '%s': %s", arg, tostring(why)))
return 1
end
for item in fs.list(real) do
io.write(item, '\n')
end

34
data/OpenOS/bin/ln.lua Normal file
View File

@@ -0,0 +1,34 @@
local fs = require("filesystem")
local shell = require("shell")
local args = shell.parse(...)
if #args == 0 then
io.write("Usage: ln <target> [<name>]\n")
return 1
end
local target_name = args[1]
local target = shell.resolve(target_name)
-- don't link from target if it doesn't exist, unless it is a broken link
if not fs.exists(target) and not fs.isLink(target) then
io.stderr:write("ln: failed to access '" .. target_name .. "': No such file or directory\n")
return 1
end
local linkpath
if #args > 1 then
linkpath = shell.resolve(args[2])
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_name, linkpath)
if not result then
io.stderr:write(reason..'\n')
return 1
end

18
data/OpenOS/bin/ls.lua Normal file
View File

@@ -0,0 +1,18 @@
-- load complex, if we can (might be low on memory)
local ok, why = pcall(function(...)
return loadfile("/lib/core/full_ls.lua", "bt", _G)(...)
end, ...)
if not ok then
if type(why) == "table" then
if why.code == 0 then
return
end
why = why.reason
end
io.stderr:write(tostring(why) .. "\nFor low memory systems, try using `list` instead\n")
return 1
end
return why

42
data/OpenOS/bin/lshw.lua Normal file
View File

@@ -0,0 +1,42 @@
local computer = require("computer")
local shell = require("shell")
local text = require("text")
local args, options = shell.parse(...)
local devices = computer.getDeviceInfo()
local columns = {}
if not next(options, nil) then
options.t = true
options.d = true
options.p = true
end
if options.t then table.insert(columns, "Class") end
if options.d then table.insert(columns, "Description") end
if options.p then table.insert(columns, "Product") end
if options.v then table.insert(columns, "Vendor") end
if options.c then table.insert(columns, "Capacity") end
if options.w then table.insert(columns, "Width") end
if options.s then table.insert(columns, "Clock") end
local m = {}
for address, info in pairs(devices) do
for col, name in ipairs(columns) do
m[col] = math.max(m[col] or 1, (info[name:lower()] or ""):len())
end
end
io.write(text.padRight("Address", 10))
for col, name in ipairs(columns) do
io.write(text.padRight(name, m[col] + 2))
end
io.write("\n")
for address, info in pairs(devices) do
io.write(text.padRight(address:sub(1, 5).."...", 10))
for col, name in ipairs(columns) do
io.write(text.padRight(info[name:lower()] or "", m[col] + 2))
end
io.write("\n")
end

27
data/OpenOS/bin/lua.lua Normal file
View File

@@ -0,0 +1,27 @@
local shell = require("shell")
local args = shell.parse(...)
if #args == 0 then
args = {"/lib/core/lua_shell.lua"}
end
local filename = args[1]
local buffer, script, reason
buffer = io.lines(filename, "*a")()
if buffer then
buffer = buffer:gsub("^#![^\n]+", "") -- remove shebang if any
script, reason = load(buffer, "="..filename)
else
reason = string.format("could not open %s for reading", filename)
end
if not script then
io.stderr:write(tostring(reason) .. "\n")
os.exit(false)
end
buffer, reason = pcall(script, table.unpack(args, 2))
if not buffer then
io.stderr:write(type(reason) == "table" and reason.reason or tostring(reason), "\n")
os.exit(false)
end

20
data/OpenOS/bin/man.lua Normal file
View File

@@ -0,0 +1,20 @@
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.\n")
return 1
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 .. '\n')
return 1

27
data/OpenOS/bin/mkdir.lua Normal file
View File

@@ -0,0 +1,27 @@
local fs = require("filesystem")
local shell = require("shell")
local args = shell.parse(...)
if #args == 0 then
io.write("Usage: mkdir <dirname1> [<dirname2> [...]]\n")
return 1
end
local ec = 0
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("mkdir: cannot create directory '" .. tostring(args[i]) .. "': " .. reason .. "\n")
ec = 1
end
end
return ec

70
data/OpenOS/bin/mktmp.lua Normal file
View File

@@ -0,0 +1,70 @@
local fs = require("filesystem")
local shell = require("shell")
local sh = require("sh")
local touch = loadfile(shell.resolve("touch", "lua"))
local mkdir = loadfile(shell.resolve("mkdir", "lua"))
if not touch then
local errorMessage = "missing tools for mktmp"
io.stderr:write(errorMessage .. '\n')
return false, errorMessage
end
local args, ops = shell.parse(...)
local function pop(...)
local result
for _,key in ipairs({...}) do
result = ops[key] or result
ops[key] = nil
end
return result
end
local directory = pop('d')
local verbose = pop('v', 'verbose')
local quiet = pop('q', 'quiet')
if pop('help') or #args > 1 or next(ops) then
print([[Usage: mktmp [OPTION] [PATH]
Create a new file with a random name in $TMPDIR or PATH argument if given
-d create a directory instead of a file
-v, --verbose print result to stdout, even if no tty
-q, --quiet do not print results to stdout, even if tty (verbose overrides)
--help print this help message]])
if next(ops) then
io.stderr:write("invalid option: " .. (next(ops)) .. '\n')
return 1
end
return
end
if not verbose then
if not quiet then
if io.stdout.tty then
verbose = true
end
end
end
local prefix = args[1] or os.getenv("TMPDIR") .. '/'
if not fs.exists(prefix) then
io.stderr:write(
string.format(
"cannot create tmp file or directory at %s, it does not exist\n",
prefix))
return 1
end
local tmp = os.tmpname()
local ok, reason = (directory and mkdir or touch)(tmp)
if sh.internal.command_passed(ok) then
if verbose then
print(tmp)
end
return tmp
end
return ok, reason

96
data/OpenOS/bin/mount.lua Normal file
View File

@@ -0,0 +1,96 @@
local fs = require("filesystem")
local shell = require("shell")
local function usage()
io.stderr:write([==[
Usage: mount [OPTIONS] [device] [path]")
If no args are given, all current mount points are printed.
<Options> Note that multiple options can be used together
-r, --ro Mount the filesystem read only
--bind Create a mount bind point, folder to folder
<Args>
device Specify filesystem device by one of:
a. label
b. address (can be abbreviated)
c. folder path (requires --bind)
path Target folder path to mount to
See `man mount` for more details
]==])
os.exit(1)
end
-- smart parse, follow arg after -o
local args, opts = shell.parse(...)
opts.readonly = opts.r or opts.readonly
if opts.h or opts.help then
usage()
end
local function print_mounts()
-- for each mount
local mounts = {}
for proxy,path in fs.mounts() do
local device = {}
device.dev_path = proxy.address
device.mount_path = path
device.rw_ro = proxy.isReadOnly() and "ro" or "rw"
device.fs_label = proxy.getLabel() or proxy.address
mounts[device.dev_path] = mounts[device.dev_path] or {}
local dev_mounts = mounts[device.dev_path]
table.insert(dev_mounts, device)
end
local smounts = {}
for key,value in pairs(mounts) do
smounts[#smounts+1] = {key, value}
end
table.sort(smounts, function(a,b) return a[1] < b[1] end)
for _, dev in ipairs(smounts) do
local dev_path, dev_mounts = table.unpack(dev)
for _,device in ipairs(dev_mounts) do
local rw_ro = "(" .. device.rw_ro .. ")"
local fs_label = "\"" .. device.fs_label .. "\""
io.write(string.format("%-8s on %-10s %s %s\n",
dev_path:sub(1,8),
device.mount_path,
rw_ro,
fs_label))
end
end
end
local function do_mount()
-- bind converts a path to a proxy
local proxy, reason = fs.proxy(args[1], opts)
if not proxy then
io.stderr:write("Failed to mount: ", tostring(reason), "\n")
os.exit(1)
end
local result, mount_failure = fs.mount(proxy, shell.resolve(args[2]))
if not result then
io.stderr:write(mount_failure, "\n")
os.exit(2) -- error code
end
end
if #args == 0 then
if next(opts) then
io.stderr:write("Missing argument\n")
usage()
else
print_mounts()
end
elseif #args == 2 then
do_mount()
else
io.stderr:write("wrong number of arguments: ", #args, "\n")
usage()
end

33
data/OpenOS/bin/mv.lua Normal file
View File

@@ -0,0 +1,33 @@
local shell = require("shell")
local transfer = require("tools/transfer")
local args, options = shell.parse(...)
options.h = options.h or options.help
if #args < 2 or options.h then
io.write([[Usage: mv [OPTIONS] <from> <to>
-f overwrite without prompt
-i prompt before overwriting
unless -f
-v verbose
-n do not overwrite an existing file
--skip=P ignore paths matching lua regex P
-h, --help show this help
]])
return not not options.h
end
-- clean options for move (as opposed to copy)
options =
{
cmd = "mv",
f = options.f,
i = options.i,
v = options.v,
n = options.n, -- no clobber
skip = {options.skip},
P = true, -- move operations always preserve
r = true, -- move is allowed to move entire dirs
x = true, -- cannot move mount points
}
return transfer.batch(args, options)

View File

@@ -0,0 +1,153 @@
--[[ 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 = "https://pastebin.com/raw/" .. 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.
local 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.
local 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.
local 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,
"https://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).")

View File

@@ -0,0 +1,28 @@
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.\n")
return 1
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\n")
return 1
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, "\n")
else
io.stderr:write("no primary component for this type\n")
return 1
end

149
data/OpenOS/bin/ps.lua Normal file
View File

@@ -0,0 +1,149 @@
local process = require("process")
local unicode = require("unicode")
local event = require("event")
local event_mt = getmetatable(event.handlers)
-- WARNING this code does not use official kernel API and is likely to change
local data = {}
local widths = {}
local sorted = {}
local moved_indexes = {}
local elbow = unicode.char(0x2514)
local function thread_id(t,p)
if t then
return tostring(t):gsub("^thread: 0x", "")
end
-- find the parent thread
for k,v in pairs(process.list) do
if v == p then
return thread_id(k)
end
end
return "-"
end
local cols =
{
{"PID", thread_id},
{"EVENTS", function(_,p)
local handlers = {}
if event_mt.threaded then
handlers = rawget(p.data, "handlers") or {}
elseif not p.parent then
handlers = event.handlers
end
local count = 0
for _ in pairs(handlers) do
count = count + 1
end
return count == 0 and "-" or tostring(count)
end},
{"THREADS", function(_,p)
-- threads are handles with mt.close == thread.waitForAll
local count = 0
for _,h in ipairs(p.data.handles) do
local mt = getmetatable(h)
if mt and mt.__status then
count = count + 1
end
end
return count == 0 and "-" or tostring(count)
end},
{"PARENT", function(_,p)
for _,process_info in pairs(process.list) do
for i,handle in ipairs(process_info.data.handles) do
local mt = getmetatable(handle)
if mt and mt.__status then
if mt.process == p then
return thread_id(nil, process_info)
end
end
end
end
return thread_id(nil, p.parent)
end},
{"HANDLES", function(_, p)
local count = #p.data.handles
return count == 0 and "-" or tostring(count)
end},
{"CMD", function(_,p) return p.command end},
}
local function add_field(key, value)
if not data[key] then data[key] = {} end
table.insert(data[key], value)
widths[key] = math.max(widths[key] or 0, #value)
end
for _,key in ipairs(cols) do
add_field(key[1], key[1])
end
for thread_handle, process_info in pairs(process.list) do
for _,key in ipairs(cols) do
add_field(key[1], key[2](thread_handle, process_info))
end
end
local parent_index
for index,set in ipairs(cols) do
if set[1] == "PARENT" then
parent_index = index
break
end
end
assert(parent_index, "did not find a parent column")
local function move_to_sorted(index)
if moved_indexes[index] then
return false
end
local entry = {}
for k,v in pairs(data) do
entry[k] = v[index]
end
sorted[#sorted + 1] = entry
moved_indexes[index] = true
return true
end
local function make_elbow(depth)
return (" "):rep(depth - 1) .. (depth > 0 and elbow or "")
end
-- remove COLUMN labels to simplify sort
move_to_sorted(1)
local function update_family(parent, depth)
depth = depth or 0
parent = parent or "-"
for index in ipairs(data.PID) do
local this_parent = data[cols[parent_index][1]][index]
if this_parent == parent then
local dash_cmd = make_elbow(depth) .. data.CMD[index]
data.CMD[index] = dash_cmd
widths.CMD = math.max(widths.CMD or 0, #dash_cmd)
if move_to_sorted(index) then
update_family(data.PID[index], depth + 1)
end
end
end
end
update_family()
table.remove(cols, parent_index) -- don't show parent id
for _,set in ipairs(sorted) do
local split = ""
for _,key in ipairs(cols) do
local label = key[1]
local format = split .. "%-" .. tostring(widths[label]) .. "s"
io.write(string.format(format, set[label]))
split = " "
end
print()
end

14
data/OpenOS/bin/pwd.lua Normal file
View File

@@ -0,0 +1,14 @@
local shell = require("shell")
local fs = require("filesystem")
local _,op = shell.parse(...)
local path, why = shell.getWorkingDirectory(), ""
if op.P then
path, why = fs.realPath(path)
end
if not path then
io.stderr:write(string.format("error retrieving current directory: %s", why))
os.exit(1)
end
io.write(path, "\n")

150
data/OpenOS/bin/rc.lua Normal file
View File

@@ -0,0 +1,150 @@
local rc = require("rc")
local fs = require("filesystem")
local function loadConfig()
local env = {}
local result, reason = loadfile('/etc/rc.cfg', 't', env)
if result then
result, reason = xpcall(result, debug.traceback)
if result then
return env
end
end
return nil, reason
end
local function saveConfig(conf)
local file, reason = io.open('/etc/rc.cfg', 'w')
if not file then
return nil, reason
end
for key, value in pairs(conf) do
file:write(tostring(key) .. " = " .. require("serialization").serialize(value) .. "\n")
end
file:close()
return true
end
local function load(name, args)
if rc.loaded[name] then
return rc.loaded[name]
end
local fileName = fs.concat('/etc/rc.d/', name .. '.lua')
local env = setmetatable({args = args}, {__index = _G})
local result, reason = loadfile(fileName, 't', env)
if result then
result, reason = xpcall(result, debug.traceback)
if result then
rc.loaded[name] = env
return env
else
return nil, string.format("%s failed to start: %s", fileName, reason)
end
end
return nil, string.format("%s failed to load: %s", fileName, reason)
end
function rc.unload(name)
rc.loaded[name] = nil
end
local function rawRunCommand(conf, name, cmd, args, ...)
local result, what = load(name, args)
if result then
if not cmd then
io.output():write("Commands for service " .. name .. "\n")
for command, val in pairs(result) do
if type(val) == "function" then
io.output():write(tostring(command) .. " ")
end
end
return true
elseif type(result[cmd]) == "function" then
result, what = xpcall(result[cmd], debug.traceback, ...)
if result then
return true
end
elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then
local daemon = result
result, what = xpcall(daemon["stop"], debug.traceback, ...)
if result then
result, what = xpcall(daemon["start"], debug.traceback, ...)
if result then
return true
end
end
elseif cmd == "enable" then
conf.enabled = conf.enabled or {}
for _, _name in ipairs(conf.enabled) do
if name == _name then
return nil, "Service already enabled"
end
end
conf.enabled[#conf.enabled + 1] = name
return saveConfig(conf)
elseif cmd == "disable" then
conf.enabled = conf.enabled or {}
for n, _name in ipairs(conf.enabled) do
if name == _name then
table.remove(conf.enabled, n)
end
end
return saveConfig(conf)
else
what = "Command '" .. cmd .. "' not found in daemon '" .. name .. "'"
end
end
return nil, what
end
local function runCommand(name, cmd, ...)
local conf, reason = loadConfig()
if not conf then
return nil, reason
end
return rawRunCommand(conf, name, cmd, conf[name], ...)
end
local function allRunCommand(cmd, ...)
local conf, reason = loadConfig()
if not conf then
return nil, reason
end
local results = {}
for _, name in ipairs(conf.enabled or {}) do
results[name] = table.pack(rawRunCommand(conf, name, cmd, conf[name], ...))
end
return results
end
local stream = io.stderr
local write = stream.write
if select("#", ...) == 0 then
-- if called during boot, pipe errors to onError handler
if _G.runlevel == "S" then
write = function(_, msg)
require("event").onError(msg)
end
end
local results, reason = allRunCommand("start")
if not results then
local msg = "rc failed to start:"..tostring(reason)
write(stream, msg, "\n")
return
end
for _, result in pairs(results) do
local ok, reason = table.unpack(result)
if not ok then
write(stream, reason, "\n")
end
end
else
local result, reason = runCommand(...)
if not result then
write(stream, reason, "\n")
return 1
end
end

View File

@@ -0,0 +1,4 @@
local computer = require("computer")
io.write("Rebooting...")
computer.shutdown(true)

View File

@@ -0,0 +1,103 @@
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.\n")
return 1
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\n")
return 1
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()) .. "\n")
elseif options.f then
if not rs.setWirelessOutput then
io.stderr:write("wireless redstone not available\n")
return 1
end
if #args > 0 then
local value = args[1]
if not tonumber(value) then
io.stderr:write("invalid frequency\n")
return 1
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\n")
return 1
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\n")
return 1
end
local color = colors[args[2]]
if not color then
io.stderr:write("invalid color\n")
return 1
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) .. "\n")
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) .. "\n")
end
end

View File

@@ -0,0 +1,32 @@
local shell = require("shell")
local tty = require("tty")
local args = shell.parse(...)
local gpu = tty.gpu()
if #args == 0 then
local w, h = gpu.getViewport()
io.write(w," ",h,"\n")
return
end
if #args ~= 2 then
print("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\n")
return 1
end
local result, reason = gpu.setResolution(w, h)
if not result then
if reason then -- otherwise we didn't change anything
io.stderr:write(reason..'\n')
end
return 1
end
tty.clear()

160
data/OpenOS/bin/rm.lua Normal file
View File

@@ -0,0 +1,160 @@
local fs = require("filesystem")
local shell = require("shell")
local function usage()
print("Usage: rm [options] <filename1> [<filename2> [...]]"..[[
-f ignore nonexistent files and arguments, never prompt
-r remove directories and their contents recursively
-v explain what is being done
--help display this help and exit
For complete documentation and more options, run: man rm]])
end
local args, options = shell.parse(...)
if #args == 0 or options.help then
usage()
return 1
end
local bRec = options.r or options.R or options.recursive
local bForce = options.f or options.force
local bVerbose = options.v or options.verbose
local bEmptyDirs = options.d or options.dir
local promptLevel = (options.I and 3) or (options.i and 1) or 0
bVerbose = bVerbose and not bForce
promptLevel = bForce and 0 or promptLevel
local function perr(...)
if not bForce then
io.stderr:write(...)
end
end
local function pout(...)
if not bForce then
io.stdout:write(...)
end
end
local metas = {}
-- promptLevel 3 done before fs.exists
-- promptLevel 1 asks for each, displaying fs.exists on hit as it visits
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 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
local function createMeta(origin, rel)
local m = {origin=origin,rel=rel:gsub("/+$", "")}
if _dir(m) then
m.rel = m.rel .. '/'
end
return m
end
local function unlink(path)
os.remove(path)
return true
end
local function confirm()
if bForce then
return true
end
local r = io.read()
return r == 'y' or r == 'yes'
end
local remove
local function remove_all(parent)
if parent == nil or not _dir(parent) or _empty(parent) then
return true
end
local all_ok = true
if bRec and promptLevel == 1 then
pout(string.format("rm: descend into directory `%s'? ", parent.rel))
if not confirm() then
return false
end
for file in fs.list(_path(parent)) do
local child = createMeta(parent.origin, parent.rel .. file)
all_ok = remove(child) and all_ok
end
end
return all_ok
end
remove = function(meta)
if not remove_all(meta) then
return false
end
if not _exists(meta) then
perr(string.format("rm: cannot remove `%s': No such file or directory\n", meta.rel))
return false
elseif _dir(meta) and not bRec and not (_empty(meta) and bEmptyDirs) then
if not bEmptyDirs then
perr(string.format("rm: cannot remove `%s': Is a directory\n", meta.rel))
else
perr(string.format("rm: cannot remove `%s': Directory not empty\n", meta.rel))
end
return false
end
local ok = true
if promptLevel == 1 then
if _dir(meta) then
pout(string.format("rm: remove directory `%s'? ", meta.rel))
elseif meta.link then
pout(string.format("rm: remove symbolic link `%s'? ", meta.rel))
else -- file
pout(string.format("rm: remove regular file `%s'? ", meta.rel))
end
ok = confirm()
end
if ok then
if _readonly(meta) then
perr(string.format("rm: cannot remove `%s': Is read only\n", meta.rel))
return false
elseif not unlink(_path(meta)) then
perr(meta.rel .. ": failed to be removed\n")
ok = false
elseif bVerbose then
pout("removed '" .. meta.rel .. "'\n");
end
end
return ok
end
for _,arg in ipairs(args) do
metas[#metas+1] = createMeta(arg, arg)
end
if promptLevel == 3 and #metas > 3 then
pout(string.format("rm: remove %i arguments? ", #metas))
if not confirm() then
return
end
end
local ok = true
for _,meta in ipairs(metas) do
local result = remove(meta)
ok = ok and result
end
return bForce or ok

104
data/OpenOS/bin/rmdir.lua Normal file
View File

@@ -0,0 +1,104 @@
local shell = require("shell")
local fs = require("filesystem")
local text = require("text")
local args, options = shell.parse(...)
local function usage()
print(
[[Usage: rmdir [OPTION]... DIRECTORY...
Removes the DIRECTORY(ies), if they are empty.
-q, --ignore-fail-on-non-empty
ignore failures due solely to non-empty directories
-p, --parents remove DIRECTORY and its empty ancestors
e.g. 'rmdir -p a/b/c' is similar to 'rmdir a/b/c a/b a'
-v, --verbose output a diagnostic for every directory processed
--help display this help and exit]])
end
if options.help then
usage()
return 0
end
if #args == 0 then
io.stderr:write("rmdir: missing operand\n")
return 1
end
options.p = options.p or options.parents
options.v = options.v or options.verbose
options.q = options.q or options['ignore-fail-on-non-empty']
local ec = 0
local function ec_bump()
ec = 1
return 1
end
local function remove(path, ...)
-- check to end recursion
if path == nil then
return true
end
if options.v then
print(string.format('rmdir: removing directory, %s', path))
end
local rpath = shell.resolve(path)
if path == '.' then
io.stderr:write('rmdir: failed to remove directory \'.\': Invalid argument\n')
return ec_bump()
elseif not fs.exists(rpath) then
io.stderr:write("rmdir: cannot remove " .. path .. ": path does not exist\n")
return ec_bump()
elseif fs.isLink(rpath) or not fs.isDirectory(rpath) then
io.stderr:write("rmdir: cannot remove " .. path .. ": not a directory\n")
return ec_bump()
else
local list, reason = fs.list(rpath)
if not list then
io.stderr:write(tostring(reason)..'\n')
return ec_bump()
else
if list() then
if not options.q then
io.stderr:write("rmdir: failed to remove " .. path .. ": Directory not empty\n")
end
return ec_bump()
else
-- path exists and is empty?
local ok, reason = fs.remove(rpath)
if not ok then
io.stderr:write(tostring(reason)..'\n')
return ec_bump(), reason
end
return remove(...) -- the final return of all else
end
end
end
end
for _,path in ipairs(args) do
-- clean up the input
path = path:gsub('/+', '/')
local segments = {}
if options.p and path:len() > 1 and path:find('/') then
local chain = text.split(path, {'/'}, true)
local prefix = ''
for _,e in ipairs(chain) do
table.insert(segments, 1, prefix .. e)
prefix = prefix .. e .. '/'
end
else
segments = {path}
end
remove(table.unpack(segments))
end
return ec

23
data/OpenOS/bin/set.lua Normal file
View File

@@ -0,0 +1,23 @@
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 e = expr:find('=')
if e then
os.setenv(expr:sub(1,e-1), expr:sub(e+1))
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

50
data/OpenOS/bin/sh.lua Normal file
View File

@@ -0,0 +1,50 @@
local shell = require("shell")
local tty = require("tty")
local text = require("text")
local sh = require("sh")
debugprint("a")
local args = shell.parse(...)
shell.prime()
debugprint("b")
if #args == 0 then
debugprint("c")
local has_profile
local input_handler = {hint = sh.hintHandler}
while true do
if io.stdin.tty and io.stdout.tty then
if not has_profile then -- first time run AND interactive
has_profile = true
dofile("/etc/profile.lua")
end
if tty.getCursor() > 1 then
io.write("\n")
end
io.write(sh.expand(os.getenv("PS1") or "$ "))
end
tty.window.cursor = input_handler
local command = io.stdin:readLine(false)
tty.window.cursor = nil
if command then
command = text.trim(command)
if command == "exit" then
return
elseif command ~= "" then
--luacheck: globals _ENV
local result, reason = sh.execute(_ENV, command)
if not result and reason then
io.stderr:write(tostring(reason), "\n")
end
end
elseif command == nil then -- false only means the input was interrupted
return -- eof
end
end
else
-- execute command.
return sh.execute(...)
end

View File

@@ -0,0 +1,5 @@
local computer = require("computer")
local tty = require("tty")
tty.clear()
computer.shutdown()

60
data/OpenOS/bin/sleep.lua Normal file
View File

@@ -0,0 +1,60 @@
local shell = require("shell")
local args, options = shell.parse(...)
if options.help then
print([[Usage: sleep NUMBER[SUFFIX]...
Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default),
'm' for minutes, 'h' for hours or 'd' for days. Unlike most implementations
that require NUMBER be an integer, here NUMBER may be an arbitrary floating
point number. Given two or more arguments, pause for the amount of time
specified by the sum of their values.]])
end
local function help(bad_arg)
print("sleep: invalid option -- '"..tostring(bad_arg).."'")
print("Try 'sleep --help' for more information.")
end
local function time_type_multiplier(time_type)
if not time_type or #time_type == 0 or time_type == 's' then
return 1
elseif time_type == 'm' then
return 60
elseif time_type == 'h' then
return 60 * 60
elseif time_type == 'd' then
return 60 * 60 * 24
end
-- weird error, my bad
assert(false,'bug parsing parameter:'..tostring(time_type))
end
options.help = nil
if next(options) then
help(next(options))
return 1
end
local total_time = 0
for _,v in ipairs(args) do
local interval, time_type = v:match('^([%d%.]+)([smhd]?)$')
interval = tonumber(interval)
if not interval or interval < 0 then
help(v)
return 1
end
total_time = total_time + time_type_multiplier(time_type) * interval
end
local ins = io.stdin.stream
local pull = ins.pull
local start = 1
if not pull then
pull = require("event").pull
start = 2
end
pull(select(start, ins, total_time, "interrupted"))

View File

@@ -0,0 +1,36 @@
local shell = require("shell")
local process = require("process")
local args, options = shell.parse(...)
if #args ~= 1 then
io.stderr:write("specify a single file to source\n");
return 1
end
local file, open_reason = io.open(args[1], "r")
if not file then
if not options.q then
io.stderr:write(string.format("could not source %s because: %s\n", args[1], open_reason));
end
return 1
end
local lines = file:lines()
while true do
local line = lines()
if not line then
break
end
local current_data = process.info().data
local source_proc = process.load((assert(os.getenv("SHELL"), "no $SHELL set")))
local source_data = process.list[source_proc].data
source_data.aliases = current_data.aliases -- hacks to propogate sub shell env changes
source_data.vars = current_data.vars
process.internal.continue(source_proc, _ENV, line)
end
file:close()

18
data/OpenOS/bin/time.lua Normal file
View File

@@ -0,0 +1,18 @@
local computer = require('computer')
local sh = require('sh')
local real_before, cpu_before = computer.uptime(), os.clock()
local cmd_result = 0
if ... then
sh.execute(nil, ...)
cmd_result = sh.getLastExitCode()
end
local real_after, cpu_after = computer.uptime(), os.clock()
local real_diff = real_after - real_before
local cpu_diff = cpu_after - cpu_before
print(string.format('real%5dm%.3fs', math.floor(real_diff/60), real_diff%60))
print(string.format('cpu %5dm%.3fs', math.floor(cpu_diff/60), cpu_diff%60))
return cmd_result

54
data/OpenOS/bin/touch.lua Normal file
View File

@@ -0,0 +1,54 @@
--[[Lua implementation of the UN*X touch command--]]
local shell = require("shell")
local fs = require("filesystem")
local args, options = shell.parse(...)
local function usage()
print(
[[Usage: touch [OPTION]... FILE...
Update the modification times of each FILE to the current time.
A FILE argument that does not exist is created empty, unless -c is supplied.
-c, --no-create do not create any files
--help display this help and exit]])
end
if options.help then
usage()
return 0
elseif #args == 0 then
io.stderr:write("touch: missing operand\n")
return 1
end
options.c = options.c or options["no-create"]
local errors = 0
for _,arg in ipairs(args) do
local path = shell.resolve(arg)
if fs.isDirectory(path) then
io.stderr:write(string.format("`%s' ignored: directories not supported\n", arg))
else
local real, reason = fs.realPath(path)
if real then
local file
if fs.exists(real) or not options.c then
file = io.open(real, "a")
end
if not file then
real = options.c
reason = "permission denied"
else
file:close()
end
end
if not real then
io.stderr:write(string.format("touch: cannot touch `%s': %s\n", arg, reason))
errors = 1
end
end
end
return errors

331
data/OpenOS/bin/tree.lua Normal file
View File

@@ -0,0 +1,331 @@
local computer = require("computer")
local shell = require("shell")
local fs = require("filesystem")
local tx = require("transforms")
local text = require("text")
local args, opts = shell.parse(...)
local function die(...)
io.stderr:write(...)
os.exit(1)
end
do -- handle cli
if opts.help then
print([[Usage: tree [OPTION]... [FILE]...
-a, --all do not ignore entries starting with .
--full-time with -l, print time in full iso format
-h, --human-readable with -l, print human readable sizes
--si likewise, but use powers of 1000 not 1024
--level=LEVEL descend only LEVEL directories deep
--color=WHEN WHEN can be
auto - colorize output only if writing to a tty,
always - always colorize output,
never - never colorize output; (default: auto)
-l use a long listing format
-f print the full path prefix for each file
-i do not print indentation lines
-p append "/" indicator to directories
-Q, --quote quote filenames with double quotes
-r, --reverse reverse order while sorting
-S sort by file size
-t sort by modification type, newest first
-X sort alphabetically by entry extension
-C do not count files and directories
-R count root directories like other files
--help print this help and exit]])
return 0
end
if #args == 0 then
table.insert(args, ".")
end
opts.level = tonumber(opts.level) or math.huge
if opts.level < 1 then
die("Invalid level, must be greater than 0")
end
opts.color = opts.color or "auto"
if opts.color == "auto" then
opts.color = io.stdout.tty and "always" or "never"
end
if opts.color ~= "always" and opts.color ~= "never" then
die("Invalid value for --color=WHEN option; WHEN should be auto, always or never")
end
end
local lastYield = computer.uptime()
local function yieldopt()
if computer.uptime() - lastYield > 2 then
lastYield = computer.uptime()
os.sleep(0)
end
end
local function peekable(iterator, state, var1)
local nextItem = {iterator(state, var1)}
return setmetatable({
peek = function()
return table.unpack(nextItem)
end
}, {
__call = coroutine.wrap(function()
while true do
local item = nextItem
nextItem = {iterator(state, nextItem[1])}
coroutine.yield(table.unpack(item))
if nextItem[1] == nil then break end
end
end)
})
end
local function filter(entry)
return opts.a or entry:sub(1, 1) ~= "."
end
local function stat(path)
local st = {}
st.path = path
st.name = fs.name(path) or "/"
st.sortName = st.name:gsub("^%.","")
st.time = fs.lastModified(path)
st.isLink = fs.isLink(path)
st.isDirectory = fs.isDirectory(path)
st.size = st.isLink and 0 or fs.size(path)
st.extension = st.name:match("(%.[^.]+)$") or ""
st.fs = fs.get(path)
return st
end
local colorize
if opts.color == "always" then
-- from /lib/core/full_ls.lua
local colors = tx.foreach(text.split(os.getenv("LS_COLORS") or "", {":"}, true), function(e)
local parts = text.split(e, {"="}, true)
return parts[2], parts[1]
end)
function colorize(stat)
return stat.isLink and colors.ln or
stat.isDirectory and colors.di or
colors["*" .. stat.extension] or
colors.fi
end
end
local function list(path)
return coroutine.wrap(function()
local l = {}
for entry in fs.list(path) do
if filter(entry) then
table.insert(l, stat(fs.concat(path, entry)))
end
end
if opts.S then
table.sort(l, function(a, b)
return a.size < b.size
end)
elseif opts.t then
table.sort(l, function(a, b)
return a.time < b.time
end)
elseif opts.X then
table.sort(l, function(a, b)
return a.extension < b.extension
end)
else
table.sort(l, function(a, b)
return a.sortName < b.sortName
end)
end
for i = opts.r and #l or 1, opts.r and 1 or #l, opts.r and -1 or 1 do
coroutine.yield(l[i])
end
end)
end
local function digRoot(rootPath)
coroutine.yield(stat(rootPath), {})
if not fs.isDirectory(rootPath) then
return
end
local iterStack = {peekable(list(rootPath))}
local pathStack = {rootPath}
local levelStack = {not not iterStack[#iterStack]:peek()}
repeat
local entry = iterStack[#iterStack]()
if entry then
levelStack[#levelStack] = not not iterStack[#iterStack]:peek()
local path = fs.concat(fs.concat(table.unpack(pathStack)), entry.name)
coroutine.yield(entry, levelStack)
if entry.isDirectory and opts.level > #levelStack then
table.insert(iterStack, peekable(list(path)))
table.insert(pathStack, entry.name)
table.insert(levelStack, not not iterStack[#iterStack]:peek())
end
else
table.remove(iterStack)
table.remove(pathStack)
table.remove(levelStack)
end
until #iterStack == 0
end
local function dig(roots)
return coroutine.wrap(function()
for _, root in ipairs(roots) do
digRoot(root)
end
end)
end
local function nod(n) -- from /lib/core/full_ls.lua
return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0"
end
local function formatFSize(size) -- from /lib/core/full_ls.lua
if not opts.h and not opts["human-readable"] and not opts.si then
return tostring(size)
end
local sizes = {"", "K", "M", "G"}
local unit = 1
local power = opts.si and 1000 or 1024
while size > power and unit < #sizes do
unit = unit + 1
size = size / power
end
return nod(math.floor(size*10)/10)..sizes[unit]
end
local function pad(txt) -- from /lib/core/full_ls.lua
txt = tostring(txt)
return #txt >= 2 and txt or "0" .. txt
end
local function formatTime(epochms) -- from /lib/core/full_ls.lua
local month_names = {"January","February","March","April","May","June",
"July","August","September","October","November","December"}
if epochms == 0 then return "" end
local d = os.date("*t", epochms)
local day, hour, min, sec = nod(d.day), pad(nod(d.hour)), pad(nod(d.min)), pad(nod(d.sec))
if opts["full-time"] then
return string.format("%s-%s-%s %s:%s:%s ", d.year, pad(nod(d.month)), pad(day), hour, min, sec)
else
return string.format("%s %2s %2s:%2s ", month_names[d.month]:sub(1,3), day, hour, pad(min))
end
end
local function writeEntry(entry, levelStack)
for i, hasNext in ipairs(levelStack) do
if opts.i then break end
if i == #levelStack then
if hasNext then
io.write("├── ")
else
io.write("└── ")
end
else
if hasNext then
io.write("│   ")
else
io.write(" ")
end
end
end
if opts.l then
io.write("[")
io.write(entry.isDirectory and "d" or entry.isLink and "l" or "f", "-")
io.write("r", entry.fs.isReadOnly() and "-" or "w", " ")
io.write(formatFSize(entry.size), " ")
io.write(formatTime(entry.time))
io.write("] ")
end
if opts.Q then io.write('"') end
if opts.color == "always" then
io.write("\27[" .. colorize(entry) .. "m")
end
if opts.f then
io.write(entry.path)
else
io.write(entry.name)
end
if opts.color == "always" then
io.write("\27[0m")
end
if opts.p and entry.isDirectory then
io.write("/")
end
if opts.Q then io.write('"') end
io.write("\n")
end
local function writeCount(dirs, files)
io.write("\n")
io.write(dirs, " director", dirs == 1 and "y" or "ies")
io.write(", ")
io.write(files, " file", files == 1 and "" or "s")
io.write("\n")
end
local dirs, files = 0, 0
local roots = {}
for _, arg in ipairs(args) do
local path = shell.resolve(arg)
local real, reason = fs.realPath(path)
if not real then
die("cannot access ", path, ": ", reason or "unknown error")
elseif not fs.exists(path) then
die("cannot access ", path, ":", "No such file or directory")
else
table.insert(roots, real)
end
end
for entry, levelStack in dig(roots) do
if opts.R or #levelStack > 0 then
if entry.isDirectory then
dirs = dirs + 1
else
files = files + 1
end
end
writeEntry(entry, levelStack)
yieldopt()
end
if not opts.C then
writeCount(dirs, files)
end

View File

@@ -0,0 +1,37 @@
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.\n")
return 1
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\n")
return 1
end
end
end
if not proxy then
io.stderr:write(tostring(reason)..'\n')
return 1
end
if not fs.umount(proxy) then
io.stderr:write("nothing to unmount here\n")
return 1
end

View File

@@ -0,0 +1,19 @@
local shell = require("shell")
local args = shell.parse(...)
if #args < 1 then
io.write("Usage: unalias <name>...\n")
return 2
end
local e = 0
for _,arg in ipairs(args) do
local result = shell.getAlias(arg)
if not result then
io.stderr:write(string.format("unalias: %s: not found\n", arg))
e = 1
else
shell.setAlias(arg, nil)
end
end
return e

View File

@@ -0,0 +1,9 @@
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

View File

@@ -0,0 +1,13 @@
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\n", hours, minutes, seconds))

View File

@@ -0,0 +1,14 @@
local computer = require("computer")
local shell = require("shell")
local args = shell.parse(...)
if #args ~= 1 then
io.write("Usage: useradd <name>\n")
return 1
end
local result, reason = computer.addUser(args[1])
if not result then
io.stderr:write(reason..'\n')
return 1
end

View File

@@ -0,0 +1,13 @@
local computer = require("computer")
local shell = require("shell")
local args = shell.parse(...)
if #args ~= 1 then
io.write("Usage: userdel <name>\n")
return 1
end
if not computer.removeUser(args[1]) then
io.stderr:write("no such user\n")
return 1
end

115
data/OpenOS/bin/wget.lua Normal file
View File

@@ -0,0 +1,115 @@
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)
local preexisted
if fs.exists(filename) then
preexisted = true
if not options.f 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, "a")
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
f:close()
f = nil
if not options.q then
io.write("Downloading... ")
end
local result, response = pcall(internet.request, url, nil, {["user-agent"]="Wget/OpenComputers"})
if result then
local result, reason = pcall(function()
for chunk in response do
if not f then
f, reason = io.open(filename, "wb")
assert(f, "failed opening file for writing: " .. tostring(reason))
end
f:write(chunk)
end
end)
if not result then
if not options.q then
io.stderr:write("failed.\n")
end
if f then
f:close()
if not preexisted then
fs.remove(filename)
end
end
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
if f then
f:close()
end
if not options.q then
io.write("Saved data to " .. filename .. "\n")
end
else
if not options.q then
io.write("failed.\n")
end
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

25
data/OpenOS/bin/which.lua Normal file
View File

@@ -0,0 +1,25 @@
local shell = require("shell")
local args = shell.parse(...)
if #args == 0 then
io.write("Usage: which <program>\n")
return 255
end
for i = 1, #args do
local result, reason = shell.resolve(args[i], "lua")
if not result then
result = shell.getAlias(args[i])
if result then
result = args[i] .. ": aliased to " .. result
end
end
if result then
print(result)
else
io.stderr:write(args[i] .. ": " .. reason .. "\n")
return 1
end
end

32
data/OpenOS/bin/yes.lua Normal file
View File

@@ -0,0 +1,32 @@
--[[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-3\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
local msg = #args == 0 and 'y' or table.concat(args, ' ')
msg = msg .. '\n'
while io.write(msg) do
if io.stdout.tty then
os.sleep(0)
end
end
return 0