OC-Tsuki/ksrc/kio.lua

299 lines
6.9 KiB
Lua

@[[if not svar.get("PRINT_DMESG_LEVEL") then
svar.set("PRINT_DMESG_LEVEL", "1")
end]]
---@module kio "Kernel I/O"
kio = {}
local _stream = {}
function _stream:read(amt)
local buf = ""
local lc = ""
if (amt == "*l") then -- Our line ending is \n
repeat
lc = self.proto.read(self.udat, 1)
if (lc ~= "\n") then
buf = buf .. (lc or "")
end
until not lc or lc == "" or lc == "\n"
return buf
--elseif (amt == "*n") then
elseif (amt == "*a") then
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
amt = send - pos
end
return self.proto.read(self.udat, amt)
end
function _stream:write(data)
return self.proto.write(self.udat, data)
end
function _stream:seek(whence, amt)
if not amt then
amt = whence or 0
whence = "cur"
end
return self.proto.seek(self.udat, whence, amt)
end
function _stream:eof()
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
return pos == send
end
function _stream:close()
return self.proto.close(self.udat)
end
function _stream:size()
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
return send
end
function kio.create_stream(udat, proto)
return setmetatable({udat=udat, proto=proto}, {__index=_stream})
end
@[[local kio_errc = 0
function kio_err(name, rtn)]]
kio.errno["@[{name}]"] = @[{kio_errc}]
kio_errors[@[{kio_errc}]] = "@[{rtn}]"
@[[kio_errc = kio_errc + 1
end]]
kio.errno = {}
local kio_errors = {}
@[[
kio_err("FILE_NOT_FOUND", "not found")
kio_err("FILE_DIRECTORY", "file is a directory")
kio_err("DEV_TIMEOUT", "device timeout")
kio_err("IO_ERROR", "generic i/o error")
kio_err("UNSUPPORTED_OPERATION", "unsupported operation")
kio_err("NOT_ALLOWED", "not allowed")
kio_err("TOO_MANY_SYMLINKS", "too many symlinks")
kio_err("DEV_FULL", "device is full")
kio_err("DEV_READ_ONLY", "device is read only")
]]
kio.geterror = function(e)
return nil, kio_errors[e] or "generic error"
end
function kio.invoke(path, method, ...)
local dev, rpath = vfs.resolve(path)
return dev.dev[method](dev.dev, rpath, ...)
end
function kio.has_ads(path)
end
function kio.ads_exists(path, ads)
end
function kio.has_acl(path)
end
---@func calc_seek
---@arg whence string "Like io.seek's whence."
---@arg amt integer "The amount to seek."
---@arg size integer "The size of the stream."
---@arg pos integer "The current position."
---@return integer "The new position"
---@desc "This function calculates the new position for seeking."
function kio.calc_seek(whence, amt, size, pos)
if (whence == "cur") then
pos = pos + amt
elseif (whence == "end") then
pos = size + amt
elseif (whence == "set") then
pos = amt
end
if pos > size then
pos = size
elseif pos < 1 then
pos = 1
end
return pos
end
---@func filestream
---@arg path string "Path to the file"
---@arg mode string "File mode"
---@return stream "The stream for the file."
---@desc "This creates a stream from a file."
function kio.filestream(path, mode)
local dev, rpath = vfs.resolve(path)
local h = dev:open(rpath, mode)
local stat = dev:stat(rpath)
return kio.create_stream({dev=dev, hand=h, stat=stat}, {
read = function(self, amt)
return self.dev:read(self.hand, amt)
end,
seek = function(self, whence, amt)
local pos = self.dev:seek(self.hand, 0)
local npos = kio.calc_seek(whence, amt, self.stat.size, pos)
return self.dev:seek(self.hand, npos-pos)
end,
write = function(self, data)
return self.dev:write(self.hand, data)
end,
close = function(self)
return self.dev:close(self.hand)
end
})
end
---@func dir
---@arg udat table "The data to pass to to prototype"
---@arg proto function "The prototype function for the iterator"
---@return function "The iterator."
---@desc "Creates a directory iterator."
function kio.dir(udat, proto)
return function()
return proto(udat)
end
end
---@func memstream
---@arg str string "The string to create a stream of."
---@arg write boolean "If the stream should be writable or not."
---@return stream "The memory stream."
---@desc "Creates a memory stream."
---@note "stream:close() returns the string."
function kio.memstream(str, write)
return kio.create_stream({str=str, pos=1, write=write}, {
read = function(self, amt)
local dat = self.str:sub(self.pos, self.pos+amt-1)
self.pos = self.pos+#dat
return dat
end,
write = function(self, dat)
if (write) then
local startstr = self.str:sub(1, self.pos-1)
local endstr = self.str:sub(self.pos+#dat)
self.str = startstr..dat..endstr
self.pos = self.pos + #dat
end
end,
seek = function(self, whence, amt)
self.pos = kio.calc_seek(whence, amt, #self.str, self.pos)
return self.pos
end,
close = function(self)
return self.str
end
})
end
kio.loglevel = {
DEBUG = 0,
INFO = 1,
WARNING = 2,
ERROR = 3,
PANIC = 255
}
kio.levels = {
[0] = "DEBUG",
"INFO",
"WARNING",
"ERROR",
[255] = "PANIC"
}
---@func dprint
---@arg level integer "The log level"
---@arg status string "The message."
---@desc "This method logs to the kernel log and wherever else is set up to be printed to."
function kio.dprint(level, status)
local stack = {}
local lkexec = {}
local i = 0
while lkexec do
lkexec = debug.getinfo(i)
stack[#stack+1] = lkexec
end
local src = stack[#stack].source:sub(2)
local spart = vfs.parts(src)
local exec = spart[#spart]:match("^(.+)%.([^%.]+)$")
local message = string.format("[%.2f] [%s] [%s] %s", computer.uptime(), levels[level], exec, status)
dmesgs[#dmesgs+1] = {ut=computer.uptime,lvl=level,x=exec,st=status}
if (level < $[{PRINT_DMESG_LEVEL}]) then
sig.push {
type = "dprint",
msg = message
}
end
end
---@func dmesg
---@see kio.dprint
kio.dmesg = dprint
---@func panic
---@arg msg string "The error message to display."
function kio.panic(msg)
dmesg(255, "Kernel panic!")
end
---@func create_pipe
---@return stream "Pipe in"
---@return stream "Pipe out"
---@desc "Creates a pipe."
function kio.create_pipe()
local pipecore = kio.memstream()
local pipein = kio.create_stream({
dat = pipecore
}, {
read = function()
-- no
end,
seek = function()
-- no
end,
write = function(self, dat)
self.dat:seek("end", 0)
self.dat:write(dat)
end,
close = function()
-- *no*
end
})
local pipeout = kio.create_stream({
dat = pipecore,
pos = 1
}, {
read = function(self, amt)
self.dat:seek("set", self.pos)
local dat = self.dat:read()
self.pos = self.pos+#dat
return dat
end,
seek = function()end,
write = function()end,
close = function()end
})
return pipein, pipeout
end
---@func init
---@desc "Only called once. Sets up the kio library."
function kio.init()
---@func create_buffer
---@arg blocking boolean "True if read calls to a buffer that doesn't contain enough data block."
---@arg pid integer "This is set to the PID of the process which handles the buffers."
---@return table "The buffer for use anywhere."
kio.create_buffer = create_buffer
kio.init = false
end