Compare commits

..

No commits in common. "56af6d1ade673edf13a8121dd6558f07ed4d0aa6" and "e09650276a19bc3b02442fcdbfaab0c474368862" have entirely different histories.

28 changed files with 437 additions and 238 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
apidoc.md
apidoc.html
*.cpio *.cpio
*.af *.af
/target /target
/doc

View File

@ -1,14 +1,11 @@
#!/bin/bash #!/bin/bash
LUA=${LUA:-lua} LUA=${LUA:-lua}
rm -r target/* rm -r target/*
mkdir -p target/doc &>/dev/null mkdir target &>/dev/null
$LUA luapreproc.lua module/init.lua target/init.lua $LUA luapreproc.lua module/init.lua target/init.lua
echo _OSVERSION=\"PsychOS 2.0a2-$(git rev-parse --short HEAD)\" > target/version.lua echo _OSVERSION=\"PsychOS 2.0a2-$(git rev-parse --short HEAD)\" > target/version.lua
cat target/version.lua target/init.lua > target/tinit.lua cat target/version.lua target/init.lua > target/tinit.lua
mv target/tinit.lua target/init.lua mv target/tinit.lua target/init.lua
cp -r service/ lib/ cfg/ target/ cp -r service/ lib/ cfg/ target/
$LUA finddesc.lua $(find module/ -type f) $(find lib/ -type f) > apidoc.md
rm target/version.lua rm target/version.lua
rm -r doc/ &>/dev/null
$LUA finddesc.lua doc/ $(find lib/ module/ -type f|sort)
$LUA gendoc.lua target/doc/kernel.dict $(find module/ -type f|sort)
pandoc doc/apidoc.md docs-metadata.yml --template=template.tex -o doc/apidoc.pdf

View File

@ -1,3 +0,0 @@
---
title: 'PsychOS API Documentation'
---

View File

@ -1,60 +1,27 @@
#!/usr/bin/env lua
local doc = require "lib/doc"
local tA = {...} local tA = {...}
local docfiles = {}
local outpath = table.remove(tA,1) for _,file in pairs(tA) do
docfiles[file] = {}
print(outpath) local f = io.open(file)
local lines = {}
local function formatDocs(fd) for l in f:read("*a"):gmatch("[^\n]+") do
local rs = "" if l:find("function") and not l:find("local") then
for name,finfo in pairs(fd) do lines[#lines+1] = l
if rs:len() > 0 then
rs = rs .. "\n\n"
end end
local as = ""
for k,v in pairs(finfo.args) do
if k > 1 then
as = as .. ", "
end
as = as .. v[1]
if v[2] then
as = as .. "^"..v[2].."^"
end
end
local rt = ""
for k,v in pairs(finfo.outtypes or {}) do
if rt:len() > 0 then
rt = rt .. ", "
else
rt = ": "
end
rt = rt .. v
end
rs = string.format("%s## %s(%s)%s\n%s",rs,name,as,rt,finfo.description)
end end
return rs for k,v in pairs(lines) do
end local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)")
if name and args and desc then
os.execute("sh -c 'mkdir -p "..outpath .. "'") docfiles[file][#docfiles[file]+1] = string.format("## %s(%s)\n%s\n",name,args,desc)
if outpath:sub(#outpath) == "/" then outpath = outpath:sub(1, #outpath - 1) end end
local ad = io.open(outpath.."/apidoc.md","w")
for k,v in pairs(tA) do
local fd = doc.parsefile(v)
local ds = formatDocs(fd)
print(string.format("%s: %i",v,ds:len()))
if ds and ds:len() > 0 then
os.execute("sh -c 'mkdir -p $(dirname \""..outpath.."/"..v.."\")'")
local f = io.open(outpath.."/"..v:gsub("%.lua$",".md"),"wb")
f:write(string.format("# %s\n\n",v))
f:write(ds)
f:write("\n\n")
f:close()
ad:write(string.format("# %s\n\n",v))
ad:write(ds)
ad:write("\n\n")
end end
end end
ad:close() for k,v in pairs(docfiles) do
if #v > 0 then
print("\n# "..k)
for l,m in pairs(v) do
print(m)
end
end
end

View File

@ -1,51 +0,0 @@
#!/usr/bin/env lua
local doc = require "lib/doc"
local ser = require "lib/serialization"
local tA = {...}
local outpath = table.remove(tA,1)
print(outpath)
local function formatDocs(fd)
local rs = ""
for name,finfo in pairs(fd) do
if rs:len() > 0 then
rs = rs .. "\n\n"
end
local as = ""
for k,v in pairs(finfo.args) do
if k > 1 then
as = as .. ", "
end
as = as .. v[1]
if v[2] then
as = as .. "^"..v[2].."^"
end
end
local rt = ""
for k,v in pairs(finfo.outtypes or {}) do
if rt:len() > 0 then
rt = rt .. ", "
else
rt = ": "
end
rt = rt .. v
end
rs = string.format("%s## %s(%s)%s\n%s",rs,name,as,rt,finfo.description)
end
return rs
end
local ad = io.open(outpath,"wb")
for k,v in pairs(tA) do
local fd = doc.parsefile(v)
local ds = ser.serialize(fd)
local tn = v:match("/(.+)$")
if ds:len() > 3 then
ad:write(tn.."\t"..ds:gsub("\n",""):gsub(", +",",").."\n")
end
end
ad:close()

View File

@ -1,4 +1,3 @@
local _,serial = pcall(require,"serialization")
local doc = {} local doc = {}
doc.searchers = {} doc.searchers = {}
doc.tctab = { doc.tctab = {
@ -97,20 +96,6 @@ function doc.searchers.lib(name) -- string -- string string -- Tries to find a d
if not dt then return false, "unable to find documentation for "..tostring(name) end if not dt then return false, "unable to find documentation for "..tostring(name) end
return doc.format(dt) return doc.format(dt)
end end
function doc.searchers.cdoc(topic) -- string -- string string -- Searches for documentation labelled as *topic* in .dict files under /boot/doc/
if not serial then return end
for k,v in ipairs(fs.list("/boot/doc")) do
if v:sub(-5) == ".dict" then
local f=io.open("/boot/doc/"..v,"rb")
for line in f:lines() do
local mname, docs = line:match("^(.-)\t(.+)$")
if mname == topic or mname == topic..".lua" then
return doc.format(serial.unserialize(docs))
end
end
end
end
end
function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc(). function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc().
local lib = os.getenv("LIB") or "/boot/lib" local lib = os.getenv("LIB") or "/boot/lib"

View File

@ -1,5 +1,5 @@
local event = {} local event = {}
function event.pull(t,...) -- number -- -- return an event, optionally with timeout *t* and filter *...*. function event.pull(t,...) -- return an event, optionally with timeout *t* and filter *...*.
local tA = {...} local tA = {...}
if type(t) == "string" then if type(t) == "string" then
table.insert(tA,1,t) table.insert(tA,1,t)
@ -26,7 +26,7 @@ function event.pull(t,...) -- number -- -- return an event, optionally with time
return nil return nil
end end
function event.listen(e,f) -- string function -- -- run function *f* for every occurance of event *e* function event.listen(e,f) -- run function *f* for every occurance of event *e*
os.spawn(function() while true do os.spawn(function() while true do
local tEv = {coroutine.yield()} local tEv = {coroutine.yield()}
if tEv[1] == e then if tEv[1] == e then
@ -36,7 +36,7 @@ function event.listen(e,f) -- string function -- -- run function *f* for every o
end end,string.format("[%d] %s listener",os.pid(),e)) end end,string.format("[%d] %s listener",os.pid(),e))
end end
function event.ignore(e,f) -- string function -- -- stop function *f* running for every occurance of event *e* function event.ignore(e,f) -- stop function *f* running for every occurance of event *e*
computer.pushSignal("unlisten",e,tostring(f)) computer.pushSignal("unlisten",e,tostring(f))
end end

View File

@ -12,7 +12,7 @@ net.minport = 32768
net.maxport = 65535 net.maxport = 65535
net.openports = {} net.openports = {}
function net.genPacketID() -- -- string -- generate a random 16-character string, for use in packet IDs function net.genPacketID() -- generate a random 16-character string, for use in packet IDs
local npID = "" local npID = ""
for i = 1, 16 do for i = 1, 16 do
npID = npID .. string.char(math.random(32,126)) npID = npID .. string.char(math.random(32,126))
@ -20,11 +20,11 @@ function net.genPacketID() -- -- string -- generate a random 16-character string
return npID return npID
end end
function net.usend(to,port,data,npID) -- string number string string -- -- send an unreliable packet to host *to* on port *port* with data *data*, optionally with the packet ID *npID* function net.usend(to,port,data,npID) -- send an unreliable packet to host *to* on port *port* with data *data*, optionally with the packet ID *npID*
computer.pushSignal("net_send",0,to,port,data,npID) computer.pushSignal("net_send",0,to,port,data,npID)
end end
function net.rsend(to,port,data,block) -- string number string boolean -- boolean -- send a reliable packet to host *to* on port *port* with data *data*, with *block* set to true to disable blocking function net.rsend(to,port,data,block) -- send a reliable packet to host *to* on port *port* with data *data*, with *block* set to true to disable blocking
local pid, stime = net.genPacketID(), computer.uptime() + net.streamdelay local pid, stime = net.genPacketID(), computer.uptime() + net.streamdelay
computer.pushSignal("net_send",1,to,port,data,pid) computer.pushSignal("net_send",1,to,port,data,pid)
if block then return false end if block then return false end
@ -37,7 +37,7 @@ end
-- ordered packet delivery, layer 4? -- ordered packet delivery, layer 4?
function net.send(to,port,ldata) -- string number string -- boolean -- send arbitrary data *ldata* reliably to host *to* on port *port* function net.send(to,port,ldata) -- send arbitrary data *ldata* reliably to host *to* on port *port*
local tdata = {} local tdata = {}
if ldata:len() > net.mtu then if ldata:len() > net.mtu then
for i = 1, ldata:len(), net.mtu do for i = 1, ldata:len(), net.mtu do
@ -112,7 +112,7 @@ local function socket(addr,port,sclose)
return conn return conn
end end
function net.open(to,port) -- string number -- buffer -- open a socket to host *to* on port *port* function net.open(to,port) -- open a socket to host *to* on port *port*
if not net.rsend(to,port,"openstream") then return false, "no ack from host" end if not net.rsend(to,port,"openstream") then return false, "no ack from host" end
local st = computer.uptime()+net.streamdelay local st = computer.uptime()+net.streamdelay
local est = false local est = false
@ -139,7 +139,7 @@ function net.open(to,port) -- string number -- buffer -- open a socket to host *
return socket(to,data,sclose) return socket(to,data,sclose)
end end
function net.listen(port) -- number -- buffer -- listen for connections on port *port* in a blocking manner function net.listen(port) -- listen for connections on port *port* in a blocking manner
repeat repeat
_, from, rport, data = event.pull("net_msg") _, from, rport, data = event.pull("net_msg")
until rport == port and data == "openstream" until rport == port and data == "openstream"
@ -150,7 +150,7 @@ function net.listen(port) -- number -- buffer -- listen for connections on port
return socket(from,nport,sclose) return socket(from,nport,sclose)
end end
function net.flisten(port,listener) -- number function -- function -- run function *listener* on a connection to *port* function net.flisten(port,listener) -- run function *listener* on a connection to *port*
local function helper(_,from,rport,data) local function helper(_,from,rport,data)
if rport == port and data == "openstream" then if rport == port and data == "openstream" then
local nport = math.random(net.minport,net.maxport) local nport = math.random(net.minport,net.maxport)

View File

@ -22,7 +22,7 @@ local function saveConfig()
return true return true
end end
function rc.load(name,force) -- string boolean -- table -- Attempts to load service *name*, and if *force* is true, replaces the current instance. function rc.load(name,force)
if force then if force then
rc.stop(name) rc.stop(name)
service[name] = nil service[name] = nil
@ -35,7 +35,7 @@ function rc.load(name,force) -- string boolean -- table -- Attempts to load serv
return res return res
end end
function rc.stop(name,...) -- string -- boolean string -- Stops service *name*, supplying *...* to the stop function. Returns false and a reason if this fails. function rc.stop(name,...)
if not service[name] then return false, "service not found" end if not service[name] then return false, "service not found" end
if service[name].stop then if service[name].stop then
service[name].stop(...) service[name].stop(...)
@ -47,7 +47,7 @@ function rc.stop(name,...) -- string -- boolean string -- Stops service *name*,
rc.pids[name] = nil rc.pids[name] = nil
end end
function rc.start(name,...) -- string -- boolean string -- Stops service *name*, supplying *...* to the stop function. Returns false and a reason if this fails. function rc.start(name,...)
rc.load(name) rc.load(name)
if not service[name] then return false, "service not found" end if not service[name] then return false, "service not found" end
local rv = {service[name].start(...)} local rv = {service[name].start(...)}
@ -56,19 +56,19 @@ function rc.start(name,...) -- string -- boolean string -- Stops service *name*,
end end
end end
function rc.restart(name) -- string -- -- Restarts service *name* using rc.stop and rc.start. function rc.restart(name)
rc.stop(name) rc.stop(name)
rc.start(name) rc.start(name)
end end
function rc.enable(name) -- string -- -- Enables service *name* being started on startup. function rc.enable(name)
for k,v in pairs(cfg.enabled) do for k,v in pairs(cfg.enabled) do
if v == name then return false end if v == name then return false end
end end
cfg.enabled[#cfg.enabled+1] = name cfg.enabled[#cfg.enabled+1] = name
saveConfig() saveConfig()
end end
function rc.disable(name) -- string -- -- Disables service *name* being started on startup. function rc.disable(name)
local disabled = false local disabled = false
for k,v in pairs(cfg.enabled) do for k,v in pairs(cfg.enabled) do
if v == name then table.remove(cfg.enabled,k) disabled = true break end if v == name then table.remove(cfg.enabled,k) disabled = true break end

View File

@ -27,7 +27,7 @@ function rpcf.list()
return rt return rt
end end
function rpc.call(hostname,fn,...) -- string string -- boolean -- Calls exported function *fn* on host *hostname*, with parameters *...*, returning whatever the function returns, or false. function rpc.call(hostname,fn,...)
if hostname == "localhost" then if hostname == "localhost" then
return rpcf[fn](...) return rpcf[fn](...)
end end
@ -44,7 +44,7 @@ function rpc.call(hostname,fn,...) -- string string -- boolean -- Calls exported
end end
return false return false
end end
function rpc.proxy(hostname,filter) -- string string -- table -- Returns a component.proxy()-like table from the functions on *hostname* with names matching *filter*. function rpc.proxy(hostname,filter)
filter=(filter or "").."(.+)" filter=(filter or "").."(.+)"
local fnames = rpc.call(hostname,"list") local fnames = rpc.call(hostname,"list")
if not fnames then return false end if not fnames then return false end
@ -59,7 +59,7 @@ function rpc.proxy(hostname,filter) -- string string -- table -- Returns a compo
end end
return rt return rt
end end
function rpc.register(name,fn) -- string function -- -- Registers a function to be exported by the RPC library. function rpc.register(name,fn)
local rpcrunning = false local rpcrunning = false
for k,v in pairs(os.tasks()) do for k,v in pairs(os.tasks()) do
if os.taskInfo(v).name == "rpc daemon" then if os.taskInfo(v).name == "rpc daemon" then

View File

@ -3,7 +3,7 @@ local local_pairs=function(tbl)
local mt=getmetatable(tbl) local mt=getmetatable(tbl)
return (mt and mt.__pairs or pairs)(tbl) return (mt and mt.__pairs or pairs)(tbl)
end end
function serial.serialize(value,af) -- boolean -- string -- serialize *value* into a string. If *af* is true, allow functions. This breaks unserialization. function serial.serialize(value,af) -- serialize *value* into a string. If *af* is true, allow functions. This breaks unserialization.
local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true} local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true}
local id="^[%a_][%w_]*$" local id="^[%a_][%w_]*$"
local ts={} local ts={}
@ -40,7 +40,7 @@ function serial.serialize(value,af) -- boolean -- string -- serialize *value* in
else error("ut "..t) end end else error("ut "..t) end end
return s(value, 1) return s(value, 1)
end end
function serial.unserialize(data) -- string -- -- return *data*, but unserialized function serial.unserialize(data) -- return *data*, but unserialized
checkArg(1, data, "string") checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}}) local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then return nil, reason end if not result then return nil, reason end

View File

@ -19,7 +19,7 @@ local function wrapUnits(n)
return tostring(math.floor(n))..(scale[count] or "") return tostring(math.floor(n))..(scale[count] or "")
end end
function shutil.import(lib) -- string -- boolean -- Imports the functions from library *lib* into the shell environment. function shutil.import(lib)
local cE = os.getenv("INCLUDE") or shell.include local cE = os.getenv("INCLUDE") or shell.include
local nE = {} local nE = {}
for k,v in pairs(cE) do for k,v in pairs(cE) do
@ -31,7 +31,7 @@ function shutil.import(lib) -- string -- boolean -- Imports the functions from l
return true return true
end end
function shutil.unimport(lib) -- string -- boolean -- Removes the functions from *lib* from the shell environment. function shutil.unimport(lib)
local cE = os.getenv("INCLUDE") or shell.include local cE = os.getenv("INCLUDE") or shell.include
local nE = {} local nE = {}
for k,v in pairs(cE) do for k,v in pairs(cE) do
@ -43,7 +43,7 @@ function shutil.unimport(lib) -- string -- boolean -- Removes the functions from
return true return true
end end
function shutil.ls(...) -- string -- -- Prints contents of directories specified as *...*. function shutil.ls(...)
local tA = {...} local tA = {...}
if not tA[1] then tA[1] = "." end if not tA[1] then tA[1] = "." end
for _,d in ipairs(tA) do for _,d in ipairs(tA) do
@ -56,7 +56,7 @@ function shutil.ls(...) -- string -- -- Prints contents of directories specified
end end
end end
function shutil.cat(...) -- string -- -- Outputs the contents of files specified in *...* to the standard output. function shutil.cat(...)
for _,fn in ipairs({...}) do for _,fn in ipairs({...}) do
local f = io.open(fn,"rb") local f = io.open(fn,"rb")
io.write(f:read("*a")) io.write(f:read("*a"))
@ -64,7 +64,7 @@ function shutil.cat(...) -- string -- -- Outputs the contents of files specified
end end
end end
function shutil.ps() -- Prints the processes running on the system. function shutil.ps()
print("PID# Parent | Name") print("PID# Parent | Name")
for k,v in pairs(os.tasks()) do for k,v in pairs(os.tasks()) do
local t = os.taskInfo(v) local t = os.taskInfo(v)
@ -72,7 +72,7 @@ function shutil.ps() -- Prints the processes running on the system.
end end
end end
function shutil.df() -- Prints free disk space. function shutil.df()
local mt = fs.mounts() local mt = fs.mounts()
local ml = 0 local ml = 0
for k,v in pairs(mt) do for k,v in pairs(mt) do
@ -88,7 +88,7 @@ function shutil.df() -- Prints free disk space.
end end
end end
function shutil.mount(addr,path) -- string string -- boolean string -- Mounts filesystem component with address *addr* to *path* in the filesystem. function shutil.mount(addr,path)
if not addr then if not addr then
local mt = fs.mounts() local mt = fs.mounts()
for k,v in pairs(mt) do for k,v in pairs(mt) do
@ -114,7 +114,7 @@ function shutil.mount(addr,path) -- string string -- boolean string -- Mounts fi
end end
end end
function shutil.free() -- Displays used and free memory. function shutil.free()
print("Total Used Free") print("Total Used Free")
print(string.format("%5s %5s %5s",wrapUnits(computer.totalMemory()),wrapUnits(computer.totalMemory()-computer.freeMemory()),wrapUnits(computer.freeMemory()))) print(string.format("%5s %5s %5s",wrapUnits(computer.totalMemory()),wrapUnits(computer.totalMemory()-computer.freeMemory()),wrapUnits(computer.freeMemory())))
end end

View File

@ -4,7 +4,7 @@ local function normalise(path)
return table.concat(fs.segments(path),"/") return table.concat(fs.segments(path),"/")
end end
function unionfs.create(...) -- string -- table -- Returns a unionfs object of the directories specified in *...*. function unionfs.create(...)
local paths,fids,fc = {...}, {}, 0 local paths,fids,fc = {...}, {}, 0
for k,v in pairs(paths) do for k,v in pairs(paths) do
paths[k] = "/"..normalise(v) paths[k] = "/"..normalise(v)

View File

@ -1,5 +1,5 @@
local vtansi = {} local vtansi = {}
function vtansi.vtemu(gpu) -- table -- function -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal function vtansi.vtemu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF} local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
local mx, my = gpu.maxResolution() local mx, my = gpu.maxResolution()
local cx, cy = 1, 1 local cx, cy = 1, 1
@ -142,7 +142,7 @@ function vtansi.vtemu(gpu) -- table -- function -- takes GPU component proxy *gp
return termwrite return termwrite
end end
function vtansi.vtsession(gpua,scra) -- string string -- table -- creates a process to handle the GPU and screen address combination *gpua*/*scra*. Returns read, write and "close" functions. function vtansi.vtsession(gpua,scra) -- creates a process to handle the GPU and screen address combination *gpua*/*scra*. Returns read, write and "close" functions.
local gpu = component.proxy(gpua) local gpu = component.proxy(gpua)
gpu.bind(scra) gpu.bind(scra)
local write = vtansi.vtemu(gpu) local write = vtansi.vtemu(gpu)

View File

@ -181,7 +181,6 @@ env:process(arg[1])
local tmpfile = os.tmpname() local tmpfile = os.tmpname()
if tmpfile:sub(#tmpfile) == "." then tmpfile = tmpfile:sub(1, #tmpfile - 1) end if tmpfile:sub(#tmpfile) == "." then tmpfile = tmpfile:sub(1, #tmpfile - 1) end
local tmpf = io.open(tmpfile, "wb") local tmpf = io.open(tmpfile, "wb")
env.code = env.code:gsub("%-%-.-\n","\n"):gsub("\n\n","\n")
tmpf:write(env.code) tmpf:write(env.code)
tmpf:close() tmpf:close()
--if (os.execute("lua minify.lua "..tmpfile.." > "..arg[2])) then --if (os.execute("lua minify.lua "..tmpfile.." > "..arg[2])) then

View File

@ -2,7 +2,7 @@
buffer = {} buffer = {}
function buffer.new(mode, stream) -- string table -- table -- create a new buffer in mode *mode* backed by stream object *stream* function buffer.new(mode, stream) -- create a new buffer in mode *mode* backed by stream object *stream*
local result = { local result = {
mode = {}, mode = {},
stream = stream, stream = stream,

View File

@ -64,7 +64,7 @@ end
devfs.component.address = "devfs" devfs.component.address = "devfs"
devfs.component.type = "devfs" devfs.component.type = "devfs"
function devfs.register(fname,fopen) -- string function -- -- Register a new devfs node with the name *fname* that will run the function *fopen* when opened. This function should return a function for read, a function for write, function for close, and optionally, a function for seek, in that order. function devfs.register(fname,fopen) -- Register a new devfs node with the name *fname* that will run the function *fopen* when opened. This function should return a function for read, a function for write, function for close, and optionally, a function for seek, in that order.
devfs.files[fname] = fopen devfs.files[fname] = fopen
end end

View File

@ -3,14 +3,14 @@ fs = {}
local fsmounts = {} local fsmounts = {}
-- basics -- basics
function fs.segments(path) -- string -- table -- Splits *path* on each / function fs.segments(path) -- splits *path* on each /
local segments = {} local segments = {}
for segment in path:gmatch("[^/]+") do for segment in path:gmatch("[^/]+") do
segments[#segments+1] = segment segments[#segments+1] = segment
end end
return segments return segments
end end
function fs.resolve(path) -- string -- string string -- Resolves *path* to a specific filesystem mount and path function fs.resolve(path) -- resolves *path* to a specific filesystem mount and path
if not path or path == "." then path = os.getenv("PWD") end if not path or path == "." then path = os.getenv("PWD") end
if path:sub(1,1) ~= "/" then path=(os.getenv("PWD") or "").."/"..path end if path:sub(1,1) ~= "/" then path=(os.getenv("PWD") or "").."/"..path end
local segments, rpath, rfs= fs.segments(path) local segments, rpath, rfs= fs.segments(path)
@ -44,7 +44,7 @@ local function fclose(self)
return fsmounts[self.fs].close(self.fid) return fsmounts[self.fs].close(self.fid)
end end
function fs.open(path,mode) -- string string -- table -- Opens file *path* with mode *mode*, returning a file object. function fs.open(path,mode) -- opens file *path* with mode *mode*
mode = mode or "rb" mode = mode or "rb"
local fsi,path = fs.resolve(path) local fsi,path = fs.resolve(path)
if not fsmounts[fsi] then return false end if not fsmounts[fsi] then return false end
@ -62,7 +62,7 @@ function fs.open(path,mode) -- string string -- table -- Opens file *path* with
return false return false
end end
function fs.copy(from,to) -- string string -- boolean -- copies a file from *from* to *to* function fs.copy(from,to) -- copies a file from *from* to *to*
local of = fs.open(from,"rb") local of = fs.open(from,"rb")
local df = fs.open(to,"wb") local df = fs.open(to,"wb")
if not of or not df then if not of or not df then
@ -71,34 +71,33 @@ function fs.copy(from,to) -- string string -- boolean -- copies a file from *fro
df:write(of:read("*a")) df:write(of:read("*a"))
df:close() df:close()
of:close() of:close()
return true
end end
function fs.rename(from,to) -- string string -- boolean -- Moves file *from* to *to* function fs.rename(from,to) -- moves file *from* to *to*
local ofsi, opath = fs.resolve(from) local ofsi, opath = fs.resolve(from)
local dfsi, dpath = fs.resolve(to) local dfsi, dpath = fs.resolve(to)
if ofsi == dfsi then if ofsi == dfsi then
fsmounts[ofsi].rename(opath,dpath) fsmounts[ofsi].rename(opath,dpath)
return true return true
end end
if not fs.copy(from,to) then return false end fs.copy(from,to)
if not fs.remove(from) then return false end fs.remove(from)
return true return true
end end
function fs.mount(path,proxy) -- string table -- boolean -- Mounts the filesystem *proxy* to the mount point *path* if it is a directory. BYO proxy. function fs.mount(path,proxy) -- mounts the filesystem *proxy* to the mount point *path* if it is a directory. BYO proxy.
if fs.isDirectory(path) and not fsmounts[table.concat(fs.segments(path),"/")] then if fs.isDirectory(path) and not fsmounts[table.concat(fs.segments(path),"/")] then
fsmounts[table.concat(fs.segments(path),"/")] = proxy fsmounts[table.concat(fs.segments(path),"/")] = proxy
return true return true
end end
return false, "path is not a directory" return false, "path is not a directory"
end end
function fs.umount(path) -- string -- -- Unmounts filesystem from *path*. function fs.umount(path)
local fsi,_ = fs.resolve(path) local fsi,_ = fs.resolve(path)
fsmounts[fsi] = nil fsmounts[fsi] = nil
end end
function fs.mounts() -- -- table -- Returns a table containing the mount points of all mounted filesystems function fs.mounts() -- returns a table containing the mount points of all mounted filesystems
local rt = {} local rt = {}
for k,v in pairs(fsmounts) do for k,v in pairs(fsmounts) do
rt[#rt+1] = k,v.address or "unknown" rt[#rt+1] = k,v.address or "unknown"
@ -106,11 +105,11 @@ function fs.mounts() -- -- table -- Returns a table containing the mount points
return rt return rt
end end
function fs.address(path) -- string -- string -- Returns the address of the filesystem at a given path, if applicable; do not expect a sensical response function fs.address(path) -- returns the address of the filesystem at a given path, if applicable; do not expect a sensical response
local fsi,_ = fs.resolve(path) local fsi,_ = fs.resolve(path)
return fsmounts[fsi].address return fsmounts[fsi].address
end end
function fs.type(path) -- string -- string -- Returns the component type of the filesystem at a given path, if applicable function fs.type(path) -- returns the component type of the filesystem at a given path, if applicable
local fsi,_ = fs.resolve(path) local fsi,_ = fs.resolve(path)
return fsmounts[fsi].type or "filesystem" return fsmounts[fsi].type or "filesystem"
end end

View File

@ -1,12 +1,12 @@
io = {} io = {}
function io.open(path,mode) -- string string -- table -- Open file *path* in *mode*. Returns a buffer object. function io.open(path,mode) -- Open file *path* in *mode*. Returns a buffer object.
local f,e = fs.open(path, mode) local f,e = fs.open(path, mode)
if not f then return false, e end if not f then return false, e end
return buffer.new(mode,f) return buffer.new(mode,f)
end end
function io.input(fd) -- table -- table -- Sets the default input stream to *fd* if provided, either as a buffer as a path. Returns the default input stream. function io.input(fd) -- Sets the default input stream to *fd* if provided, either as a buffer as a path. Returns the default input stream.
if type(fd) == "string" then if type(fd) == "string" then
fd=io.open(fd,"rb") fd=io.open(fd,"rb")
end end
@ -15,7 +15,7 @@ function io.input(fd) -- table -- table -- Sets the default input stream to *fd*
end end
return os.getenv("STDIN") return os.getenv("STDIN")
end end
function io.output(fd) -- table -- table -- Sets the default output stream to *fd* if provided, either as a buffer as a path. Returns the default output stream. function io.output(fd) -- Sets the default output stream to *fd* if provided, either as a buffer as a path. Returns the default output stream.
if type(fd) == "string" then if type(fd) == "string" then
fd=io.open(fd,"wb") fd=io.open(fd,"wb")
end end

View File

@ -1,19 +1,19 @@
function loadfile(p) -- string -- function -- reads file *p* and returns a function if possible function loadfile(p) -- reads file *p* and returns a function if possible
local f = io.open(p,"rb") local f = io.open(p,"rb")
local c = f:read("*a") local c = f:read("*a")
f:close() f:close()
return load(c,p,"t") return load(c,p,"t")
end end
function runfile(p,...) -- string -- -- runs file *p* with arbitrary arguments in the current thread function runfile(p,...) -- runs file *p* with arbitrary arguments in the current thread
return loadfile(p)(...) return loadfile(p)(...)
end end
function os.spawnfile(p,n,...) -- string string -- number -- spawns a new process from file *p* with name *n*, with arguments following *n*. function os.spawnfile(p,n,...) -- spawns a new process from file *p* with name *n*, with arguments following *n*.
local tA = {...} local tA = {...}
return os.spawn(function() local res={pcall(loadfile(p), table.unpack(tA))} computer.pushSignal("process_finished", os.pid(), table.unpack(res)) dprint(table.concat(res)) end,n or p) return os.spawn(function() local res={pcall(loadfile(p), table.unpack(tA))} computer.pushSignal("process_finished", os.pid(), table.unpack(res)) dprint(table.concat(res)) end,n or p)
end end
_G.package = {} _G.package = {}
package.loaded = {computer=computer,component=component,fs=fs,buffer=buffer} package.loaded = {computer=computer,component=component,fs=fs,buffer=buffer}
function require(f,force) -- string boolean -- table -- searches for a library with name *f* and returns what the library returns, if possible. if *force* is set, loads the library even if it is cached function require(f,force) -- searches for a library with name *f* and returns what the library returns, if possible. if *force* is set, loads the library even if it is cached
if not package.loaded[f] or force then if not package.loaded[f] or force then
local lib = os.getenv("LIB") or "/boot/lib" local lib = os.getenv("LIB") or "/boot/lib"
for d in lib:gmatch("[^\n]+") do for d in lib:gmatch("[^\n]+") do
@ -29,6 +29,6 @@ function require(f,force) -- string boolean -- table -- searches for a library w
end end
error("library not found: "..f) error("library not found: "..f)
end end
function reload(f) -- string -- table -- Reloads library *f* from disk into memory. function reload(f)
return require(f,true) return require(f,true)
end end

141
module/nvt100.lua Normal file
View File

@ -0,0 +1,141 @@
function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
local mx, my = gpu.maxResolution()
local cx, cy = 1, 1
local pc = " "
local lc = ""
local mode = 0 -- 0 normal, 1 escape, 2 command
local lw = true
local sx, sy = 1,1
local cs = ""
local bg, fg = 0, 0xFFFFFF
-- setup
gpu.setResolution(mx,my)
gpu.fill(1,1,mx,my," ")
local function checkCursor()
if cx > mx and lw then
cx, cy = 1, cy+1
end
if cy > my then
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=my
end
if cy < 1 then cy = 1 end
if cx < 1 then cx = 1 end
end
local function termwrite(s)
local wb = ""
local lb, ec = nil, nil
local function flushwb()
while wb:len() > 0 do
checkCursor()
local wl = wb:sub(1,mx-cx+1)
wb = wb:sub(wl:len()+1)
gpu.set(cx, cy, wl)
cx = cx + wl:len()
end
end
local rs = ""
s=s:gsub("\8","\27[D")
pc = gpu.get(cx,cy)
gpu.setForeground(fg)
gpu.setBackground(bg)
gpu.set(cx,cy,pc)
for cc in s:gmatch(".") do
if mode == 0 then
if cc == "\n" then
flushwb()
cx,cy = 1, cy+1
elseif cc == "\t" then
wb=wb..(" "):rep(8*((cx+9)//8))
elseif cc == "\27" then
flushwb()
mode = 1
else
wb = wb .. cc
end
elseif mode == 1 then
if cc == "[" then
mode = 2
else
mode = 0
end
elseif mode == 2 then
if cc:match("[%d;]") then
cs = cs .. cc
else
mode = 0
local tA = {}
for s in cs:gmatch("%d+") do
tA[#tA+1] = tonumber(s)
end
if cc == "H" then
cx, cy = tA[1] or 1, tA[2] or 1
elseif cc == "A" then
cy = cy - (tA[1] or 1)
elseif cc == "B" then
cy = cy + (tA[1] or 1)
elseif cc == "C" then
cx = cx + (tA[1] or 1)
elseif cc == "D" then
cx = cx - (tA[1] or 1)
elseif cc == "s" then
sx, sy = cx, cy
elseif cc == "u" then
cx, cy = sx, sy
elseif cc == "n" and tA[1] == 6 then
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
elseif cc == "K" and tA[1] == 1 then
gpu.fill(1,cy,cx,1," ")
elseif cc == "K" and tA[1] == 2 then
gpu.fill(cx,cy,mx,1," ")
elseif cc == "K" then
gpu.fill(1,cy,mx,1," ")
elseif cc == "J" and tA[1] == 1 then
gpu.fill(1,1,mx,cy," ")
elseif cc == "J" and tA[1] == 2 then
gpu.fill(1,1,mx,my," ")
cx, cy = 1, 1
elseif cc == "J" then
gpu.fill(1,cy,mx,my," ")
elseif cc == "m" then
for _,num in ipairs(tA) do
if num == 0 then
fg,bg,ec,lb = 0xFFFFFF,0,true,true
elseif num == 7 then
local nfg,nbg = bg, fg
fg, bg = nfg, nbg
elseif num > 29 and num < 38 then
fg = colours[num-29]
elseif num > 39 and num < 48 then
bg = colours[num-39]
elseif num == 100 then -- disable local echo
ec = false
elseif num == 101 then -- disable line mode
lb = false
end
end
gpu.setForeground(fg)
gpu.setBackground(bg)
end
cs = ""
checkCursor()
end
end
end
flushwb()
checkCursor()
pc = gpu.get(cx,cy)
gpu.setForeground(bg)
gpu.setBackground(fg)
gpu.set(cx,cy,pc)
gpu.setForeground(fg)
gpu.setBackground(bg)
return rs, lb, ec
end
return termwrite
end

View File

@ -1,4 +1,4 @@
function os.chdir(p) -- string -- boolean string -- changes the current working directory of the calling process to the directory specified in *p*, returning true or false, error function os.chdir(p) -- changes the current working directory of the calling process to the directory specified in *p*, returning true or false, error
if not (p:sub(1,1) == "/") then if not (p:sub(1,1) == "/") then
local np = {} local np = {}
for k,v in pairs(fs.segments(os.getenv("PWD").."/"..p)) do for k,v in pairs(fs.segments(os.getenv("PWD").."/"..p)) do

View File

@ -1,6 +1,6 @@
do do
local tTasks,nPid,nTimeout,cPid = {},1,0.25,0 -- table of tasks, next process ID, event timeout, current PID local tTasks,nPid,nTimeout,cPid = {},1,0.25,0 -- table of tasks, next process ID, event timeout, current PID
function os.spawn(f,n) -- function string -- number -- creates a process from function *f* with name *n* function os.spawn(f,n) -- creates a process from function *f* with name *n*
tTasks[nPid] = { tTasks[nPid] = {
c=coroutine.create(f), -- actual coroutine c=coroutine.create(f), -- actual coroutine
n=n, -- process name n=n, -- process name
@ -16,20 +16,20 @@ function os.spawn(f,n) -- function string -- number -- creates a process from fu
nPid = nPid + 1 nPid = nPid + 1
return nPid - 1 return nPid - 1
end end
function os.kill(pid) -- number -- -- removes process *pid* from the task list function os.kill(pid) -- removes process *pid* from the task list
tTasks[pid] = nil tTasks[pid] = nil
end end
function os.pid() -- -- number -- returns the current process' PID function os.pid() -- returns the current process' PID
return cPid return cPid
end end
function os.tasks() -- -- table -- returns a table of process IDs function os.tasks() -- returns a table of process IDs
local rt = {} local rt = {}
for k,v in pairs(tTasks) do for k,v in pairs(tTasks) do
rt[#rt+1] = k rt[#rt+1] = k
end end
return rt return rt
end end
function os.taskInfo(pid) -- number -- table -- returns info on process *pid* as a table with name and parent values function os.taskInfo(pid) -- returns info on process *pid* as a table with name and parent values
pid = pid or os.pid() pid = pid or os.pid()
if not tTasks[pid] then return false end if not tTasks[pid] then return false end
return {name=tTasks[pid].n,parent=tTasks[pid].P} return {name=tTasks[pid].n,parent=tTasks[pid].P}

View File

@ -9,7 +9,6 @@ syslog.notice = 5
syslog.info = 6 syslog.info = 6
syslog.debug = 7 syslog.debug = 7
-- function syslog(msg, level, service) -- string number string -- -- Output *msg* to the system log, with severity *level*, from *service*.
local rdprint=dprint or function() end local rdprint=dprint or function() end
setmetatable(syslog,{__call = function(_,msg, level, service) setmetatable(syslog,{__call = function(_,msg, level, service)
level, service = level or syslog.info, service or (os.taskInfo(os.pid()) or {}).name or "unknown" level, service = level or syslog.info, service or (os.taskInfo(os.pid()) or {}).name or "unknown"

5
module/term.lua Normal file
View File

@ -0,0 +1,5 @@
--#include "module/vt-task.lua"
do
local r,w = vtemu(component.list("gpu")(),component.list("screen")())
devfs.register("tty0", function() return r,w,function() end end)
end

58
module/vt-task.lua Normal file
View File

@ -0,0 +1,58 @@
do
--#include "module/nvt100.lua"
function vtemu(gpua,scra) -- creates a process to handle the GPU and screen address combination *gpua*/*scra*. Returns read, write and "close" functions.
local gpu = component.proxy(gpua)
gpu.bind(scra)
local write = vt100emu(gpu)
local kba = {}
for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
kba[v]=true
end
local buf, lbuf, echo = "", true, true
os.spawn(function() dprint(pcall(function()
while true do
local ty,ka,ch = coroutine.yield()
if ty == "key_down" and kba[ka] then
if ch == 13 then ch = 10 end
if ch == 8 then
if buf:len() > 0 then
if echo then write("\8 \8") end
buf = buf:sub(1,-2)
end
elseif ch > 0 then
if echo then write(string.char(ch)) end
buf=buf..string.char(ch)
end
end
end
end)) end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8)))
local function bread(n)
local r
if lbuf then
while not buf:find("\n") do
coroutine.yield()
end
local n = buf:find("\n")
r, buf = buf:sub(1,n), buf:sub(n+1)
else
r = buf
buf = ""
coroutine.yield()
end
return r
end
local function bwrite(d)
local ba, lb, ec = write(d)
buf = buf .. ba
if lb ~= nil then
dprint("local buffer mode: "..tostring(lb))
lbuf = lb
end
if ec ~= nil then
dprint("echo mode: "..tostring(ec))
echo = ec
end
end
return bread, bwrite, function() io.write("\27[2J\27[H") end
end
end

