Compare commits
9 Commits
1.2.2
...
413afd96de
| Author | SHA1 | Date | |
|---|---|---|---|
| 413afd96de | |||
| a0a0ac69d4 | |||
| 17453983ad | |||
| a6550aa069 | |||
| 02e7b3897c | |||
| 34b89a8e34 | |||
| 0eabfebd0f | |||
| 5b2e5eac65 | |||
| 415064480a |
@@ -1,33 +0,0 @@
|
||||
local args = {...}
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
local fs = require("sys.fs")
|
||||
|
||||
if not args[1] then
|
||||
while true do
|
||||
local content = syscall.read(0, 1024)
|
||||
if not content or content == "" then break end
|
||||
printInline(content)
|
||||
end
|
||||
print("")
|
||||
return
|
||||
end
|
||||
|
||||
for _, arg in ipairs(args) do
|
||||
local filePath = arg
|
||||
if filePath:sub(1,1) ~= "/" then
|
||||
filePath = syscall.getcwd().."/"..filePath
|
||||
end
|
||||
|
||||
if not fs.exists(filePath) then
|
||||
print(name..": Cannot access '"..arg.."': No such file.")
|
||||
else
|
||||
local fd = syscall.open(filePath, "r")
|
||||
while true do
|
||||
local content = syscall.read(fd, 1024)
|
||||
if not content or content == "" then break end
|
||||
printInline(content)
|
||||
end
|
||||
syscall.close(fd)
|
||||
end
|
||||
end
|
||||
print("")
|
||||
@@ -1 +0,0 @@
|
||||
syscall.devctl(1,"clear")
|
||||
@@ -1,2 +0,0 @@
|
||||
local args = {...}
|
||||
print(table.concat(args, " "))
|
||||
@@ -1,6 +1,6 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/tty1","r") --stdin (Device 0)
|
||||
syscall.open("/dev/tty/tty1","w") --stdout (Device 1)
|
||||
syscall.open("/dev/tty/1","r") --stdin (Device 0)
|
||||
syscall.open("/dev/tty/1","w") --stdout (Device 1)
|
||||
syscall.open("/dev/null","w") --stderr (device 2)
|
||||
|
||||
local success, errorMsg = xpcall(function()
|
||||
@@ -810,6 +810,160 @@ builtinCmds.df = function(...)
|
||||
end
|
||||
end
|
||||
|
||||
local function listDir(dir, prefix)
|
||||
local ok, entries = pcall(syscall.listdir, dir)
|
||||
if not ok or not entries then return {} end
|
||||
local results = {}
|
||||
for _, e in ipairs(entries) do
|
||||
if prefix == "" or e:sub(1, #prefix) == prefix then
|
||||
local fullpath = (dir == "/" and "/" or dir.."/")..e
|
||||
local t = syscall.type(fullpath)
|
||||
results[#results+1] = t == "directory" and (e.."/") or e
|
||||
end
|
||||
end
|
||||
table.sort(results)
|
||||
return results
|
||||
end
|
||||
|
||||
local function listCommands(prefix)
|
||||
local results = {}
|
||||
local seen = {}
|
||||
for name in pairs(builtinCmds) do
|
||||
if prefix == "" or name:sub(1, #prefix) == prefix then
|
||||
if not seen[name] then results[#results+1] = name; seen[name] = true end
|
||||
end
|
||||
end
|
||||
local paths = string.split(syscall.getEnviron("PATH") or "/bin/", ":")
|
||||
for _, p in ipairs(paths) do
|
||||
local ok, entries = pcall(syscall.listdir, p)
|
||||
if ok and entries then
|
||||
for _, e in ipairs(entries) do
|
||||
local fullpath = (p:sub(-1)=="/" and p or p.."/")..e
|
||||
local xok = pcall(syscall.access, fullpath, "x")
|
||||
if xok and (prefix == "" or e:sub(1, #prefix) == prefix) then
|
||||
if not seen[e] then results[#results+1] = e; seen[e] = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(results)
|
||||
return results
|
||||
end
|
||||
|
||||
local function commonPrefix(list)
|
||||
if #list == 0 then return "" end
|
||||
local pre = list[1]
|
||||
for i = 2, #list do
|
||||
local s = list[i]
|
||||
local j = 1
|
||||
while j <= #pre and j <= #s and pre:sub(j,j) == s:sub(j,j) do j = j+1 end
|
||||
pre = pre:sub(1, j-1)
|
||||
if pre == "" then return "" end
|
||||
end
|
||||
return pre
|
||||
end
|
||||
|
||||
local function showCompletions(completions)
|
||||
syscall.write(1, "\n")
|
||||
local W = 51
|
||||
local maxlen = 0
|
||||
for _, c in ipairs(completions) do if #c > maxlen then maxlen = #c end end
|
||||
local colw = maxlen + 2
|
||||
local cols = math.max(1, math.floor(W / colw))
|
||||
local i = 0
|
||||
for _, c in ipairs(completions) do
|
||||
local padded = c .. string.rep(" ", colw - #c)
|
||||
syscall.write(1, padded)
|
||||
i = i + 1
|
||||
if i % cols == 0 then syscall.write(1, "\n") end
|
||||
end
|
||||
if i % cols ~= 0 then syscall.write(1, "\n") end
|
||||
end
|
||||
|
||||
local function parseForCompletion(input, cursorPos)
|
||||
local sofar = input:sub(1, cursorPos - 1)
|
||||
local tokens = {}
|
||||
for tok in sofar:gmatch("%S+") do tokens[#tokens+1] = tok end
|
||||
local partial = sofar:match("%S+$") or ""
|
||||
local isFirst = (#tokens == 0) or (sofar:sub(-1) ~= " " and #tokens == 1)
|
||||
|
||||
local partialDir, partialBase
|
||||
if partial:find("/") then
|
||||
partialDir = partial:match("^(.*/)") or "/"
|
||||
partialBase = partial:match("[^/]*$") or ""
|
||||
else
|
||||
partialDir = nil
|
||||
partialBase = partial
|
||||
end
|
||||
|
||||
return tokens, partial, isFirst, partialDir, partialBase
|
||||
end
|
||||
|
||||
local tabState = { last = nil, idx = 0, list = {} }
|
||||
|
||||
local function doTabComplete(input, cursorPos)
|
||||
local tokens, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
|
||||
|
||||
local candidates
|
||||
if isFirst then
|
||||
if partialDir then
|
||||
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
|
||||
candidates = listDir(dir, partialBase)
|
||||
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
|
||||
else
|
||||
candidates = listCommands(partialBase)
|
||||
end
|
||||
else
|
||||
local dir, base
|
||||
if partialDir then
|
||||
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
|
||||
base = partialBase
|
||||
else
|
||||
dir = syscall.getcwd()
|
||||
base = partialBase
|
||||
end
|
||||
candidates = listDir(dir, base)
|
||||
if partialDir then
|
||||
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
|
||||
end
|
||||
end
|
||||
|
||||
if #candidates == 0 then
|
||||
return input, cursorPos, false
|
||||
end
|
||||
|
||||
local context = input.."\0"..tostring(cursorPos)
|
||||
if tabState.last ~= context then
|
||||
tabState.last = context
|
||||
tabState.idx = 0
|
||||
tabState.list = candidates
|
||||
end
|
||||
|
||||
if #candidates == 1 then
|
||||
local completed = candidates[1]
|
||||
local before = input:sub(1, cursorPos - 1 - #partial)
|
||||
local after = input:sub(cursorPos)
|
||||
local newInput = before .. completed .. after
|
||||
local newCursor = #before + #completed + 1
|
||||
tabState.last = nil
|
||||
return newInput, newCursor, true
|
||||
end
|
||||
|
||||
local pre = commonPrefix(candidates)
|
||||
if #pre > #partial then
|
||||
local before = input:sub(1, cursorPos - 1 - #partial)
|
||||
local after = input:sub(cursorPos)
|
||||
local newInput = before .. pre .. after
|
||||
local newCursor = #before + #pre + 1
|
||||
tabState.last = newInput.."\0"..tostring(newCursor)
|
||||
tabState.list = candidates
|
||||
return newInput, newCursor, true
|
||||
else
|
||||
showCompletions(candidates)
|
||||
return input, cursorPos, true
|
||||
end
|
||||
end
|
||||
|
||||
local function getUserInput()
|
||||
syscall.devctl(1,"sfgc",3)
|
||||
syscall.write(1, userhost)
|
||||
@@ -829,6 +983,41 @@ local function getUserInput()
|
||||
local history = 0
|
||||
local dirty = true
|
||||
|
||||
local function getGhostSuffix()
|
||||
if #input == 0 then return "" end
|
||||
local _, partial, isFirst, partialDir, partialBase = parseForCompletion(input, cursorPos)
|
||||
if cursorPos ~= #input + 1 then return "" end
|
||||
local candidates
|
||||
if isFirst then
|
||||
if partialDir then
|
||||
local dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
|
||||
candidates = listDir(dir, partialBase)
|
||||
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
|
||||
else
|
||||
candidates = listCommands(partialBase)
|
||||
end
|
||||
else
|
||||
local dir, base
|
||||
if partialDir then
|
||||
dir = partialDir:sub(1,1) == "/" and partialDir or (syscall.getcwd().."/"..partialDir)
|
||||
base = partialBase
|
||||
else
|
||||
dir = syscall.getcwd()
|
||||
base = partialBase
|
||||
end
|
||||
candidates = listDir(dir, base)
|
||||
if partialDir then
|
||||
for i, c in ipairs(candidates) do candidates[i] = partialDir..c end
|
||||
end
|
||||
end
|
||||
if #candidates == 0 then return "" end
|
||||
local pre = commonPrefix(candidates)
|
||||
if #pre > #partial then
|
||||
return pre:sub(#partial + 1)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function redraw()
|
||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
||||
@@ -841,21 +1030,31 @@ local function getUserInput()
|
||||
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
||||
end
|
||||
syscall.devctl(1,"sfgc",1); syscall.devctl(1,"sbgc",16)
|
||||
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
|
||||
local after = string.sub(input, cursorPos+1)
|
||||
syscall.write(1, after)
|
||||
local ghost = getGhostSuffix()
|
||||
if #ghost > 0 then
|
||||
syscall.devctl(1,"sfgc",14)
|
||||
syscall.write(1, ghost)
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.write(1, " ")
|
||||
else
|
||||
syscall.write(1, " ")
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local key = syscall.read(0)
|
||||
if key and key ~= "" then
|
||||
if key=="\19" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
|
||||
elseif key=="\20" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
|
||||
elseif key=="\17" then
|
||||
if key=="[D" then if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end
|
||||
elseif key=="[C" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end
|
||||
elseif key=="[A" then
|
||||
if history<#commandHistory then
|
||||
history=history+1
|
||||
input=commandHistory[#commandHistory-history+1]
|
||||
cursorPos=#input+1;dirty=true
|
||||
end
|
||||
elseif key=="\18" then
|
||||
elseif key=="[B" then
|
||||
if history>1 then
|
||||
history=history-1
|
||||
input=commandHistory[#commandHistory-history+1]
|
||||
@@ -863,6 +1062,38 @@ local function getUserInput()
|
||||
elseif history==1 then
|
||||
history=0;input="";cursorPos=1;dirty=true
|
||||
end
|
||||
elseif key=="[H" then cursorPos=1;dirty=true
|
||||
elseif key=="[F" then cursorPos=#input+1;dirty=true
|
||||
elseif key=="[3~" then
|
||||
if cursorPos<=#input then
|
||||
input=string.sub(input,1,cursorPos-1)..string.sub(input,cursorPos+1)
|
||||
dirty=true
|
||||
end
|
||||
elseif key=="\t" then
|
||||
local newInput, newCursor, needsRedraw = doTabComplete(input, cursorPos)
|
||||
if needsRedraw then
|
||||
input = newInput; cursorPos = newCursor
|
||||
local posStr = syscall.devctl(1, "gpos")
|
||||
local sep = posStr:find(";")
|
||||
local px = tonumber(posStr:sub(1, sep-1))
|
||||
local py = tonumber(posStr:sub(sep+1))
|
||||
if px > 1 then
|
||||
syscall.devctl(1,"spos",1,py)
|
||||
local tsz = syscall.devctl(1,"size") or "51;19"
|
||||
local tw = tonumber(tsz:match("^(%d+)")) or 51
|
||||
syscall.write(1, string.rep(" ", tw))
|
||||
syscall.devctl(1,"spos",1,py)
|
||||
end
|
||||
syscall.devctl(1,"sfgc",3); syscall.write(1, userhost)
|
||||
syscall.devctl(1,"sfgc",1); syscall.write(1, ":")
|
||||
syscall.devctl(1,"sfgc",10); syscall.write(1, syscall.getcwd())
|
||||
syscall.devctl(1,"sfgc",1); syscall.write(1, "$ ")
|
||||
posStr = syscall.devctl(1, "gpos")
|
||||
sep = posStr:find(";")
|
||||
curOffsetX = tonumber(posStr:sub(1, sep-1))
|
||||
curOffsetY = tonumber(posStr:sub(sep+1))
|
||||
dirty = true
|
||||
end
|
||||
elseif key=="\b" then
|
||||
if cursorPos>1 then
|
||||
input=string.sub(input,1,cursorPos-2)..string.sub(input,cursorPos)
|
||||
@@ -873,7 +1104,7 @@ local function getUserInput()
|
||||
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
||||
syscall.write(1, input.." \n")
|
||||
return input
|
||||
else
|
||||
elseif #key == 1 and key:byte(1) >= 32 and key:byte(1) < 127 then
|
||||
input=string.sub(input,1,cursorPos-1)..key..string.sub(input,cursorPos)
|
||||
cursorPos=cursorPos+1;dirty=true
|
||||
end
|
||||
@@ -946,23 +1177,16 @@ local function runCommand(command)
|
||||
return
|
||||
end
|
||||
|
||||
local text = fs.readAllText(cmdPath)
|
||||
local program, err = load(text, progName)
|
||||
if not program then
|
||||
syscall.devctl(1,"sfgc",2)
|
||||
local line, rest = tostring(err):match(":(%d+): (.+)$")
|
||||
if line then printInline(progName..": load error on line "..line..": "); print(rest)
|
||||
else print(progName..": load error: "..tostring(err)) end
|
||||
syscall.devctl(1,"sfgc",1); return
|
||||
end
|
||||
|
||||
local proc = syscall.spawn(function(...)
|
||||
syscall.open("/dev/tty/tty1","r")
|
||||
syscall.open("/dev/tty/tty1","w")
|
||||
syscall.open("/dev/null","w")
|
||||
local ok2, msg = pcall(program, ...)
|
||||
if not ok2 then printError(progName, msg) end
|
||||
end, progName, nil, {table.unpack(args, 2)})
|
||||
local proc = syscall.spawn(function()
|
||||
-- Open standard fds so programs that don't do it themselves work correctly.
|
||||
syscall.open("/dev/tty/1", "r") -- fd 0 stdin
|
||||
syscall.open("/dev/tty/1", "w") -- fd 1 stdout
|
||||
syscall.open("/dev/null", "w") -- fd 2 stderr
|
||||
-- exec replaces this coroutine's code with a fresh isolated environment
|
||||
-- compiled from disk by the kernel (via loadExecutable -> freshUserEnv),
|
||||
-- so the child cannot share any upvalue or syscall table state with hysh.
|
||||
syscall.exec(cmdPath, {table.unpack(args, 2)})
|
||||
end, progName)
|
||||
|
||||
while true do
|
||||
local exited, code = syscall.collect(proc)
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/tty1","r")
|
||||
syscall.open("/dev/tty/tty1","w")
|
||||
syscall.open("/dev/null","r")
|
||||
syscall.devctl(1,"clear")
|
||||
syscall.devctl(1,"sfgc",1)
|
||||
syscall.devctl(1,"spos",1,1)
|
||||
print("HyperionOS hysh Shell")
|
||||
local str=""
|
||||
local stopInput=false
|
||||
local proc=0
|
||||
local fs=require("sys.fs")
|
||||
local timeout=false
|
||||
syscall.setEnviron("SHELL","simpleshell")
|
||||
printInline("> ")
|
||||
syscall.sigcatch(function(sig)
|
||||
if sig==1 then
|
||||
syscall.kill(proc)
|
||||
print("Terminated")
|
||||
printInline("> ")
|
||||
stopInput=false
|
||||
end
|
||||
end)
|
||||
|
||||
while true do
|
||||
if not stopInput then
|
||||
local input=syscall.read(0)
|
||||
if input then
|
||||
if input=="\b" then
|
||||
if #str>0 then
|
||||
str=str:sub(1,#str-1)
|
||||
printInline("\b")
|
||||
end
|
||||
elseif input=="\n" then
|
||||
print("")
|
||||
stopInput=true
|
||||
if str == "" then
|
||||
printInline("> ")
|
||||
stopInput=false
|
||||
else
|
||||
local path=nil
|
||||
local split=string.split(str, " ")
|
||||
if fs.exists("/bin/"..split[1]) then
|
||||
path="/bin/"..split[1]
|
||||
elseif fs.exists("/bin/"..split[1]..".lua") then
|
||||
path="/bin/"..split[1]..".lua"
|
||||
end
|
||||
if not path then
|
||||
print("Program not found")
|
||||
printInline("> ")
|
||||
stopInput=false
|
||||
else
|
||||
local text = fs.readAllText(path)
|
||||
local program, err = load(text, path)
|
||||
if not program then
|
||||
print(err)
|
||||
printInline("> ")
|
||||
end
|
||||
proc = syscall.spawn(function(...)
|
||||
syscall.open("/dev/tty/tty1","r")
|
||||
syscall.open("/dev/tty/tty1","w")
|
||||
syscall.open("/dev/null","w")
|
||||
program(...)
|
||||
end, path, nil, {table.unpack(split, 2)})
|
||||
end
|
||||
str=""
|
||||
end
|
||||
else
|
||||
str=str..input
|
||||
printInline(input)
|
||||
end
|
||||
timeout=false
|
||||
else
|
||||
timeout=true
|
||||
end
|
||||
else
|
||||
local exited, code = syscall.collect(proc)
|
||||
if exited then
|
||||
if code then
|
||||
print("\nTask exited with code:\n"..tostring(code))
|
||||
end
|
||||
printInline("> ")
|
||||
stopInput=false
|
||||
end
|
||||
timeout=true
|
||||
end
|
||||
if timeout then
|
||||
if stopInput then
|
||||
sleep(.5)
|
||||
else
|
||||
sleep(.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
--:Minify:--
|
||||
syscall.open("/dev/tty/tty1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/tty/tty1", "w") --stdout (fd 1)
|
||||
syscall.open("/dev/tty/1", "r") --stdin (fd 0)
|
||||
syscall.open("/dev/tty/1", "w") --stdout (fd 1)
|
||||
syscall.open("/dev/null", "w") --stderr (fd 2)
|
||||
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
--:Minify:--
|
||||
print("HyperionOS lua")
|
||||
local str=""
|
||||
local stopInput=false
|
||||
local timeout=false
|
||||
local luaEnv=setmetatable({},{__index=_ENV})
|
||||
printInline("> ")
|
||||
while true do
|
||||
local input=syscall.read(0)
|
||||
if input then
|
||||
if input=="\b" then
|
||||
if #str>0 then
|
||||
str=str:sub(1,#str-1)
|
||||
printInline("\b")
|
||||
end
|
||||
elseif input=="\n" then
|
||||
print("")
|
||||
stopInput=true
|
||||
if str == "" then
|
||||
printInline("> ")
|
||||
stopInput=false
|
||||
elseif str == "exit()" then
|
||||
break
|
||||
else
|
||||
local func=load(str,"@Lua","t",luaEnv)
|
||||
local ok,err = xpcall(func, debug.traceback)
|
||||
if not ok then
|
||||
print(err)
|
||||
end
|
||||
printInline("\n> ")
|
||||
str=""
|
||||
end
|
||||
str=""
|
||||
else
|
||||
str=str..input
|
||||
printInline(input)
|
||||
end
|
||||
timeout=false
|
||||
else
|
||||
timeout=true
|
||||
end
|
||||
|
||||
if timeout then
|
||||
if stopInput then
|
||||
sleep(.5)
|
||||
else
|
||||
sleep(.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -223,7 +223,7 @@ local function prompt(label, default)
|
||||
tbg(16); tfg(1)
|
||||
local key = syscall.read(0)
|
||||
if not key or key == "" then sleep(0.02)
|
||||
elseif key == "\27" then return nil
|
||||
elseif key == "" then return nil
|
||||
elseif key == "\n" then return inp
|
||||
elseif key == "\b" then if #inp > 0 then inp = inp:sub(1,-2) end
|
||||
else
|
||||
@@ -359,31 +359,29 @@ while running do
|
||||
local key = syscall.read(0)
|
||||
if key and key ~= "" then
|
||||
local b = key:byte(1)
|
||||
if key == "\17" then moveCursorUp(map); dirty=true
|
||||
elseif key == "\18" then moveCursorDown(map); dirty=true
|
||||
elseif key == "\19" then
|
||||
if cx > 1 then cx=cx-1
|
||||
elseif cy > 1 then cy=cy-1; cx=#lines[cy]+1 end
|
||||
dirty=true
|
||||
elseif key == "\20" then
|
||||
if key == "[A" then moveCursorUp(map); dirty=true
|
||||
elseif key == "[B" then moveCursorDown(map); dirty=true
|
||||
elseif key == "[C" then
|
||||
if cx <= #lines[cy] then cx=cx+1
|
||||
elseif cy < #lines then cy=cy+1; cx=1 end
|
||||
dirty=true
|
||||
elseif key == "[D" then
|
||||
if cx > 1 then cx=cx-1
|
||||
elseif cy > 1 then cy=cy-1; cx=#lines[cy]+1 end
|
||||
dirty=true
|
||||
elseif key == "[H" then cx=1; dirty=true
|
||||
elseif key == "[F" then cx=#lines[cy]+1; dirty=true
|
||||
elseif key == "[5~" then for _=1,ROWS do moveCursorUp(map) end; dirty=true
|
||||
elseif key == "[6~" then for _=1,ROWS do moveCursorDown(map) end; dirty=true
|
||||
elseif key == "[3~" then delRight()
|
||||
elseif key == "\n" then newline()
|
||||
elseif key == "\b" then delLeft()
|
||||
elseif key == "\t" then for _=1,4 do insChar(" ") end
|
||||
elseif b == 1 then cx=1; dirty=true
|
||||
elseif b == 2 then
|
||||
for _=1,ROWS do moveCursorUp(map) end; dirty=true
|
||||
elseif b == 4 then delRight()
|
||||
elseif b == 5 then cx=#lines[cy]+1; dirty=true
|
||||
elseif b == 6 then
|
||||
local p=prompt("Find: ",sPat); dirty=true
|
||||
if p then sPat=p; sLine=0; findNext() end
|
||||
elseif b == 7 then goToLine()
|
||||
elseif b == 11 then cutLine()
|
||||
elseif b == 12 then
|
||||
for _=1,ROWS do moveCursorDown(map) end; dirty=true
|
||||
elseif b == 14 then
|
||||
if sPat=="" then
|
||||
local p=prompt("Find: ",""); dirty=true
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
local args = {...}
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
if #args == 0 then
|
||||
print(name..": Missing operand.")
|
||||
return
|
||||
end
|
||||
|
||||
local fs = require("sys.fs")
|
||||
local newDir = args[1]
|
||||
if newDir:sub(1, 1) ~= "/" then
|
||||
newDir = syscall.getcwd().."/"..newDir
|
||||
end
|
||||
|
||||
if newDir:sub(#newDir, #newDir) ~= "/" then
|
||||
newDir = newDir.."/"
|
||||
end
|
||||
|
||||
if fs.isDir(newDir) then
|
||||
print(name..": Cannot create directory '"..args[1].."': Directory already exists.")
|
||||
return
|
||||
end
|
||||
|
||||
fs.mkdir(newDir)
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
for i,v in ipairs(syscall.getTasks()) do
|
||||
local task = syscall.getTask(v)
|
||||
print(task.pid,task.username,task.name,task.status)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
print(syscall.getcwd())
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local syscalls=syscall.sysdump()
|
||||
for i=1, #syscalls do
|
||||
print(syscalls[i])
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
print((syscall.getUsername() or "Unknown"))
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local args = {...}
|
||||
while true do
|
||||
if #args == 0 then
|
||||
|
||||
388
Src/Hyperion-core/lib/json
Normal file
388
Src/Hyperion-core/lib/json
Normal file
@@ -0,0 +1,388 @@
|
||||
--:Minify:--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
@@ -317,7 +317,18 @@ kernel.processes.cctmond = function()
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
-- Update modifier keys
|
||||
local ctrlKeyMap = {
|
||||
[apis.keys.a]=1, [apis.keys.b]=2, [apis.keys.c]=3,
|
||||
[apis.keys.d]=4, [apis.keys.e]=5, [apis.keys.f]=6,
|
||||
[apis.keys.g]=7, [apis.keys.h]=8, [apis.keys.i]=9,
|
||||
[apis.keys.j]=10, [apis.keys.k]=11, [apis.keys.l]=12,
|
||||
[apis.keys.m]=13, [apis.keys.n]=14, [apis.keys.o]=15,
|
||||
[apis.keys.p]=16, [apis.keys.q]=17, [apis.keys.r]=18,
|
||||
[apis.keys.s]=19, [apis.keys.t]=20, [apis.keys.u]=21,
|
||||
[apis.keys.v]=22, [apis.keys.w]=23, [apis.keys.x]=24,
|
||||
[apis.keys.y]=25, [apis.keys.z]=26,
|
||||
}
|
||||
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = true
|
||||
@@ -325,11 +336,31 @@ kernel.processes.cctmond = function()
|
||||
alt = true
|
||||
end
|
||||
|
||||
-- Handle Ctrl+C
|
||||
if ctrl and charOrKey == apis.keys.c then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1) -- SIGINT
|
||||
if ctrl then
|
||||
local ctrlByte = ctrlKeyMap[charOrKey]
|
||||
if ctrlByte then
|
||||
if ctrlByte == 3 then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
else
|
||||
fifo.push(string.char(ctrlByte))
|
||||
end
|
||||
end
|
||||
else
|
||||
local specialKeyMap = {
|
||||
[apis.keys.up] = "[A",
|
||||
[apis.keys.down] = "[B",
|
||||
[apis.keys.right] = "[C",
|
||||
[apis.keys.left] = "[D",
|
||||
[apis.keys.home] = "[H",
|
||||
[apis.keys["end"]] = "[F",
|
||||
[apis.keys.pageUp] = "[5~",
|
||||
[apis.keys.pageDown] = "[6~",
|
||||
[apis.keys.delete] = "[3~",
|
||||
}
|
||||
local special = specialKeyMap[charOrKey]
|
||||
if special then fifo.push(special) end
|
||||
end
|
||||
|
||||
elseif eventType == "keyReleased" then
|
||||
@@ -354,10 +385,10 @@ kernel.processes.cctmond = function()
|
||||
end
|
||||
end
|
||||
|
||||
newtty(apis.term, "TTY1", fifo.pop)
|
||||
newtty(apis.term, "1", fifo.pop)
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,"TTY"..tostring(i+1),function () end)
|
||||
newtty(v,tostring(i+1),function () end)
|
||||
end
|
||||
0
Src/Hyperion-installer/@CD/install
Normal file
0
Src/Hyperion-installer/@CD/install
Normal file
@@ -8,7 +8,7 @@ local computer = args[6]
|
||||
local ifs = args[7]
|
||||
local kernel = {}
|
||||
kernel.LOG_Text=""
|
||||
kernel.version="HyperionOS V1.2.0"
|
||||
kernel.version="HyperionOS V1.2.3"
|
||||
kernel.process = "Kernel"
|
||||
kernel.users={[0]="root",[1]="User"}
|
||||
kernel.hostname = "hyperion"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
kernel.allowGlobalOverwrites = true
|
||||
|
||||
@@ -207,17 +207,30 @@ function toHex(num)
|
||||
return string.format("%X", num)
|
||||
end
|
||||
|
||||
syscall = setmetatable({}, {
|
||||
__index = function(self, name)
|
||||
return function(...)
|
||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||
if res[1] then
|
||||
return table.unpack(res, 2, res.n)
|
||||
else
|
||||
error(res[2], 2)
|
||||
local function makeSyscallProxy()
|
||||
local backing = {}
|
||||
return setmetatable(backing, {
|
||||
__index = function(self, name)
|
||||
local raw = rawget(self, name)
|
||||
if raw ~= nil then return raw end
|
||||
return function(...)
|
||||
local res = table.pack(coroutine.yield("syscall", name, ...))
|
||||
if res[1] then
|
||||
return table.unpack(res, 2, res.n)
|
||||
else
|
||||
error(res[2], 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
end,
|
||||
__newindex = function(self, k, v)
|
||||
rawset(self, k, v)
|
||||
end,
|
||||
__metatable=false
|
||||
})
|
||||
end
|
||||
|
||||
syscall = makeSyscallProxy()
|
||||
|
||||
_makeSyscallProxy = makeSyscallProxy
|
||||
|
||||
table.serialize = serialize
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local vfs = {}
|
||||
kernel.vfs = vfs
|
||||
@@ -113,57 +113,23 @@ end
|
||||
|
||||
local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$"
|
||||
|
||||
local function normalizePath(path)
|
||||
local task = kernel.currentTask
|
||||
local cwd = task.cwd or "/"
|
||||
|
||||
if path:sub(1, 1) ~= "/" then
|
||||
path = cwd .. "/" .. path
|
||||
local function tokenizePath(path)
|
||||
local isAbsolute = (path:sub(1,1) == "/")
|
||||
local tokens = {}
|
||||
for comp in (path .. "/"):gmatch("([^/]*)/") do
|
||||
table.insert(tokens, comp)
|
||||
end
|
||||
return isAbsolute, tokens
|
||||
end
|
||||
|
||||
local stack = {}
|
||||
local i = 1
|
||||
local len = #path
|
||||
while i <= len do
|
||||
local j = path:find("/", i, true)
|
||||
local comp
|
||||
if j then
|
||||
comp = path:sub(i, j - 1)
|
||||
i = j + 1
|
||||
else
|
||||
comp = path:sub(i)
|
||||
i = len + 1
|
||||
end
|
||||
|
||||
comp = comp:match("^%s*(.-)%s*$")
|
||||
|
||||
if comp == "" or comp == "." then
|
||||
elseif comp == ".." then
|
||||
if #stack > 0 then
|
||||
table.remove(stack)
|
||||
end
|
||||
else
|
||||
comp = comp:lower()
|
||||
if not comp:match(SAFE_COMPONENT_PATTERN) then
|
||||
error("EINVAL: illegal characters in path component: " .. comp, 2)
|
||||
end
|
||||
if comp == ".meta" then
|
||||
error("EINVAL: reserved path component: " .. comp, 2)
|
||||
end
|
||||
table.insert(stack, comp)
|
||||
end
|
||||
local function validateComponent(comp)
|
||||
local lower = comp:lower()
|
||||
if not lower:match(SAFE_COMPONENT_PATTERN) then
|
||||
error("EINVAL: illegal characters in path component: " .. comp, 3)
|
||||
end
|
||||
|
||||
local result = "/" .. table.concat(stack, "/")
|
||||
|
||||
local root = task and task.root
|
||||
if root and root ~= "/" then
|
||||
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
|
||||
result = root
|
||||
end
|
||||
if lower == ".meta" then
|
||||
error("EINVAL: reserved path component: .meta", 3)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function vfs.splitPath(path)
|
||||
@@ -222,47 +188,205 @@ local function readMetaEntry(disk, parentDiskPath, filename)
|
||||
end
|
||||
|
||||
local MAX_SYMLINK = 16
|
||||
local function resolveSymlinks(path, noFollow, _depth)
|
||||
_depth = _depth or 0
|
||||
if _depth > MAX_SYMLINK then error("ELOOP") end
|
||||
path = normalizePath(path)
|
||||
|
||||
local parts = {}
|
||||
for p in path:gmatch("[^/]+") do table.insert(parts, p) end
|
||||
local function namei(path, noFollow, symDepth)
|
||||
symDepth = symDepth or 0
|
||||
if symDepth > MAX_SYMLINK then error("ELOOP") end
|
||||
|
||||
local resolved = ""
|
||||
local task = kernel.currentTask
|
||||
local euid = (task and (task.euid or task.uid)) or kernel.uid
|
||||
local groups = (task and task.groups) or kernel.groups or {}
|
||||
local root = (task and task.root) or "/"
|
||||
local cwd = (task and task.cwd) or "/"
|
||||
|
||||
for i, part in ipairs(parts) do
|
||||
local candidate = resolved == "" and ("/" .. part) or (resolved .. "/" .. part)
|
||||
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
|
||||
|
||||
if noFollow and i == #parts then
|
||||
resolved = candidate
|
||||
break
|
||||
end
|
||||
|
||||
local disk, parentDisk = resolveMount(resolved == "" and "/" or resolved)
|
||||
local entry = readMetaEntry(disk, parentDisk, part)
|
||||
|
||||
if entry and entry.etype == 0x01 then
|
||||
local target = entry.cmeta
|
||||
if target:sub(1,1) ~= "/" then
|
||||
target = (resolved == "" and "/" or resolved) .. "/" .. target
|
||||
local function canTraverse(entry)
|
||||
if euid == 0 then return true end
|
||||
if not entry then return true end
|
||||
local bits = entry.perms
|
||||
if euid == entry.owner and bit_is_set(bits, 9) then return true end
|
||||
if entry.group then
|
||||
for _, gid in ipairs(groups) do
|
||||
if gid == entry.group and bit_is_set(bits, 8) then return true end
|
||||
end
|
||||
if i < #parts then
|
||||
target = target .. "/" .. table.concat(parts, "/", i+1, #parts)
|
||||
end
|
||||
return resolveSymlinks(normalizePath(target), noFollow, _depth + 1)
|
||||
end
|
||||
|
||||
resolved = candidate
|
||||
return bit_is_set(bits, 7)
|
||||
end
|
||||
|
||||
if resolved == "" then resolved = "/" end
|
||||
return resolved
|
||||
local isAbsolute, tokens = tokenizePath(path)
|
||||
|
||||
local stack = {}
|
||||
|
||||
if isAbsolute then
|
||||
stack = {}
|
||||
else
|
||||
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
|
||||
end
|
||||
|
||||
local i = 1
|
||||
while i <= #tokens do
|
||||
local comp = tokens[i]
|
||||
i = i + 1
|
||||
|
||||
comp = comp:match("^%s*(.-)%s*$")
|
||||
|
||||
if comp == "" or comp == "." then
|
||||
elseif comp == ".." then
|
||||
local currentPath = "/" .. table.concat(stack, "/")
|
||||
|
||||
local jailStack = {}
|
||||
if root ~= "/" then
|
||||
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
|
||||
end
|
||||
|
||||
if #stack <= #jailStack then
|
||||
stack = {}
|
||||
for _, seg in ipairs(jailStack) do table.insert(stack, seg) end
|
||||
else
|
||||
local exitName = stack[#stack]
|
||||
local parentPath = "/" .. table.concat(stack, "/", 1, #stack - 1)
|
||||
if parentPath == "/" then parentPath = "/" end
|
||||
|
||||
local okM, diskM, dpM = pcall(resolveMount, parentPath == "" and "/" or parentPath)
|
||||
if okM and diskM then
|
||||
local entry = readMetaEntry(diskM, dpM, exitName)
|
||||
if entry then
|
||||
if entry.etype ~= 0x00 then
|
||||
error("ENOTDIR: not a directory: " .. currentPath)
|
||||
end
|
||||
if not canTraverse(entry) then
|
||||
error("EACCES: permission denied traversing " .. currentPath)
|
||||
end
|
||||
else
|
||||
local okD, diskD, dpD = pcall(resolveMount, currentPath)
|
||||
if okD and diskD then
|
||||
local dtype = diskD:type(dpD)
|
||||
if dtype ~= nil and dtype ~= "directory" then
|
||||
error("ENOTDIR: not a directory: " .. currentPath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.remove(stack)
|
||||
end
|
||||
|
||||
else
|
||||
validateComponent(comp)
|
||||
local lname = comp:lower()
|
||||
|
||||
local curPath = "/" .. table.concat(stack, "/")
|
||||
|
||||
local okM, diskM, dpM = pcall(resolveMount, curPath == "/" and "/" or curPath)
|
||||
local entry = nil
|
||||
if okM and diskM then
|
||||
entry = readMetaEntry(diskM, dpM, lname)
|
||||
end
|
||||
|
||||
local isFinal = (i > #tokens)
|
||||
|
||||
if entry and entry.etype == 0x01 then
|
||||
if isFinal and noFollow then
|
||||
table.insert(stack, lname)
|
||||
else
|
||||
symDepth = symDepth + 1
|
||||
if symDepth > MAX_SYMLINK then error("ELOOP") end
|
||||
|
||||
local target = entry.cmeta
|
||||
if not target or target == "" then
|
||||
error("ENOENT: empty symlink target")
|
||||
end
|
||||
|
||||
local symIsAbs, symTokens = tokenizePath(target)
|
||||
|
||||
if symIsAbs then
|
||||
stack = {}
|
||||
if root ~= "/" then
|
||||
for seg in root:gmatch("[^/]+") do table.insert(stack, seg) end
|
||||
end
|
||||
end
|
||||
|
||||
local fresh = {}
|
||||
for j = 1, i - 2 do table.insert(fresh, tokens[j]) end
|
||||
local insertAt = #fresh + 1
|
||||
for _, t in ipairs(symTokens) do table.insert(fresh, t) end
|
||||
for j = i, #tokens do table.insert(fresh, tokens[j]) end
|
||||
tokens = fresh
|
||||
i = insertAt
|
||||
end
|
||||
else
|
||||
table.insert(stack, lname)
|
||||
|
||||
if not isFinal then
|
||||
local newPath = "/" .. table.concat(stack, "/")
|
||||
local okD, diskD, dpD = pcall(resolveMount, newPath)
|
||||
if okD and diskD then
|
||||
local dtype = diskD:type(dpD)
|
||||
if dtype ~= nil and dtype ~= "directory" then
|
||||
error("ENOTDIR: not a directory: " .. newPath)
|
||||
end
|
||||
end
|
||||
if not canTraverse(entry) then
|
||||
error("EACCES: permission denied traversing " .. newPath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local result = "/" .. table.concat(stack, "/")
|
||||
|
||||
if root ~= "/" then
|
||||
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
|
||||
result = root
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local function normalizePath(path)
|
||||
local task = kernel.currentTask
|
||||
local cwd = (task and task.cwd) or "/"
|
||||
local root = (task and task.root) or "/"
|
||||
if root ~= "/" and root:sub(-1) == "/" then root = root:sub(1,-2) end
|
||||
|
||||
local isAbsolute, tokens = tokenizePath(path)
|
||||
local stack = {}
|
||||
|
||||
if not isAbsolute then
|
||||
for seg in cwd:gmatch("[^/]+") do table.insert(stack, seg) end
|
||||
end
|
||||
|
||||
local jailStack = {}
|
||||
if root ~= "/" then
|
||||
for seg in root:gmatch("[^/]+") do table.insert(jailStack, seg) end
|
||||
end
|
||||
|
||||
for _, comp in ipairs(tokens) do
|
||||
comp = comp:match("^%s*(.-)%s*$")
|
||||
if comp == "" or comp == "." then
|
||||
elseif comp == ".." then
|
||||
if #stack > #jailStack then
|
||||
table.remove(stack)
|
||||
end
|
||||
else
|
||||
table.insert(stack, comp:lower())
|
||||
end
|
||||
end
|
||||
|
||||
local result = "/" .. table.concat(stack, "/")
|
||||
if root ~= "/" then
|
||||
if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then
|
||||
result = root
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function resolvePath(path, noFollow)
|
||||
local real = resolveSymlinks(path, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
local disk, diskPath = resolveMount(real)
|
||||
if kernel.config.logPathResolution then
|
||||
kernel.log("resolvePath '"..path.."' -> '"..real.."' diskPath '"..diskPath.."'")
|
||||
@@ -271,24 +395,23 @@ local function resolvePath(path, noFollow)
|
||||
end
|
||||
|
||||
local function getFileMeta(path, noFollow)
|
||||
local real = resolveSymlinks(path, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
|
||||
local parts = {}
|
||||
for p in real:gmatch("[^/]+") do table.insert(parts, p) end
|
||||
if real == "/" then
|
||||
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
|
||||
end
|
||||
|
||||
local default = { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
|
||||
if #parts == 0 then return default end
|
||||
local parent, name = real:match("^(.*)/([^/]+)$")
|
||||
if not parent or parent == "" then parent = "/" end
|
||||
|
||||
local parentNorm = "/" .. table.concat(parts, "/", 1, #parts - 1)
|
||||
if parentNorm == "" then parentNorm = "/" end
|
||||
local disk, parentDiskPath = resolveMount(parentNorm)
|
||||
local entry = readMetaEntry(disk, parentDiskPath, parts[#parts])
|
||||
local disk, parentDiskPath = resolveMount(parent)
|
||||
local entry = readMetaEntry(disk, parentDiskPath, name)
|
||||
if entry then return entry end
|
||||
return default
|
||||
return { etype = 0x00, owner = 0, group = 0, perms = 63, cmeta = "" }
|
||||
end
|
||||
|
||||
local function writeMetaEntry(path, name, entry, noFollow)
|
||||
local real = resolveSymlinks(path, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
local disk, diskPath = resolveMount(real)
|
||||
|
||||
local mp
|
||||
@@ -668,7 +791,7 @@ function vfs.mkdir(path)
|
||||
end
|
||||
|
||||
function vfs.remove(path)
|
||||
local norm = resolveSymlinks(path, true)
|
||||
local norm = namei(path, true)
|
||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||
if parent == "" then parent = "/" end
|
||||
local parentMeta = getFileMeta(parent)
|
||||
@@ -681,7 +804,7 @@ function vfs.remove(path)
|
||||
end
|
||||
|
||||
if meta.etype == 0x01 then
|
||||
local norm = resolveSymlinks(path, true)
|
||||
local norm = namei(path, true)
|
||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||
if parent == "" then parent = "/" end
|
||||
local name = norm:match("[^/]+$")
|
||||
@@ -747,7 +870,7 @@ function vfs.access(path, mode)
|
||||
end
|
||||
|
||||
local function updateMeta(path, fn, noFollow)
|
||||
local real = resolveSymlinks(path, noFollow)
|
||||
local real = namei(path, noFollow)
|
||||
local norm = real
|
||||
local parent = norm:match("^(.*)/[^/]+$") or "/"
|
||||
if parent == "" then parent = "/" end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
local cache = {}
|
||||
kernel.searchpaths = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local proxy = {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
-- Loop device driver:
|
||||
--
|
||||
-- BIND (directory) - re-routes VFS calls into a host directory subtree.
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
---- :Minify:--
|
||||
--local kernel = ...
|
||||
--
|
||||
--local timeout = false
|
||||
--kernel.processes.keventd = function()
|
||||
-- while true do
|
||||
-- local event = {kernel.computer:getMachineEvent()}
|
||||
-- if event[1] then
|
||||
-- if event[1] == "keyTyped" then
|
||||
-- if event[3] == "\x1b^s" then
|
||||
-- kernel.shutdown()
|
||||
-- elseif event[3] == "\x1b^r" then
|
||||
-- kernel.reboot()
|
||||
-- end
|
||||
-- end
|
||||
-- timeout = false
|
||||
-- else
|
||||
-- timeout = true
|
||||
-- end
|
||||
-- if timeout then sleep(.05) end
|
||||
-- end
|
||||
--end
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
-- Supports:
|
||||
-- AF_UNIX - local IPC via /var/run/*.sock paths
|
||||
-- AF_INET - network sockets with three backends:
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
-- :Minify:--
|
||||
local kernel = ...
|
||||
local apis = kernel.apis
|
||||
local native = apis.peripheral
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
local peripheral={}
|
||||
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
local function write(text, term)
|
||||
local x, y = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
for i = 1, #text do
|
||||
local c = text:sub(i, i)
|
||||
|
||||
if c == "\n" then
|
||||
y = y + 1
|
||||
x = 1
|
||||
elseif c == "\t" then
|
||||
local tabSize = 4
|
||||
local spaces = tabSize - ((x - 1) % tabSize)
|
||||
term.write(string.rep(" ", spaces))
|
||||
x = x + spaces
|
||||
elseif c == "\b" then
|
||||
if x > 1 then
|
||||
x = x - 1
|
||||
term.setCursorPos(x, y)
|
||||
term.write(" ")
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
else
|
||||
if x <= w and y <= h then
|
||||
term.setCursorPos(x, y)
|
||||
term.write(c)
|
||||
x = x + 1
|
||||
end
|
||||
end
|
||||
|
||||
if x > w then
|
||||
x = 1
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
if y - 1 >= h then
|
||||
term.scroll(1)
|
||||
y = h
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(x, y)
|
||||
end
|
||||
|
||||
kernel.devfs.data.tty={}
|
||||
local ctrl,alt = false, false
|
||||
|
||||
local function serializeBool(bool)
|
||||
if bool then
|
||||
return "T"
|
||||
else
|
||||
return "F"
|
||||
end
|
||||
end
|
||||
|
||||
local function newtty(obj, id, ev)
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
local rv=""
|
||||
for i=1, amount or 1 do
|
||||
local event = {ev()}
|
||||
if event[1] then
|
||||
rv=rv..event[1]
|
||||
end
|
||||
end
|
||||
if rv=="" then rv=nil end
|
||||
return rv
|
||||
end,
|
||||
write=function(content)
|
||||
write(content, obj)
|
||||
end,
|
||||
size=function()
|
||||
local s={obj.getSize()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
clear=function()
|
||||
obj.clear()
|
||||
obj.setCursorPos(1,1)
|
||||
end,
|
||||
gpos=function()
|
||||
local s={obj.getCursorPos()}
|
||||
return table.concat(s,";")
|
||||
end,
|
||||
spos=function(x,y)
|
||||
return obj.setCursorPos(x,y)
|
||||
end,
|
||||
sfgc=function(c)
|
||||
return obj.setTextColor(colors[c])
|
||||
end,
|
||||
sbgc=function(c)
|
||||
return obj.setBackgroundColor(colors[c])
|
||||
end,
|
||||
gfgc=function()
|
||||
return icolors[obj.getTextColor()]
|
||||
end,
|
||||
gbgc=function()
|
||||
return icolors[obj.getBackgroundColor()]
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(ctrl)..";"..serializeBool(alt)
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
return h
|
||||
elseif mode=="r" then
|
||||
h["write"]=nil
|
||||
return h
|
||||
elseif mode=="w" then
|
||||
h["read"]=nil
|
||||
return h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fifo = kernel.newFifo()
|
||||
|
||||
local ctrlLetterKeys = nil
|
||||
local specialKeys = nil
|
||||
|
||||
local function buildKeyMaps()
|
||||
if ctrlLetterKeys then return end
|
||||
local k = apis.keys
|
||||
ctrlLetterKeys = {}
|
||||
local letters = {
|
||||
{k.a,1},{k.b,2},{k.c,3},{k.d,4},{k.e,5},{k.f,6},{k.g,7},
|
||||
{k.h,8}, {k.j,10},{k.k,11},{k.l,12},{k.m,13},
|
||||
{k.n,14},{k.o,15},{k.p,16},
|
||||
{k.u,21},{k.v,22},{k.w,23},{k.x,24},{k.y,25},{k.z,26},
|
||||
}
|
||||
for _, pair in ipairs(letters) do
|
||||
ctrlLetterKeys[pair[1]] = string.char(pair[2])
|
||||
end
|
||||
specialKeys = {
|
||||
[k.home] = "\1",
|
||||
[k.delete] = "\4",
|
||||
[k["end"]] = "\5",
|
||||
[k.pageUp] = "\2",
|
||||
[k.pageDown]= "\12",
|
||||
}
|
||||
end
|
||||
|
||||
kernel.processes.cctmond = function()
|
||||
local timeout = false
|
||||
while true do
|
||||
local event = {kernel.computer:getMachineEvent()}
|
||||
|
||||
if event[1] then
|
||||
local eventType = event[1]
|
||||
local charOrKey = event[3]
|
||||
|
||||
buildKeyMaps()
|
||||
|
||||
if eventType == "keyPressed" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = true
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = true
|
||||
end
|
||||
|
||||
if ctrl and charOrKey == apis.keys.c then
|
||||
for _, task in ipairs(syscall.getTasks()) do
|
||||
syscall.sigsend(task, 1)
|
||||
end
|
||||
end
|
||||
|
||||
if ctrl and ctrlLetterKeys[charOrKey] then
|
||||
fifo.push(ctrlLetterKeys[charOrKey])
|
||||
end
|
||||
|
||||
if specialKeys[charOrKey] then
|
||||
fifo.push(specialKeys[charOrKey])
|
||||
end
|
||||
elseif eventType == "keyReleased" then
|
||||
if charOrKey == apis.keys.leftCtrl or charOrKey == apis.keys.rightCtrl then
|
||||
ctrl = false
|
||||
elseif charOrKey == apis.keys.leftAlt or charOrKey == apis.keys.rightAlt then
|
||||
alt = false
|
||||
end
|
||||
elseif eventType == "keyTyped" then
|
||||
if charOrKey then fifo.push(charOrKey) end
|
||||
end
|
||||
|
||||
timeout = false
|
||||
else
|
||||
timeout = true
|
||||
end
|
||||
|
||||
if timeout then
|
||||
sleep(0.05)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newtty(apis.term, "tty1", fifo.pop)
|
||||
|
||||
|
||||
for i,v in ipairs({peripheral.find("monitor")}) do
|
||||
v.setTextScale(.5)
|
||||
v.write("Initializing...")
|
||||
newtty(v,"tty"..tostring(i+1),function () end)
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local args = {...}
|
||||
local kernel = args[1]
|
||||
kernel._G = _G
|
||||
@@ -55,3 +55,19 @@ local origLoad = load
|
||||
kernel._U = readonly(kernel._G)
|
||||
kernel._U._G = kernel._U
|
||||
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
|
||||
|
||||
function kernel.freshUserEnv()
|
||||
local locals = {}
|
||||
locals.syscall = _makeSyscallProxy()
|
||||
|
||||
local env = setmetatable(locals, {
|
||||
__index = kernel._U,
|
||||
__newindex = function(_, k, v) rawset(locals, k, v) end,
|
||||
__metatable=false
|
||||
})
|
||||
|
||||
locals._G = env
|
||||
locals.load = function(a, b, c, d) return origLoad(a, b, c, d or env) end
|
||||
|
||||
return env
|
||||
end
|
||||
|
||||
@@ -11,14 +11,16 @@ local function bit_is_set(num, bit)
|
||||
return math.floor(num / (2 ^ bit)) % 2 == 1
|
||||
end
|
||||
|
||||
local function loadExecutable(path, env)
|
||||
local function loadExecutable(path)
|
||||
kernel.vfs.access(path, "rx")
|
||||
|
||||
local fd = kernel.vfs.open(path, "r")
|
||||
local data = kernel.vfs.read(fd, 1024 * 1024 * 4)
|
||||
kernel.vfs.close(fd)
|
||||
|
||||
local func, err = load(data, "@" .. path, "t", env or kernel._U)
|
||||
local env = kernel.freshUserEnv()
|
||||
|
||||
local func, err = load(data, "@" .. path, "t", env)
|
||||
if not func then error("ENOEXEC: " .. tostring(err)) end
|
||||
|
||||
local meta = kernel.vfs.lstat(path)
|
||||
@@ -92,7 +94,7 @@ local function createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||
end
|
||||
|
||||
function sys.spawn(func, name, envars, args, tgid)
|
||||
local caller = kernel.currentTask
|
||||
local caller = kernel.currentTask
|
||||
local real_uid = caller and caller.uid or kernel.uid
|
||||
local eff_uid = caller and caller.euid or real_uid
|
||||
return createTask(func, name, envars, args, tgid, real_uid, eff_uid)
|
||||
@@ -357,10 +359,25 @@ function kernel.main()
|
||||
|
||||
if task.sigq and #task.sigq ~= 0 and task.sigh then
|
||||
local coro = coroutine.create(task.sigh)
|
||||
if kernel.config.preempt then
|
||||
resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1))
|
||||
else
|
||||
coroutine.resume(coro, table.remove(task.sigq, 1))
|
||||
local sigret = { coroutine.resume(coro, table.remove(task.sigq, 1)) }
|
||||
while coroutine.status(coro) ~= "dead" do
|
||||
if sigret[1] == false then break end
|
||||
if sigret[2] == "syscall" then
|
||||
local scname = sigret[3]
|
||||
local sysret
|
||||
if kernel.syscalls[scname] then
|
||||
sysret = { xpcall(kernel.syscalls[scname], debug.traceback, table.unpack(sigret, 4)) }
|
||||
else
|
||||
sysret = { false, "Unknown syscall: " .. tostring(scname) }
|
||||
end
|
||||
if not sysret[1] then
|
||||
sigret = { coroutine.resume(coro, false, sysret[2]) }
|
||||
else
|
||||
sigret = { coroutine.resume(coro, true, table.unpack(sysret, 2)) }
|
||||
end
|
||||
else
|
||||
sigret = { coroutine.resume(coro) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local sysc=kernel.syscalls
|
||||
kernel.gpio={}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
function print(...)
|
||||
local args = {...}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
kernel.log("Loading init system...")
|
||||
kernel.log("InitPath: " .. kernel.config.initPath)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
kernel.processes.login = function()
|
||||
local ok, err = pcall(syscall.execspawn, "/bin/login", "login")
|
||||
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
|
||||
if not ok then
|
||||
kernel.log("Failed to exec /bin/login: " .. tostring(err), "ERROR", 2)
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
|
||||
local P = kernel.vfs.P
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
kernel.allowGlobalOverwrites = false
|
||||
@@ -0,0 +1,6 @@
|
||||
local args={...}
|
||||
|
||||
local json=require("json")
|
||||
local http=require("http")
|
||||
local rootRepo="https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/spm/spm.src"
|
||||
|
||||
|
||||
15
build.py
15
build.py
@@ -72,7 +72,7 @@ def process_root(src_root: Path, out_root: Path, minify: bool):
|
||||
if minify and has_minify_header(src):
|
||||
print(" > Minifying")
|
||||
result = subprocess.run(
|
||||
["luamin", "-f", str(src)],
|
||||
["luamin.cmd", "-f", str(src)],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
@@ -88,17 +88,8 @@ def process_root(src_root: Path, out_root: Path, minify: bool):
|
||||
|
||||
def install_bootloader(arch: str, release: bool):
|
||||
boot_dir = BUILD_ROOT / "$" / ARCH_BOOT_DIR[arch]
|
||||
boot_lua = boot_dir / "boot.lua"
|
||||
eeprom = boot_dir / "eeprom"
|
||||
|
||||
for src in (boot_lua, eeprom):
|
||||
if not src.exists():
|
||||
print(f" ! Bootloader file not found: {src}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f" Installing: boot.lua -> Build/boot.lua")
|
||||
shutil.copy2(boot_lua, BUILD_ROOT / "boot.lua")
|
||||
|
||||
eeprom_dst_name = "startup.lua" if release else "eeprom"
|
||||
print(f" Installing: eeprom -> Build/{eeprom_dst_name}")
|
||||
shutil.copy2(eeprom, BUILD_ROOT / eeprom_dst_name)
|
||||
@@ -203,7 +194,7 @@ def _make_firstboot_kmod(users):
|
||||
|
||||
lines.append("do")
|
||||
lines.append(" local ok, err = pcall(function()")
|
||||
lines.append(" kernel.vfs.remove('/lib/modules/hyperion/50_firstboot_users.kmod')")
|
||||
lines.append(" kernel.vfs.remove('/lib/modules/Hyperion/50_firstboot_users.kmod')")
|
||||
lines.append(" end)")
|
||||
lines.append(" if not ok then")
|
||||
lines.append(" kernel.log('FIRSTBOOT: could not self-delete: ' .. tostring(err), 'WARN')")
|
||||
@@ -215,7 +206,7 @@ def _make_firstboot_kmod(users):
|
||||
|
||||
def inject_makeusers(users, arch):
|
||||
base = BUILD_ROOT / "$" if arch else BUILD_ROOT
|
||||
kmod_path = base / "lib" / "modules" / "hyperion" / "50_firstboot_users.kmod"
|
||||
kmod_path = base / "lib" / "modules" / "Hyperion" / "50_firstboot_users.kmod"
|
||||
kmod_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
kmod_path.write_text(_make_firstboot_kmod(users), encoding="utf-8")
|
||||
print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT)))
|
||||
|
||||
Reference in New Issue
Block a user