mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 10:36:44 +00:00
CapyOS 0.1.0 update
This commit is contained in:
parent
990b30b7bb
commit
2bf4b17577
31 changed files with 1227 additions and 124 deletions
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
|
@ -0,0 +1,7 @@
|
|||
alias ll "ls -al"
|
||||
alias la "ls -a"
|
||||
alias rmdir "rm -r"
|
||||
alias reboot "shutdown -r"
|
||||
|
||||
# Comment or remove the line below to disable the MOTD
|
||||
motd
|
|
@ -1,4 +1,4 @@
|
|||
local version = "0.0.3"
|
||||
local version = "0.1.0"
|
||||
local systemDirectory = "/sys"
|
||||
|
||||
print("Starting CapyOS")
|
||||
|
|
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
|
@ -0,0 +1,28 @@
|
|||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
|
||||
if options.l or options.list then
|
||||
for alias, value in pairs(shell.aliases) do
|
||||
print(string.format("%s = \"%s\"", alias, value))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local alias = args[1]
|
||||
|
||||
if not alias or options.h or options.help then
|
||||
print("Usage: alias [option...] <alias> [command]")
|
||||
print("Options:")
|
||||
print(" -l --list: List aliases")
|
||||
return false
|
||||
end
|
||||
|
||||
local command = table.pack(select(2, ...))
|
||||
if #command == 0 then
|
||||
shell.aliases[alias] = nil
|
||||
return
|
||||
end
|
||||
|
||||
shell.aliases[alias] = table.concat(command, " ")
|
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
|
@ -0,0 +1,4 @@
|
|||
local scheduler = require("scheduler")
|
||||
scheduler.spawn(function()
|
||||
shell.run(arg.string)
|
||||
end)
|
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
|
@ -0,0 +1,9 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local args = {...}
|
||||
|
||||
local path = shell.resolve(args[1])
|
||||
|
||||
local f<close> = fs.open(path, "r")
|
||||
print(f:read("a"))
|
||||
f:close()
|
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
|
@ -0,0 +1,4 @@
|
|||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
print(table.concat(args, " "))
|
|
@ -11,7 +11,13 @@ local function slowPrint(text, delay)
|
|||
print()
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
local text = "Hello, World!"
|
||||
if #args > 0 then
|
||||
text = table.concat(args, " ")
|
||||
end
|
||||
|
||||
local color = colors[math.random(1, #colors)]
|
||||
|
||||
term.setForeground(color)
|
||||
slowPrint("Hello, World!", 0.05)
|
||||
slowPrint(text, 0.05)
|
||||
|
|
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
local fs = require("fs")
|
||||
local helpPath = "/sys/share/help"
|
||||
|
||||
local topicName = arg[1] or "index"
|
||||
|
||||
if not fs.exists(fs.combine(helpPath, topicName)) then
|
||||
print(string.format("Topic \"%s\" not found.", topicName))
|
||||
return false
|
||||
end
|
||||
|
||||
shell.run("/sys/bin/less.lua", fs.combine(helpPath, topicName))
|
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
|
@ -0,0 +1,78 @@
|
|||
local term = require("term")
|
||||
local keys = require("keys")
|
||||
local event = require("event")
|
||||
local fs = require("fs")
|
||||
local timer = require("timer")
|
||||
local colors = require("colors")
|
||||
|
||||
local filename = shell.resolve(arg[1])
|
||||
|
||||
local f<close> = fs.open(filename, "r")
|
||||
local lines = {}
|
||||
local lineMax = 0
|
||||
for line in f:lines() do
|
||||
table.insert(lines, line)
|
||||
lineMax = math.max(lineMax, #line)
|
||||
end
|
||||
f:close()
|
||||
|
||||
|
||||
local width, height = term.getSize()
|
||||
height = height - 1
|
||||
local posx, posy = 0, 0
|
||||
|
||||
local function redraw()
|
||||
term.clear()
|
||||
term.setForeground(colors.white)
|
||||
for i = 1, height do
|
||||
if i + posy > #lines then
|
||||
break
|
||||
end
|
||||
term.setPos(-posx + 1, i)
|
||||
term.write(lines[i + posy])
|
||||
end
|
||||
|
||||
term.setForeground(colors.yellow)
|
||||
term.setPos(1, height + 1)
|
||||
term.write("Use arrow keys to move or press Q to exit.")
|
||||
end
|
||||
|
||||
while true do
|
||||
redraw()
|
||||
|
||||
local _, key = event.pull("key_down")
|
||||
|
||||
if key == keys.enter or key == keys.down then
|
||||
posy = posy + 1
|
||||
elseif key == keys.up then
|
||||
posy = posy - 1
|
||||
elseif key == keys.right then
|
||||
posx = posx + 1
|
||||
elseif key == keys.left then
|
||||
posx = posx - 1
|
||||
elseif key == keys.q or key == keys.escape then
|
||||
-- Clear event queue
|
||||
timer.sleep(0)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
|
||||
if posy > #lines - height then
|
||||
posy = #lines - height
|
||||
end
|
||||
|
||||
if posy < 0 then
|
||||
posy = 0
|
||||
end
|
||||
|
||||
if posx + width > lineMax + 1 then
|
||||
posx = lineMax - width + 1
|
||||
end
|
||||
|
||||
if posx < 0 then
|
||||
posx = 0
|
||||
end
|
||||
end
|
|
@ -1,25 +1,88 @@
|
|||
local args = { ... }
|
||||
local fs = require("fs")
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local dir = shell.getDir()
|
||||
local argparser = require("argparser")
|
||||
|
||||
if args[1] then
|
||||
dir = shell.resolve(args[1])
|
||||
local theme = {
|
||||
directory = colors.lightBlue,
|
||||
file = colors.white,
|
||||
lua = colors.yellow,
|
||||
}
|
||||
|
||||
local function humanizeBytes(n)
|
||||
local prefixes = {
|
||||
[0] = "",
|
||||
"k",
|
||||
"M",
|
||||
"G",
|
||||
"T",
|
||||
}
|
||||
local block = 1024
|
||||
local prefixIndex = 0
|
||||
|
||||
while n >= block do
|
||||
n = n / 1024
|
||||
prefixIndex = prefixIndex + 1
|
||||
end
|
||||
|
||||
return string.format("%.0f%s", n, prefixes[prefixIndex])
|
||||
end
|
||||
|
||||
if not fs.isDir(dir) then
|
||||
error("No such directory: " .. dir, 0)
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if options.h or options.help then
|
||||
print("Usage: ls [option...] [path]")
|
||||
print("List files (current directory by default)")
|
||||
print("Options:")
|
||||
print(" -a: Include hidden files")
|
||||
print(" -l: Use long listing format")
|
||||
return
|
||||
end
|
||||
local path = shell.getDir()
|
||||
|
||||
if args[1] then
|
||||
path = shell.resolve(args[1])
|
||||
end
|
||||
|
||||
if not fs.isDir(path) then
|
||||
error("No such directory: " .. path, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local files = fs.list(dir)
|
||||
for k, v in ipairs(files) do
|
||||
if fs.isDir(fs.combine(dir, v)) then
|
||||
term.setForeground(colors.lightBlue)
|
||||
print(v .. "/")
|
||||
else
|
||||
term.setForeground(colors.white)
|
||||
print(v)
|
||||
local entries = fs.list(path)
|
||||
|
||||
if options.l then
|
||||
print(string.format("total %d", #entries))
|
||||
end
|
||||
local printed = 0
|
||||
for i, entry in ipairs(entries) do
|
||||
if entry:sub(1, 1) ~= "." or options.a then
|
||||
printed = printed + 1
|
||||
local attributes = fs.attributes(fs.combine(path, entry))
|
||||
local size = humanizeBytes(attributes.size)
|
||||
local date = os.date("%x %H:%m", attributes.modified // 1000)
|
||||
|
||||
local entryType
|
||||
if attributes.isDirectory then
|
||||
entryType = "directory"
|
||||
else
|
||||
entryType = "file"
|
||||
if string.match(entry, "%.lua$") then
|
||||
entryType = "lua"
|
||||
end
|
||||
end
|
||||
|
||||
if options.l then
|
||||
term.setForeground(colors.white)
|
||||
term.write(string.format("%s %5s %s ", attributes.isDirectory and "d" or "-", size, date))
|
||||
end
|
||||
term.setForeground(theme[entryType])
|
||||
io.write(entry)
|
||||
|
||||
io.write(options.l and "\n" or "\t")
|
||||
end
|
||||
end
|
||||
|
||||
if not options.l and printed > 0 then
|
||||
print()
|
||||
end
|
|
@ -1,49 +1,16 @@
|
|||
local term = require("term")
|
||||
local io = require("io")
|
||||
local colors = require("colors")
|
||||
local colours = colors
|
||||
local argparser = require("argparser")
|
||||
local tableutils = require("tableutils")
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
print("This is an interactive Lua prompt.")
|
||||
print("To run a lua program, just type its name.")
|
||||
return
|
||||
end
|
||||
|
||||
--local pretty = require "cc.pretty"
|
||||
|
||||
local bRunning = true
|
||||
local tCommandHistory = {}
|
||||
local tEnv = {
|
||||
["exit"] = setmetatable({}, {
|
||||
__tostring = function() return "Call exit() to exit." end,
|
||||
__call = function() bRunning = false end,
|
||||
}),
|
||||
}
|
||||
setmetatable(tEnv, { __index = _ENV })
|
||||
|
||||
for k, v in pairs(package.loaded) do
|
||||
tEnv[k] = v
|
||||
end
|
||||
|
||||
term.setForeground(colours.yellow)
|
||||
print(_VERSION .. " interactive prompt")
|
||||
print("Call exit() to exit.")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
while bRunning do
|
||||
term.setForeground(colours.yellow)
|
||||
io.write("> ")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||
table.insert(tCommandHistory, s)
|
||||
end
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
local function evaluate(str, env, chunkname)
|
||||
chunkname = chunkname or "=lua"
|
||||
local nForcePrint = 0
|
||||
local func, e = load(s, "=lua", "t", tEnv)
|
||||
local func2 = load("return " .. s, "=lua", "t", tEnv)
|
||||
local func, e = load(str, chunkname, "t", env)
|
||||
local func2 = load("return " .. str, chunkname, "t", env)
|
||||
if not func then
|
||||
if func2 then
|
||||
func = func2
|
||||
|
@ -62,14 +29,70 @@ while bRunning do
|
|||
local n = 1
|
||||
while n < tResults.n or n <= nForcePrint do
|
||||
local value = tResults[n + 1]
|
||||
print(tostring(value))
|
||||
print(tableutils.pretty(value))
|
||||
n = n + 1
|
||||
end
|
||||
else
|
||||
io.stderr.print(tResults[2])
|
||||
return false
|
||||
end
|
||||
else
|
||||
io.stderr.print(e)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function createEnvironment()
|
||||
return setmetatable({}, { __index = _ENV })
|
||||
end
|
||||
|
||||
local function loadPackages(env)
|
||||
for k, v in pairs(package.loaded) do
|
||||
env[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if options.e then
|
||||
local env = createEnvironment()
|
||||
loadPackages(env)
|
||||
return evaluate(table.concat(args, " "), env)
|
||||
end
|
||||
|
||||
if #args > 0 then
|
||||
print("This is an interactive Lua prompt.")
|
||||
print("To run a lua program, just type its name.")
|
||||
return
|
||||
end
|
||||
|
||||
--local pretty = require "cc.pretty"
|
||||
|
||||
local bRunning = true
|
||||
local tCommandHistory = {}
|
||||
|
||||
|
||||
local tEnv = createEnvironment()
|
||||
tEnv.exit = setmetatable({}, {
|
||||
__tostring = function() return "Call exit() to exit." end,
|
||||
__call = function() bRunning = false end,
|
||||
})
|
||||
loadPackages(tEnv)
|
||||
|
||||
term.setForeground(colors.yellow)
|
||||
print(_VERSION .. " interactive prompt")
|
||||
print("Call exit() to exit.")
|
||||
term.setForeground(colors.white)
|
||||
|
||||
while bRunning do
|
||||
term.setForeground(colors.yellow)
|
||||
io.write("> ")
|
||||
term.setForeground(colors.white)
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||
table.insert(tCommandHistory, s)
|
||||
end
|
||||
|
||||
evaluate(s, tEnv)
|
||||
|
||||
end
|
||||
|
|
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
|
@ -0,0 +1,20 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local date = os.date("*t")
|
||||
|
||||
if date.month == 4 and date.day == 28 then
|
||||
print("Ed Balls")
|
||||
return
|
||||
end
|
||||
|
||||
local motdList = {}
|
||||
|
||||
local f<close> = fs.open("/sys/share/motd.txt", "r")
|
||||
for line in f:lines() do
|
||||
table.insert(motdList, line)
|
||||
end
|
||||
f:close()
|
||||
|
||||
local motdIndex = math.random(1, #motdList)
|
||||
|
||||
print(motdList[motdIndex])
|
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
local fs = require("fs")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if not args[1] or not args[2] or options.h or options.help then
|
||||
print("Usage: mv [option...] <source> <target>")
|
||||
print("Options:")
|
||||
print(" -h --help: Display help")
|
||||
return
|
||||
end
|
||||
|
||||
local source = shell.resolve(args[1])
|
||||
local destination = shell.resolve(args[2])
|
||||
|
||||
fs.move(source, destination)
|
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
|
@ -0,0 +1,10 @@
|
|||
local fs = require("fs")
|
||||
local programs = fs.list("/sys/bin", function(name, attr)
|
||||
return not attr.isDirectory
|
||||
end)
|
||||
|
||||
for i, v in ipairs(programs) do
|
||||
programs[i] = string.gsub(v, "%.lua$", "")
|
||||
end
|
||||
|
||||
print(table.concat(programs, " "))
|
|
@ -1,8 +0,0 @@
|
|||
local timer = require("timer")
|
||||
local machine = require("machine")
|
||||
|
||||
print("Goodbye!")
|
||||
|
||||
timer.sleep(1)
|
||||
|
||||
machine.reboot()
|
|
@ -1,10 +1,16 @@
|
|||
local fs = require("fs")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args = { ... }
|
||||
if #args == 0 then
|
||||
print("Usage: rm <file>")
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if not args[1] or options.h or options.help then
|
||||
print("Usage: rm [option...] <path>")
|
||||
print("Options:")
|
||||
print(" -r --recursive: Delete non-empty directories")
|
||||
print(" -h --help: Display help")
|
||||
return
|
||||
end
|
||||
|
||||
local file = shell.resolve(args[1])
|
||||
fs.delete(file, true)
|
||||
|
||||
fs.delete(file, options.recursive or options.r)
|
||||
|
|
|
@ -2,42 +2,31 @@ local term = require("term")
|
|||
local colors = require("colors")
|
||||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
local argparser = require("argparser")
|
||||
local scheduler = require("scheduler")
|
||||
|
||||
local exit = false
|
||||
local parentShell = shell
|
||||
local isStartupShell = parentShell == nil
|
||||
local shell = {}
|
||||
|
||||
shell.path = "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||
shell.homePath = "/home"
|
||||
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||
shell.homePath = parentShell and parentShell.home or "/home"
|
||||
shell.aliases = parentShell and parentShell.aliases or {}
|
||||
|
||||
local currentDir = shell.homePath
|
||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
||||
|
||||
local function buildEnvironment(path, args)
|
||||
local function buildEnvironment(path, args, argf)
|
||||
local arg = { table.unpack(args, 2) }
|
||||
arg[0] = path
|
||||
arg.string = argf
|
||||
|
||||
return setmetatable({
|
||||
shell = shell,
|
||||
arg = arg
|
||||
arg = arg,
|
||||
}, { __index = _G })
|
||||
end
|
||||
|
||||
local function tokenise(...)
|
||||
local sLine = table.concat({ ... }, " ")
|
||||
local tWords = {}
|
||||
local bQuoted = false
|
||||
for match in string.gmatch(sLine .. "\"", "(.-)\"") do
|
||||
if bQuoted then
|
||||
table.insert(tWords, match)
|
||||
else
|
||||
for m in string.gmatch(match, "[^ \t]+") do
|
||||
table.insert(tWords, m)
|
||||
end
|
||||
end
|
||||
bQuoted = not bQuoted
|
||||
end
|
||||
return tWords
|
||||
end
|
||||
|
||||
function shell.getDir()
|
||||
return currentDir
|
||||
end
|
||||
|
@ -72,16 +61,24 @@ function shell.resolveProgram(path)
|
|||
end
|
||||
|
||||
function shell.run(...)
|
||||
local args = tokenise(...)
|
||||
local args = argparser.tokenize(...)
|
||||
local argf = table.concat({...}, " ")
|
||||
local command = args[1]
|
||||
|
||||
argf = argf:sub(#command + 2)
|
||||
|
||||
local path = shell.resolveProgram(command)
|
||||
|
||||
if not path then
|
||||
io.stderr.print("Command not found: " .. command)
|
||||
return false
|
||||
if shell.aliases[command] then
|
||||
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
||||
else
|
||||
io.stderr.print("Command not found: " .. command)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local env = buildEnvironment(command, args)
|
||||
local env = buildEnvironment(command, args, argf)
|
||||
|
||||
local func, err = loadfile(path, "t", env)
|
||||
|
||||
|
@ -90,7 +87,15 @@ function shell.run(...)
|
|||
return false
|
||||
end
|
||||
|
||||
local ok, err = pcall(func, table.unpack(args, 2))
|
||||
local ok, err
|
||||
local function run()
|
||||
ok, err = pcall(func, table.unpack(args, 2))
|
||||
|
||||
end
|
||||
|
||||
local programTask = scheduler.spawn(run)
|
||||
coroutine.yield("scheduler_task_end")
|
||||
|
||||
if not ok then
|
||||
io.stderr.print(err)
|
||||
return false
|
||||
|
@ -107,6 +112,21 @@ if not fs.exists(shell.homePath) then
|
|||
fs.makeDir(shell.homePath)
|
||||
end
|
||||
|
||||
term.setForeground(colors.white)
|
||||
term.setBackground(colors.black)
|
||||
|
||||
if isStartupShell then
|
||||
if fs.exists(fs.combine(shell.homePath, ".shrc")) then
|
||||
local f <close> = fs.open(fs.combine(shell.homePath, ".shrc"), "r")
|
||||
for line in f:lines() do
|
||||
if line:match("%S") and not line:match("^%s-#") then
|
||||
shell.run(line)
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
local history = {}
|
||||
local lastExecSuccess = true
|
||||
while not exit do
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
local timer = require("timer")
|
||||
local machine = require("machine")
|
||||
local scheduler = require("scheduler")
|
||||
local argparser = require("argparser")
|
||||
|
||||
print("Goodbye!")
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
timer.sleep(1)
|
||||
if options.h or options.help then
|
||||
print("Usage: shutdown [option...]")
|
||||
print("Shutdown or restart Capy64.")
|
||||
print("Options:")
|
||||
print(" -s --shutdown: Shutdown and exit Capy64. (default)")
|
||||
print(" -r --reboot: Restart Capy64.")
|
||||
print(" -t --time: Time to wait in seconds. (\"now\" is 0 seconds, default)")
|
||||
return
|
||||
end
|
||||
|
||||
machine.shutdown()
|
||||
local time = 0
|
||||
if options.t or options.time then
|
||||
time = options.t or options.time
|
||||
end
|
||||
if time == "now" then
|
||||
time = 0
|
||||
else
|
||||
time = tonumber(time)
|
||||
if not time then
|
||||
error("Invalid time option: " .. (options.t or options.time), 0)
|
||||
end
|
||||
end
|
||||
|
||||
scheduler.ipc(1, "power", {
|
||||
reboot = options.r or options.reboot,
|
||||
time = time,
|
||||
})
|
||||
|
|
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
|
@ -0,0 +1,28 @@
|
|||
local fs = require("fs")
|
||||
local expect = require("expect").expect
|
||||
|
||||
local fsList = fs.list
|
||||
|
||||
function fs.list(path, filter)
|
||||
expect(1, path, "string")
|
||||
expect(2, filter, "nil", "function")
|
||||
|
||||
if not fs.isDir(path) then
|
||||
error("directory not found", 2)
|
||||
end
|
||||
|
||||
local list = fsList(path)
|
||||
if not filter then
|
||||
return list
|
||||
end
|
||||
|
||||
local filteredList = {}
|
||||
for i = 1, #list do
|
||||
local attributes = fs.attributes(fs.combine(path, list[i]))
|
||||
if filter(list[i], attributes) then
|
||||
table.insert(filteredList, list[i])
|
||||
end
|
||||
end
|
||||
|
||||
return filteredList
|
||||
end
|
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
|
@ -0,0 +1,47 @@
|
|||
local machine = require("machine")
|
||||
local scheduler = require("scheduler")
|
||||
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
term.setForeground(0x59c9ff)
|
||||
term.setBackground(colors.black)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
|
||||
term.write(os.version())
|
||||
term.setPos(1, 2)
|
||||
|
||||
local function spawnShell()
|
||||
return scheduler.spawn(loadfile("/sys/bin/shell.lua"))
|
||||
end
|
||||
|
||||
local function main()
|
||||
local shellTask = spawnShell()
|
||||
while true do
|
||||
local ev = {coroutine.yield()}
|
||||
if ev[1] == "ipc_message" then
|
||||
local sender = ev[2]
|
||||
local call = ev[3]
|
||||
if call == "power" then
|
||||
local options = ev[4]
|
||||
--todo: handle time and cancels
|
||||
if options.reboot then
|
||||
machine.reboot()
|
||||
else
|
||||
machine.shutdown()
|
||||
end
|
||||
end
|
||||
elseif ev[1] == "scheduler_task_end" then
|
||||
if ev[2].pid == shellTask.pid then
|
||||
if not ev[3] then
|
||||
io.stderr.print(ev[4])
|
||||
end
|
||||
shellTask = spawnShell()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scheduler.spawn(main)
|
||||
|
||||
scheduler.init()
|
|
@ -0,0 +1 @@
|
|||
require("machine").shutdown()
|
|
@ -1,15 +0,0 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local machine = require("machine")
|
||||
|
||||
term.setForeground(0x59c9ff)
|
||||
term.setBackground(colors.black)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
|
||||
term.write(os.version())
|
||||
term.setPos(1, 2)
|
||||
|
||||
dofile("/sys/bin/shell.lua")
|
||||
|
||||
machine.shutdown()
|
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
|
@ -0,0 +1,91 @@
|
|||
local argparser = {}
|
||||
|
||||
function argparser.tokenize(...)
|
||||
local input = table.concat(table.pack(...), " ")
|
||||
local tokens = {}
|
||||
|
||||
-- surely there must be a better way
|
||||
local quoted = false
|
||||
local escaped = false
|
||||
local current = ""
|
||||
for i = 1, #input do
|
||||
local char = input:sub(i, i)
|
||||
if escaped then
|
||||
escaped = false
|
||||
current = current .. char
|
||||
else
|
||||
if char == "\\" then
|
||||
escaped = true
|
||||
elseif char == "\"" then
|
||||
if quoted then
|
||||
-- close quote
|
||||
table.insert(tokens, current)
|
||||
current = ""
|
||||
end
|
||||
quoted = not quoted
|
||||
elseif char == " " and not quoted then
|
||||
if #current > 0 then
|
||||
table.insert(tokens, current)
|
||||
end
|
||||
current = ""
|
||||
else
|
||||
current = current .. char
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current ~= "" then
|
||||
table.insert(tokens, current)
|
||||
end
|
||||
|
||||
return tokens
|
||||
end
|
||||
|
||||
function argparser.parse(...)
|
||||
local tokens = { ... }
|
||||
local args = {}
|
||||
local options = {}
|
||||
local ignoreOptions = false
|
||||
|
||||
for i = 1, #tokens do
|
||||
local token = tokens[i]
|
||||
if not ignoreOptions then
|
||||
if token == "--" then
|
||||
ignoreOptions = true
|
||||
elseif token:sub(1, 2) == "--" then
|
||||
local opt, value = token:match("%-%-(.+)=(.+)")
|
||||
if not opt then
|
||||
opt = token:sub(3)
|
||||
if opt:sub(-1) == "=" then
|
||||
-- next token is value
|
||||
value = tokens[i + 1]
|
||||
opt = opt:sub(1, -2)
|
||||
options[opt] = value
|
||||
i = i + 1
|
||||
else
|
||||
options[opt] = true
|
||||
end
|
||||
else
|
||||
options[opt] = value
|
||||
end
|
||||
elseif token:sub(1, 1) == "-" then
|
||||
local opts = token:sub(2)
|
||||
for j = 1, #opts do
|
||||
options[opts:sub(j, j)] = true
|
||||
end
|
||||
else
|
||||
if #token > 0 then
|
||||
table.insert(args, token)
|
||||
end
|
||||
end
|
||||
else
|
||||
if #token > 0 then
|
||||
table.insert(args, token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return args, options
|
||||
end
|
||||
|
||||
return argparser
|
|
@ -6,7 +6,347 @@ local machine = require("machine")
|
|||
|
||||
local io = {}
|
||||
|
||||
function io.write(text)
|
||||
function io.write(sText)
|
||||
sText = tostring(sText)
|
||||
|
||||
local w, h = term.getSize()
|
||||
local x, y = term.getPos()
|
||||
|
||||
local nLinesPrinted = 0
|
||||
local function newLine()
|
||||
if y + 1 <= h then
|
||||
term.setPos(1, y + 1)
|
||||
else
|
||||
term.setPos(1, h)
|
||||
term.scroll(1)
|
||||
end
|
||||
x, y = term.getPos()
|
||||
nLinesPrinted = nLinesPrinted + 1
|
||||
end
|
||||
|
||||
-- Print the line with proper word wrapping
|
||||
sText = tostring(sText)
|
||||
while #sText > 0 do
|
||||
local whitespace = string.match(sText, "^[ \t]+")
|
||||
if whitespace then
|
||||
-- Print whitespace
|
||||
term.write(whitespace)
|
||||
x, y = term.getPos()
|
||||
sText = string.sub(sText, #whitespace + 1)
|
||||
end
|
||||
|
||||
local newline = string.match(sText, "^\n")
|
||||
if newline then
|
||||
-- Print newlines
|
||||
newLine()
|
||||
sText = string.sub(sText, 2)
|
||||
end
|
||||
|
||||
local text = string.match(sText, "^[^ \t\n]+")
|
||||
if text then
|
||||
sText = string.sub(sText, #text + 1)
|
||||
if #text > w then
|
||||
-- Print a multiline word
|
||||
while #text > 0 do
|
||||
if x > w then
|
||||
newLine()
|
||||
end
|
||||
term.write(text)
|
||||
text = string.sub(text, w - x + 2)
|
||||
x, y = term.getPos()
|
||||
end
|
||||
else
|
||||
-- Print a word normally
|
||||
if x + #text - 1 > w then
|
||||
newLine()
|
||||
end
|
||||
term.write(text)
|
||||
x, y = term.getPos()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
||||
expect(1, _sReplaceChar, "string", "nil")
|
||||
expect(2, _tHistory, "table", "nil")
|
||||
expect(3, _fnComplete, "function", "nil")
|
||||
expect(4, _sDefault, "string", "nil")
|
||||
|
||||
term.setBlink(true)
|
||||
|
||||
local sLine
|
||||
if type(_sDefault) == "string" then
|
||||
sLine = _sDefault
|
||||
else
|
||||
sLine = ""
|
||||
end
|
||||
local nHistoryPos
|
||||
local nPos, nScroll = #sLine, 0
|
||||
if _sReplaceChar then
|
||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
||||
end
|
||||
|
||||
local tCompletions
|
||||
local nCompletion
|
||||
local function recomplete()
|
||||
if _fnComplete and nPos == #sLine then
|
||||
tCompletions = _fnComplete(sLine)
|
||||
if tCompletions and #tCompletions > 0 then
|
||||
nCompletion = 1
|
||||
else
|
||||
nCompletion = nil
|
||||
end
|
||||
else
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function uncomplete()
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
|
||||
local w = term.getSize()
|
||||
local sx = term.getPos()
|
||||
|
||||
local function redraw(_bClear)
|
||||
local cursor_pos = nPos - nScroll
|
||||
if sx + cursor_pos >= w then
|
||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
||||
nScroll = sx + nPos - w
|
||||
elseif cursor_pos < 0 then
|
||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
||||
nScroll = nPos
|
||||
end
|
||||
|
||||
local _, cy = term.getPos()
|
||||
term.setPos(sx, cy)
|
||||
local sReplace = _bClear and " " or _sReplaceChar
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
||||
else
|
||||
term.write(string.sub(sLine, nScroll + 1))
|
||||
end
|
||||
|
||||
if nCompletion then
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
local oldText, oldBg
|
||||
if not _bClear then
|
||||
oldText = term.getTextColor()
|
||||
oldBg = term.getBackgroundColor()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
end
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, #sCompletion))
|
||||
else
|
||||
term.write(sCompletion)
|
||||
end
|
||||
if not _bClear then
|
||||
term.setTextColor(oldText)
|
||||
term.setBackgroundColor(oldBg)
|
||||
end
|
||||
end
|
||||
|
||||
term.setPos(sx + nPos - nScroll, cy)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
redraw(true)
|
||||
end
|
||||
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
local function acceptCompletion()
|
||||
if nCompletion then
|
||||
-- Clear
|
||||
clear()
|
||||
|
||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
sLine = sLine .. sCompletion
|
||||
nPos = #sLine
|
||||
|
||||
-- Redraw
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local sEvent, param, param1, param2 = event.pull()
|
||||
if sEvent == "char" then
|
||||
-- Typed key
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "paste" then
|
||||
-- Pasted text
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + #param
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "key_down" then
|
||||
if param == keys.enter or param == keys.numPadEnter then
|
||||
-- Enter/Numpad Enter
|
||||
if nCompletion then
|
||||
clear()
|
||||
uncomplete()
|
||||
redraw()
|
||||
end
|
||||
break
|
||||
|
||||
elseif param == keys.left then
|
||||
-- Left
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = nPos - 1
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.right then
|
||||
-- Right
|
||||
if nPos < #sLine then
|
||||
-- Move right
|
||||
clear()
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
else
|
||||
-- Accept autocomplete
|
||||
acceptCompletion()
|
||||
end
|
||||
|
||||
elseif param == keys.up or param == keys.down then
|
||||
-- Up or down
|
||||
if nCompletion then
|
||||
-- Cycle completions
|
||||
clear()
|
||||
if param == keys.up then
|
||||
nCompletion = nCompletion - 1
|
||||
if nCompletion < 1 then
|
||||
nCompletion = #tCompletions
|
||||
end
|
||||
elseif param == keys.down then
|
||||
nCompletion = nCompletion + 1
|
||||
if nCompletion > #tCompletions then
|
||||
nCompletion = 1
|
||||
end
|
||||
end
|
||||
redraw()
|
||||
|
||||
elseif _tHistory then
|
||||
-- Cycle history
|
||||
clear()
|
||||
if param == keys.up then
|
||||
-- Up
|
||||
if nHistoryPos == nil then
|
||||
if #_tHistory > 0 then
|
||||
nHistoryPos = #_tHistory
|
||||
end
|
||||
elseif nHistoryPos > 1 then
|
||||
nHistoryPos = nHistoryPos - 1
|
||||
end
|
||||
else
|
||||
-- Down
|
||||
if nHistoryPos == #_tHistory then
|
||||
nHistoryPos = nil
|
||||
elseif nHistoryPos ~= nil then
|
||||
nHistoryPos = nHistoryPos + 1
|
||||
end
|
||||
end
|
||||
if nHistoryPos then
|
||||
sLine = _tHistory[nHistoryPos]
|
||||
nPos, nScroll = #sLine, 0
|
||||
else
|
||||
sLine = ""
|
||||
nPos, nScroll = 0, 0
|
||||
end
|
||||
uncomplete()
|
||||
redraw()
|
||||
|
||||
end
|
||||
|
||||
elseif param == keys.back then
|
||||
-- Backspace
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos - 1
|
||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.home then
|
||||
-- Home
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = 0
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.delete then
|
||||
-- Delete
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys["end"] then
|
||||
-- End
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
nPos = #sLine
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.tab then
|
||||
-- Tab (accept autocomplete)
|
||||
acceptCompletion()
|
||||
|
||||
end
|
||||
|
||||
elseif sEvent == "mouse_down" or sEvent == "mouse_drag" and param == 1 then
|
||||
local _, cy = term.getPos()
|
||||
if param1 >= sx and param1 <= w and param2 == cy then
|
||||
-- Ensure we don't scroll beyond the current line
|
||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif sEvent == "term_resize" then
|
||||
-- Terminal resized
|
||||
w = term.getSize()
|
||||
redraw()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local _, cy = term.getPos()
|
||||
term.setBlink(false)
|
||||
term.setPos(w + 1, cy)
|
||||
print()
|
||||
|
||||
return sLine
|
||||
end
|
||||
|
||||
|
||||
--[[function io.write(text)
|
||||
text = tostring(text)
|
||||
|
||||
local lines = 0
|
||||
|
@ -28,7 +368,7 @@ function io.write(text)
|
|||
local chunk = text:sub(1, nl)
|
||||
text = text:sub(#chunk + 1)
|
||||
|
||||
local has_nl = chunk:sub( -1) == "\n"
|
||||
local has_nl = chunk:sub(-1) == "\n"
|
||||
if has_nl then chunk = chunk:sub(1, -2) end
|
||||
|
||||
local cx, cy = term.getPos()
|
||||
|
@ -83,7 +423,7 @@ function io.read(replace, history, complete, default)
|
|||
|
||||
local function clearCompletion()
|
||||
if completions[comp_id] then
|
||||
write((" "):rep(#completions[comp_id]))
|
||||
io.write((" "):rep(#completions[comp_id]))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -142,7 +482,7 @@ function io.read(replace, history, complete, default)
|
|||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub( -cursor_pos)
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif evt == "key_down" then
|
||||
if par1 == keys.back and #buffer > 0 then
|
||||
|
@ -151,7 +491,7 @@ function io.read(replace, history, complete, default)
|
|||
buffer = buffer:sub(1, -2)
|
||||
clearCompletion()
|
||||
elseif cursor_pos < #buffer then
|
||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub( -cursor_pos)
|
||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif par1 == keys.delete and cursor_pos > 0 then
|
||||
dirty = true
|
||||
|
@ -161,7 +501,7 @@ function io.read(replace, history, complete, default)
|
|||
elseif cursor_pos == 1 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub( -cursor_pos + 1)
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
cursor_pos = cursor_pos - 1
|
||||
elseif par1 == keys.up then
|
||||
|
@ -243,7 +583,7 @@ function io.read(replace, history, complete, default)
|
|||
buffer = text .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
||||
buffer:sub( -cursor_pos + (#text - 1))
|
||||
buffer:sub(-cursor_pos + (#text - 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -255,6 +595,7 @@ function io.read(replace, history, complete, default)
|
|||
|
||||
return buffer
|
||||
end
|
||||
]]
|
||||
|
||||
io.stderr = {}
|
||||
|
||||
|
|
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
local x = math.random()
|
||||
|
||||
return x
|
176
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
176
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
|
@ -0,0 +1,176 @@
|
|||
local expect = require("expect").expect
|
||||
local tableutils = require("tableutils")
|
||||
local event = require("event")
|
||||
|
||||
local scheduler = {}
|
||||
|
||||
local function contains(array, value)
|
||||
for k, v in pairs(array) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local tasks = {}
|
||||
local processes = 0
|
||||
|
||||
local Task = {}
|
||||
local TaskMeta = {
|
||||
__index = Task,
|
||||
__name = "OS_TASK",
|
||||
__tostring = function(self)
|
||||
return string.format("OS_TASK[%s]: %d", self.source or "", self.pid or 0)
|
||||
end,
|
||||
}
|
||||
local function newTask()
|
||||
local task = {}
|
||||
return setmetatable(task, TaskMeta)
|
||||
end
|
||||
|
||||
function Task:queue(eventName, ...)
|
||||
expect(1, eventName, "string")
|
||||
event.push("scheduler", self.pid, eventName, ...)
|
||||
end
|
||||
|
||||
local function findParent()
|
||||
local i = 3
|
||||
|
||||
while true do
|
||||
local info = debug.getinfo(i)
|
||||
if not info then
|
||||
break
|
||||
end
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.uuid == tostring(info.func) then
|
||||
return task
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function scheduler.spawn(func, options)
|
||||
expect(1, func, "function")
|
||||
expect(2, options, "nil", "table")
|
||||
|
||||
options = options or {}
|
||||
options.args = options.args or {}
|
||||
|
||||
local source = debug.getinfo(2)
|
||||
|
||||
local task = newTask()
|
||||
local pid = #tasks + 1
|
||||
task.pid = pid
|
||||
task.options = options
|
||||
task.source = source.source
|
||||
task.uuid = tostring(func)
|
||||
task.thread = coroutine.create(func)
|
||||
task.started = false
|
||||
local parent = findParent()
|
||||
if parent then
|
||||
task.parent = parent.pid
|
||||
table.insert(parent.children, pid)
|
||||
end
|
||||
task.filters = {}
|
||||
task.children = {}
|
||||
task.eventQueue = {}
|
||||
|
||||
tasks[pid] = task
|
||||
|
||||
processes = processes + 1
|
||||
|
||||
return task
|
||||
end
|
||||
|
||||
local function cascadeKill(pid, err)
|
||||
local task = tasks[pid]
|
||||
if not task then
|
||||
return
|
||||
end
|
||||
for i, cpid in ipairs(task.children) do
|
||||
cascadeKill(cpid, err)
|
||||
end
|
||||
if task.parent then
|
||||
local parent = tasks[task.parent]
|
||||
if parent then
|
||||
local index = tableutils.find(parent.children, task.pid)
|
||||
table.remove(parent.children, index)
|
||||
parent:queue("scheduler_task_end", task, err == nil, err)
|
||||
end
|
||||
else
|
||||
if err then
|
||||
error(err, 0)
|
||||
end
|
||||
end
|
||||
if task then
|
||||
task.killed = true
|
||||
coroutine.close(task.thread)
|
||||
tasks[pid] = nil
|
||||
processes = processes - 1
|
||||
end
|
||||
end
|
||||
|
||||
function scheduler.kill(pid)
|
||||
expect(1, pid, "number")
|
||||
cascadeKill(pid)
|
||||
end
|
||||
|
||||
function scheduler.ipc(pid, ...)
|
||||
expect(1, pid, "number")
|
||||
if not tasks[pid] then
|
||||
error("process by pid " .. pid .. " does not exist.", 2)
|
||||
end
|
||||
|
||||
local sender = findParent()
|
||||
tasks[pid]:queue("ipc_message", sender, ...)
|
||||
end
|
||||
|
||||
local running = false
|
||||
function scheduler.init()
|
||||
if running then
|
||||
error("scheduler already running", 2)
|
||||
end
|
||||
running = true
|
||||
|
||||
local ev = { n = 0 }
|
||||
while processes > 0 do
|
||||
for pid, task in pairs(tasks) do
|
||||
local yieldPars = ev
|
||||
if ev[1] == "scheduler" and ev[2] == pid then
|
||||
yieldPars = table.pack(table.unpack(ev, 3))
|
||||
end
|
||||
if yieldPars[1] ~= "scheduler" and not task.filters or #task.filters == 0 or contains(task.filters, yieldPars[1]) or yieldPars[1] == "interrupt" then
|
||||
if not task.started then
|
||||
yieldPars = task.options.args
|
||||
task.started = true
|
||||
end
|
||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
||||
if pars[1] then
|
||||
task.filters = table.pack(table.unpack(pars, 2))
|
||||
else
|
||||
cascadeKill(pid, pars[2])
|
||||
end
|
||||
end
|
||||
|
||||
if coroutine.status(task.thread) == "dead" then
|
||||
cascadeKill(pid)
|
||||
end
|
||||
end
|
||||
|
||||
if processes <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
ev = table.pack(coroutine.yield())
|
||||
end
|
||||
|
||||
running = false
|
||||
end
|
||||
|
||||
return scheduler
|
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
|
@ -0,0 +1,87 @@
|
|||
local expect = require("expect").expect
|
||||
local tableutils = {}
|
||||
|
||||
local function serialize(data, circular)
|
||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
||||
if type(data) == "table" then
|
||||
if not circular then
|
||||
circular = {}
|
||||
end
|
||||
local output = "{"
|
||||
for k, v in pairs(data) do
|
||||
if type(v) == "table" then
|
||||
local name = tostring(v)
|
||||
if circular[name] then
|
||||
error("circular reference in table", 2)
|
||||
end
|
||||
circular[name] = true
|
||||
end
|
||||
output = output .. string.format("[%q] = %s,", k, serialize(v, circular))
|
||||
end
|
||||
output = output .. "}"
|
||||
return output
|
||||
else
|
||||
return string.format("%q", data)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.serialize(data)
|
||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
||||
return serialize(data)
|
||||
end
|
||||
|
||||
function tableutils.deserialize(data)
|
||||
local func, err = load("return " .. data, "=tableutils", "t", {})
|
||||
if not func then
|
||||
error(err, 2)
|
||||
end
|
||||
return func()
|
||||
end
|
||||
|
||||
local function prettyvalue(value)
|
||||
if type(value) == "table" or type(value) == "function" or type(value) == "thread" or type(value) == "userdata" or type(value) == "number" then
|
||||
return tostring(value)
|
||||
else
|
||||
return string.format("%q", value)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.pretty(data)
|
||||
if type(data) == "table" then
|
||||
local output = "{"
|
||||
|
||||
local index = 0
|
||||
for k, v in pairs(data) do
|
||||
local value = prettyvalue(v)
|
||||
|
||||
if type(k) == "number" and k - 1 == index then
|
||||
index = index + 1
|
||||
output = output .. string.format("\n %s,", value)
|
||||
elseif type(k) == "string" and k:match("^[%a_][%w_]*$") then
|
||||
output = output .. string.format("\n %s = %s,", k, value)
|
||||
else
|
||||
output = output .. string.format("\n [%s] = %s,", prettyvalue(k), value)
|
||||
end
|
||||
end
|
||||
if output == "{" then
|
||||
return "{}"
|
||||
end
|
||||
output = output .. "\n}"
|
||||
|
||||
return output
|
||||
else
|
||||
return prettyvalue(data)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.find(tbl, element)
|
||||
for i = 1, #tbl do
|
||||
if tbl[i] == element then
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return tableutils
|
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
|
@ -0,0 +1,2 @@
|
|||
Welcome to CapyOS!
|
||||
Run "programs" to get a list of available programs.
|
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
|
@ -0,0 +1,12 @@
|
|||
CapyOS and Capy64 are licensed under the Apache 2.0 Public License.
|
||||
https://capy64.alexdevs.me/
|
||||
https://github.com/Ale32bit/Capy64
|
||||
|
||||
Some CapyOS components include code from the CC Recrafted project.
|
||||
https://github.com/Ocawesome101/recrafted
|
||||
|
||||
Some CapyOS components may include code from CC: Tweaked.
|
||||
https://github.com/CC-Tweaked/CC-Tweaked
|
||||
|
||||
json.lua by rxi is licensed under the MIT License.
|
||||
https://github.com/rxi/json.lua
|
8
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
8
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
Please do not use this software to flash firmwares.
|
||||
Now with bitwise operators!
|
||||
Writing to _G is a war crime.
|
||||
Tampering with _G in any way is also a war crime.
|
||||
No, Wojbie, stop! STOP TOUCHING _G!
|
||||
Stop! You've violated the law!
|
||||
That's 65% more bullet per bullet.
|
||||
This software is not responsible for collapses of spacetime.
|
Loading…
Reference in a new issue