diff --git a/src/kernel/00_preamble.lua b/src/kernel/00_preamble.lua index 379f036..7fc5e01 100644 --- a/src/kernel/00_preamble.lua +++ b/src/kernel/00_preamble.lua @@ -2,16 +2,17 @@ local bootfs = ... -- ...but [lua bios, openloader] don't, so fall back to the usual way bootfs = bootfs or computer.getBootAddress() +local tmpfs = computer.tmpAddress() --store all functions we need in locals so userspace mischief can't replace them --prefix 'k' (for kernel) on any functions userspace will also have a version of to ensure kernelspace --function usage is intentional (ie prevent accidentally using the kernel's `load` in a userspace `loadfile`) local envBase = _G --used for creating process environments local assert, checkArg, error, kload, ipairs, next, pairs, rawequal, rawset, setmetatable, tostring, type = assert, checkArg, error, load, ipairs, next, pairs, rawequal, rawset, setmetatable, tostring, type -local invoke = component.invoke +local invoke, component_type = component.invoke, component.type local kpullSignal, shutdown, uptime = computer.pullSignal, computer.shutdown, computer.uptime local co_create, co_status, kresume, kyield = coroutine.create, coroutine.status, coroutine.resume, coroutine.yield -local huge, math_type = math.huge, math.type +local huge, math_type, random = math.huge, math.type, math.random local find, format, match, sub = string.find, string.format, string.match, string.sub local concat, pack, unpack = table.concat, table.pack, table.unpack diff --git a/src/kernel/02_io.lua b/src/kernel/02_io.lua index 771dc03..e8b4c35 100644 --- a/src/kernel/02_io.lua +++ b/src/kernel/02_io.lua @@ -14,14 +14,18 @@ do end local function wrapFile(mode, fh) - local rwa, bin, plus, b2 = match(mode or 'r', '^([rwa])(b?)(%+?)(b?)$') --match all of x, xb, x+, xb+, x+b - if not rwa or (bin ~= '' and b2 ~= '') then return nil, 'bad mode' end - bin = bin .. b2 --when (bin == '') xor (b2 == ''), this is logical or + local rwa, b, plus, b2 = match(mode or 'r', '^([rwa])(b?)(%+?)(b?)$') --match all of x, xb, x+, xb+, x+b + if not rwa or (b ~= '' and b2 ~= '') then return nil, 'bad mode' end local read = rwa == 'r' or plus local write = rwa ~= 'r' or plus local append = rwa == 'a' local update = plus ~= '' + local bin = (b .. b2) ~= '' + + --TODO truncate if w+ + --TODO restrict seeking on a + --TODO restrict seek to end before write on a+ local file = setmetatable({}, filemt) filedata[file] = fh @@ -50,6 +54,7 @@ do local function path_seek(fh, ...) return invoke(fh.address, 'seek', fh.handle, ...) end function openPath(mode, addr, path) + --TODO do filesystem components support 'a' modes? local fd, err = invoke(addr, 'open', path, mode) --should we validate `mode` first? if not fd then return nil, err @@ -132,9 +137,7 @@ do if wbuf ~= '' then return flush(fh) end elseif mode == 'full' then local cap = fh._file_wcap or 512 --TODO tune default capacity to e.g. free memory - if #fh._file_wbuf >= cap then - return flush(fh) - end + if #fh._file_wbuf >= cap then return flush(fh) end else --for line buffering, only flush complete lines local i1, i2 = find(fh._file_wbuf, '.+[\r\n]') --`i2` is the last cr or lf in the buffer if i1 then --if there are any newlines @@ -143,10 +146,10 @@ do local ok, err = flush(fh) if ok then fh._file_wbuf = rest end return ok, err - else --no newlines, nothing to flush - return true end end + --never hit any of the branches that `return flush(fh)`ed, so we've successfully written to the buffer + return true end @@ -258,7 +261,7 @@ do local tell = (whence == 'cur' or not whence) and (offset == 0 or not offset) --true if we didn't move ie ftell() --after seeking, if we moved, invalidate the read buffer - if (whence and whence ~= cur) or (offset and offset ~= 0) then + if (whence and whence ~= 'cur') or (offset and offset ~= 0) then fh._file_rbuf = fh._file_rbuf and '' end diff --git a/src/kernel/04_environment.lua b/src/kernel/04_environment.lua index 0b9956e..744637c 100644 --- a/src/kernel/04_environment.lua +++ b/src/kernel/04_environment.lua @@ -9,7 +9,7 @@ given : ]] -envBase._OSVERSION = 'kitn 0.2.0-beta.1' --prefer [openloader, openos] "$name $ver" over plan9k "$name/$ver" +envBase._OSVERSION = 'kitn 0.2.0-beta.2' --prefer [openloader, openos] "$name $ver" over plan9k "$name/$ver" envBase.kitn = {} envBase._G, envBase.load = nil @@ -100,7 +100,11 @@ function fillEnv(proc, uenv) checkArg(1, filename, 'string', 'nil') error('TODO io.lines') --iff we open a file, we need to close it end, - open = function(filename, mode) return openPath(mode, bootfs, filename) end, + open = function(filename, mode) + checkArg(1, filename, 'string') + checkArg(2, filename, 'string', 'nil') + return openPath(mode, bootfs, filename) + end, output = function(file) local t = checkArgEx(1, file, 'string', 'table', 'nil') if t ~= nil then @@ -111,8 +115,14 @@ function fillEnv(proc, uenv) --popen is unimplemented because we don't have a notion of a shell read = function(...) return uio.stdin:read(...) end, tmpfile = function() - --use math.random() to pick a filename on computer.tmpAddress() that doesn't exist yet and create it - error('TODO io.tmpfile') --file is deleted on process exit, requires atExit mechanism + local deadline = uptime() + 1 --give up after 1s instead of potentially infinitely looping + local filename + repeat + filename = format('tmp.%08x', random(0, 0x7fffffff)) + if invoke(tmpfs, 'exists', filename) then filename = nil end + until filename or uptime() >= deadline + assert(filename, 'failed to allocate tmpfile') + return openPath('w+', tmpfs, filename) --TODO close at exit end, type = function(obj) local file, opened = isFile(obj) @@ -128,6 +138,14 @@ function fillEnv(proc, uenv) } uenv.io = uio + function uenv.print(...) + local parts = pack(...) + for i = 1, parts.n do + parts[i] = tostring(parts[i]) + end + assert(uenv.io.write(concat(parts, '\t', 1, parts.n), '\n')) + end + --env will automatically mirror envBase.kitn here local ukitn = uenv.kitn @@ -154,6 +172,18 @@ function fillEnv(proc, uenv) --TODO return information for the current thread within the process too return proc, nil end + + --TODO map all filesystems into a single tree, then remove this method + function ukitn.openOn(address, path, mode) + checkArg(1, address, 'string') + checkArg(2, path, 'string') + checkArg(3, mode, 'string', 'nil') + local t, err = component_type(address) + if t ~= 'filesystem' then + return nil, err or 'component is not a filesystem' + end + return openPath(mode, address, path) + end end -- vi: set ts=2: