@[[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