148
module/vt100.lua Normal file
View File

@ -0,0 +1,148 @@
function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
local mx, my = gpu.maxResolution()
local cx, cy = 1, 1
local pc = " "
local lc = ""
local mode = "n"
local lw = true
local sx, sy = 1,1
local cs = ""
local bg, fg = 0, 0xFFFFFF
-- setup
gpu.setResolution(mx,my)
gpu.fill(1,1,mx,my," ")
local function termwrite(s)
local rs = ""
s=s:gsub("\8","\27[D")
pc = gpu.get(cx,cy)
gpu.setForeground(fg)
gpu.setBackground(bg)
gpu.set(cx,cy,pc)
for i = 1, s:len() do
local cc = s:sub(i,i)
if mode == "n" then
if cc == "\n" then -- line feed
cx, cy = 1, cy+1
elseif cc == "\r" then -- cursor home
cx = 1
elseif cc == "\27" then -- escape
mode = "e"
elseif cc == "\t" then
cx = 8*((cx+9)//8)
elseif string.byte(cc) > 31 and string.byte(cc) < 127 then -- printable, I guess
gpu.set(cx, cy, cc)
cx = cx + 1
end
elseif mode == "e" then
if cc == "[" then
mode = "v"
cs = ""
elseif cc == "D" then -- scroll down
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=cy+1
mode = "n"
elseif cc == "M" then -- scroll up
gpu.copy(1,1,mx,my-1,0,1)
gpu.fill(1,1,mx,1," ")
mode = "n"
else
mode = "n"
end
elseif mode == "v" then
mode = "n"
if cc == "s" then -- save cursor
sx, sy = cx, cy
elseif cc == "u" then -- restore cursor
cx, cy = sx, sy
elseif cc == "H" then -- cursor home or to
local tx, ty = cs:match("(%d+);(%d+)")
tx, ty = tx or "1", ty or "1"
cx, cy = tonumber(tx), tonumber(ty)
elseif cc == "A" then -- cursor up
cy = cy - (tonumber(cs) or 1)
elseif cc == "B" then -- cursor down
cy = cy + (tonumber(cs) or 1)
elseif cc == "C" then -- cursor right
cx = cx + (tonumber(cs) or 1)
elseif cc == "D" then -- cursor left
cx = cx - (tonumber(cs) or 1)
elseif cc == "h" and lc == "7" then -- enable line wrap
lw = true
elseif cc == "l" and lc == "7" then -- disable line wrap
lw = false
elseif cc == "c" then
rs = string.format("%s\27[%d;%d0c",rs,mx,my)
elseif cc == "n" and lc == "6" then
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
elseif cc == "K" then
if lc == "1" then
gpu.fill(1,cy,cx,1," ")
elseif lc == "2" then
gpu.fill(cx,cy,mx,1," ")
else
gpu.fill(1,cy,mx,1," ")
end
elseif cc == "J" then
if lc == "1" then
gpu.fill(1,1,mx,cy," ")
elseif lc == "2" then
gpu.full(1,1,mx,my," ")
cx,cy = 1, 1
else
gpu.fill(1,cy,mx,my," ")
end
elseif cc == "m" then
for num in cs:gmatch("%d+") do
num=tonumber(num)
if num == 0 then
fg,bg = 0xFFFFFF,0
elseif num == 7 then
local nfg,nbg = bg, fg
fg, bg = nfg, nbg
elseif num > 29 and num < 38 then
fg = colours[num-29]
elseif num > 39 and num < 48 then
bg = colours[num-39]
end
end
gpu.setForeground(fg)
gpu.setBackground(bg)
else
cs = cs .. cc
if cc:match("[%d;]") then
mode = "v"
end
end
end
if cx > mx and lw then
cx, cy = 1, cy+1
end
if cy > my then
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=my
end
if cy < 1 then cy = 1 end
if cx < 1 then cx = 1 end
lc = cc
end
pc = gpu.get(cx,cy)
gpu.setForeground(bg)
gpu.setBackground(fg)
gpu.set(cx,cy,pc)
gpu.setForeground(fg)
gpu.setBackground(bg)
return rs
end
return termwrite
end

View File

@ -1,46 +0,0 @@
\documentclass[11pt,twoside,a4paper]{article}
\usepackage{hyperref}
\usepackage{multicol}
\usepackage{standalone}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{listings}
\usepackage{color}
\usepackage{sectsty}
\usepackage[cm]{fullpage}
\lstset{
commentstyle=\color{cyan}, % comment style
keywordstyle=\color{cyan}, % keyword style
stringstyle=\color{red}, % string literal style
numbers=left, % where to put the line-numbers; possible values are (none, left, right)
numbersep=5pt, % how far the line-numbers are from the code
numberstyle=\tiny\color{gray}, % the style that is used for the line-numbers
}
\hypersetup{
colorlinks=true
}
\subsectionfont{\ttfamily}
% pandoc stuff
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\let\stdsection\section
\renewcommand\section{\newpage\stdsection}
\lstset{basicstyle=\footnotesize\ttfamily,breaklines=true}
\newcommand{\ignore}[1]{}
\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
\author{$for(author)$$author$$sep$ \and $endfor$}
\date{}
\begin{document}
\pagenumbering{gobble}
\maketitle
\newpage
\pagenumbering{alph}
\tableofcontents
\newpage
\pagenumbering{arabic}
$body$
\end{document}