testing version of LuaBIOS and OpenOS
people were having issues getting them to work so now we promote consistency
This commit is contained in:
2
data/OpenOS/bin/address.lua
Normal file
2
data/OpenOS/bin/address.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
local computer = require("computer")
|
||||
io.write(computer.address(),"\n")
|
||||
62
data/OpenOS/bin/alias.lua
Normal file
62
data/OpenOS/bin/alias.lua
Normal 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
39
data/OpenOS/bin/cat.lua
Normal 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
51
data/OpenOS/bin/cd.lua
Normal 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
|
||||
2
data/OpenOS/bin/clear.lua
Normal file
2
data/OpenOS/bin/clear.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
local tty = require("tty")
|
||||
tty.clear()
|
||||
52
data/OpenOS/bin/components.lua
Normal file
52
data/OpenOS/bin/components.lua
Normal 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
36
data/OpenOS/bin/cp.lua
Normal 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
1
data/OpenOS/bin/date.lua
Normal file
@@ -0,0 +1 @@
|
||||
io.write(os.date("%F %T").."\n")
|
||||
76
data/OpenOS/bin/df.lua
Normal file
76
data/OpenOS/bin/df.lua
Normal 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
38
data/OpenOS/bin/dmesg.lua
Normal 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
128
data/OpenOS/bin/du.lua
Normal 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
22
data/OpenOS/bin/echo.lua
Normal 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
723
data/OpenOS/bin/edit.lua
Normal 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
132
data/OpenOS/bin/find.lua
Normal 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
88
data/OpenOS/bin/flash.lua
Normal 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
8
data/OpenOS/bin/free.lua
Normal 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
323
data/OpenOS/bin/grep.lua
Normal 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
131
data/OpenOS/bin/head.lua
Normal 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
|
||||
30
data/OpenOS/bin/hostname.lua
Normal file
30
data/OpenOS/bin/hostname.lua
Normal 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
|
||||
53
data/OpenOS/bin/install.lua
Normal file
53
data/OpenOS/bin/install.lua
Normal 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
49
data/OpenOS/bin/label.lua
Normal 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
149
data/OpenOS/bin/less.lua
Normal 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
33
data/OpenOS/bin/list.lua
Normal 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
34
data/OpenOS/bin/ln.lua
Normal 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
18
data/OpenOS/bin/ls.lua
Normal 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
42
data/OpenOS/bin/lshw.lua
Normal 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
27
data/OpenOS/bin/lua.lua
Normal 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
20
data/OpenOS/bin/man.lua
Normal 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
27
data/OpenOS/bin/mkdir.lua
Normal 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
70
data/OpenOS/bin/mktmp.lua
Normal 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
96
data/OpenOS/bin/mount.lua
Normal 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
33
data/OpenOS/bin/mv.lua
Normal 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)
|
||||
153
data/OpenOS/bin/pastebin.lua
Normal file
153
data/OpenOS/bin/pastebin.lua
Normal 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).")
|
||||
28
data/OpenOS/bin/primary.lua
Normal file
28
data/OpenOS/bin/primary.lua
Normal 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
149
data/OpenOS/bin/ps.lua
Normal 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
14
data/OpenOS/bin/pwd.lua
Normal 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
150
data/OpenOS/bin/rc.lua
Normal 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
|
||||
4
data/OpenOS/bin/reboot.lua
Normal file
4
data/OpenOS/bin/reboot.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
local computer = require("computer")
|
||||
|
||||
io.write("Rebooting...")
|
||||
computer.shutdown(true)
|
||||
103
data/OpenOS/bin/redstone.lua
Normal file
103
data/OpenOS/bin/redstone.lua
Normal 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
|
||||
32
data/OpenOS/bin/resolution.lua
Normal file
32
data/OpenOS/bin/resolution.lua
Normal 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
160
data/OpenOS/bin/rm.lua
Normal 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
104
data/OpenOS/bin/rmdir.lua
Normal 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
23
data/OpenOS/bin/set.lua
Normal 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
50
data/OpenOS/bin/sh.lua
Normal 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
|
||||
5
data/OpenOS/bin/shutdown.lua
Normal file
5
data/OpenOS/bin/shutdown.lua
Normal 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
60
data/OpenOS/bin/sleep.lua
Normal 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"))
|
||||
36
data/OpenOS/bin/source.lua
Normal file
36
data/OpenOS/bin/source.lua
Normal 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
18
data/OpenOS/bin/time.lua
Normal 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
54
data/OpenOS/bin/touch.lua
Normal 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
331
data/OpenOS/bin/tree.lua
Normal 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
|
||||
|
||||
37
data/OpenOS/bin/umount.lua
Normal file
37
data/OpenOS/bin/umount.lua
Normal 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
|
||||
19
data/OpenOS/bin/unalias.lua
Normal file
19
data/OpenOS/bin/unalias.lua
Normal 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
|
||||
9
data/OpenOS/bin/unset.lua
Normal file
9
data/OpenOS/bin/unset.lua
Normal 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
|
||||
13
data/OpenOS/bin/uptime.lua
Normal file
13
data/OpenOS/bin/uptime.lua
Normal 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))
|
||||
14
data/OpenOS/bin/useradd.lua
Normal file
14
data/OpenOS/bin/useradd.lua
Normal 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
|
||||
13
data/OpenOS/bin/userdel.lua
Normal file
13
data/OpenOS/bin/userdel.lua
Normal 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
115
data/OpenOS/bin/wget.lua
Normal 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
25
data/OpenOS/bin/which.lua
Normal 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
32
data/OpenOS/bin/yes.lua
Normal 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
|
||||
Reference in New Issue
Block a user