Compare commits
No commits in common. "master" and "git-windows-bash-fixes" have entirely different histories.
master
...
git-window
@ -1,5 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*.lua]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 1
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,4 @@
|
|||||||
*.cpio
|
*.cpio
|
||||||
*.af
|
*.af
|
||||||
apidoc.md
|
|
||||||
/target
|
/target
|
||||||
/doc
|
/doc
|
||||||
|
82
INSTALL.md
82
INSTALL.md
@ -1,82 +0,0 @@
|
|||||||
# Installing PsychOS
|
|
||||||
|
|
||||||
## From OpenOS
|
|
||||||
### Requirements
|
|
||||||
In general:
|
|
||||||
- oppm
|
|
||||||
- mtar
|
|
||||||
|
|
||||||
For installing to an unmanaged drive or tape
|
|
||||||
- slicer
|
|
||||||
- partman
|
|
||||||
- rtfs
|
|
||||||
- boopu
|
|
||||||
|
|
||||||
There are two easy methods to get these packages.
|
|
||||||
1. With oppm, if available, you can run `oppm install mtar partman rtfs boopu`.
|
|
||||||
2. You can use the `obootstrap.lua` script to set up a temporary environment for installing PsychOS. This can even be used from the OpenOS installer disk.
|
|
||||||
```
|
|
||||||
# wget https://git.shadowkat.net/izaya/OC-PsychOS2/raw/branch/master/obootstrap.lua /tmp/obootstrap.lua
|
|
||||||
# /tmp/obootstrap.lua
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preparing the target disk
|
|
||||||
#### Managed filesystem
|
|
||||||
Preparing a managed filesystem is extremely simple: attach it to your computer, and make sure there's nothing that you want to keep in the following locations on the filesystem:
|
|
||||||
- init.lua
|
|
||||||
- lib/
|
|
||||||
- service/
|
|
||||||
- doc/
|
|
||||||
- pkg/
|
|
||||||
- cfg/
|
|
||||||
|
|
||||||
#### Unmanaged drive or tape
|
|
||||||
##### Creating partitions
|
|
||||||
First, you'll need to find out how many sectors your target device has. `slicer <addr> show` will show you something like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
# slicer 9f7 show
|
|
||||||
Drive 9f755736 - 1024KiB, 2048 sectors:
|
|
||||||
# Name Type Start Len End
|
|
||||||
```
|
|
||||||
|
|
||||||
The usable space on the disk is going to be two sectors less than the total size - sectors 2 through 2047, in this case, as you will want to leave space for the OSDI partition table at the start, and the MTPT partition table at the end.
|
|
||||||
|
|
||||||
First, we'll create the boot partition. 64KiB is the recommended size, though 48KiB may be enough. OC disks use 512 byte sectors, so that will work out to 128 sectors.
|
|
||||||
|
|
||||||
```
|
|
||||||
# slicer 9f7 add init.lua boot 2 128
|
|
||||||
Drive 9f755736 - 1024KiB, 2048 sectors:
|
|
||||||
# Name Type Start Len End
|
|
||||||
1: 9f755736 mtpt 0 0 -1
|
|
||||||
2: init.lua boot 2 128 129
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, we need to create an rtfs partition, for the boot filesystem. This can use the rest of the space on the disk, but should be named `<first 8 characters of computer address>-boot`.
|
|
||||||
|
|
||||||
```
|
|
||||||
# slicer 9f7 add ffa5c282-boot rtfs 130 1918
|
|
||||||
Drive 9f755736 - 1024KiB, 2048 sectors:
|
|
||||||
# Name Type Start Len End
|
|
||||||
1: 9f755736 mtpt 0 0 -1
|
|
||||||
2: init.lua boot 2 128 129
|
|
||||||
3: ffa5c282-boot rtfs 130 1918 2047
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you're all done, you can restart partman and it should recognise the new partitions.
|
|
||||||
|
|
||||||
```
|
|
||||||
# rc partman restart
|
|
||||||
# components part
|
|
||||||
partition 9f755736-a739-4f45-8c5c-35a66a7f5dbe/2
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Formatting the filesystem
|
|
||||||
Next, we'll use the mkfs.rtfs utility to format the filesystem partition we just created. Do note that the order of components is not fixed, so using a shortened version can result in unreliable behavior, like, for example, formatting the boot partition.
|
|
||||||
|
|
||||||
```
|
|
||||||
# mkfs.rtfs 9f755736-a739-4f45-8c5c-35a66a7f5dbe/2 ffa5c282-boot
|
|
||||||
9f755736-a739-4f45-8c5c-35a66a7f5dbe/2
|
|
||||||
```
|
|
||||||
|
|
||||||
To make OpenOS mount the filesystem, the simplest way is to restart partman again, as described in the previous section.
|
|
20
README.md
20
README.md
@ -6,31 +6,23 @@ A lightweight, multi-user operating system for OpenComputers
|
|||||||
|
|
||||||
### The kernel
|
### The kernel
|
||||||
|
|
||||||
The kernel is composed of a number of modules, found in the *module/* directory, as specified by a file in the *kcfg* directory, `base` by default. Which modules are included can be customised by changing the include statements in the kernel configuration file; copying it and customizing that is recommended, so you can *git pull* later without having to stash or reset your changes.
|
The kernel can be built using luapreproc:
|
||||||
|
|
||||||
#### Unix-like systems
|
./luapreproc.lua module/init.lua kernel.lua
|
||||||
|
|
||||||
The kernel can be built using the preproc library and provided scripts:
|
|
||||||
|
|
||||||
lua build.lua kcfg/base.cfg kernel.lua
|
|
||||||
|
|
||||||
#### PsychOS
|
|
||||||
|
|
||||||
The kernel can be built from inside PsychOS using the preproc library, assuming you have the kernel source available:
|
|
||||||
|
|
||||||
preproc("kcfg/base.cfg","kernel.lua")
|
|
||||||
|
|
||||||
### The boot filesystem
|
### The boot filesystem
|
||||||
|
|
||||||
A boot filesystem contains several things:
|
A boot filesystem contains several things:
|
||||||
|
|
||||||
- The kernel, as init.lua
|
- The kernel, as init.lua
|
||||||
|
- The exec/ directory, as this contains all executables
|
||||||
- The lib/ directory, containing libraries
|
- The lib/ directory, containing libraries
|
||||||
- The service/ directory, containing system services
|
- The service/ directory, containing system services
|
||||||
- The exec/ directory, containing single-shot executable files
|
|
||||||
|
|
||||||
This has been automated in the form of build.sh, pending a real makefile.
|
This has been automated in the form of build.sh, pending a real makefile.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Documentation is generated as the system is built with build.sh; a set of markdown files will be placed into *doc/*, as well as an all-in-one *apidoc.md*. If pandoc is installed, an *apidoc.pdf* will also be generated.
|
To generate function documentation, run:
|
||||||
|
|
||||||
|
./finddesc.lua module/* lib/* > apidoc.md
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
local preproc = require "preproc"
|
|
||||||
|
|
||||||
preproc.minify = true
|
|
||||||
preproc(...)
|
|
7
build.sh
7
build.sh
@ -1,13 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
LUA=${LUA:-lua}
|
LUA=${LUA:-lua}
|
||||||
KVAR=${1:-base}
|
|
||||||
rm -r target/*
|
rm -r target/*
|
||||||
mkdir -p target/doc &>/dev/null
|
mkdir -p target/doc &>/dev/null
|
||||||
$LUA build.lua kcfg/$KVAR.cfg target/init.lua
|
$LUA luapreproc.lua module/init.lua target/init.lua
|
||||||
echo _OSVERSION=\"PsychOS 2.0a3-$(git rev-parse --short HEAD)-$KVAR\" > 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/ exec/ target/
|
cp -r service/ lib/ cfg/ target/
|
||||||
rm target/version.lua
|
rm target/version.lua
|
||||||
rm -r doc/ &>/dev/null
|
rm -r doc/ &>/dev/null
|
||||||
$LUA finddesc.lua doc/ $(find lib/ module/ -type f|sort)
|
$LUA finddesc.lua doc/ $(find lib/ module/ -type f|sort)
|
||||||
|
1
cfg/rc.cfg
Normal file
1
cfg/rc.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
{enabled={"getty","minitel"}}
|
@ -1,2 +0,0 @@
|
|||||||
--#include "module/base.lua"
|
|
||||||
--#include "module/init.lua"
|
|
@ -1,3 +0,0 @@
|
|||||||
--#include "module/base.lua"
|
|
||||||
--#include "module/rtfsboot.lua"
|
|
||||||
--#include "module/init.lua"
|
|
26
lib/doc.lua
26
lib/doc.lua
@ -4,7 +4,6 @@ doc.searchers = {}
|
|||||||
doc.tctab = {
|
doc.tctab = {
|
||||||
["string"] = 31,
|
["string"] = 31,
|
||||||
["table"] = 32,
|
["table"] = 32,
|
||||||
["userdata"] = 32,
|
|
||||||
["number"] = 33,
|
["number"] = 33,
|
||||||
["boolean"] = 35,
|
["boolean"] = 35,
|
||||||
["function"] = 36
|
["function"] = 36
|
||||||
@ -112,31 +111,6 @@ function doc.searchers.cdoc(topic) -- string -- string string -- Searches for do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function doc.searchers.component(name)
|
|
||||||
local dt = {}
|
|
||||||
local addr = component.list(name)()
|
|
||||||
if addr then
|
|
||||||
for fname,_ in pairs(component.methods(addr)) do
|
|
||||||
fd = {args={},outtypes={},atypes={}}
|
|
||||||
local ds = component.doc(addr,fname)
|
|
||||||
local ins, outs, desc = ds:match("%((.-)%)") or "", ds:match("%):(.*)%-%-") or "", ds:match("%-%-(.+)") or ""
|
|
||||||
for arg in ins:gmatch("[^,%s%[%]]+") do
|
|
||||||
local an,at = arg:match("(.-):(.+)")
|
|
||||||
at = at:match("(.-)=") or at
|
|
||||||
fd.args[#fd.args+1] = {an,at}
|
|
||||||
fd.atypes[an] = at
|
|
||||||
end
|
|
||||||
for out in outs:gmatch("[^,]+") do
|
|
||||||
fd.outtypes[#fd.outtypes+1] = out:match("^%s*(.-)%s*$")
|
|
||||||
end
|
|
||||||
fd.description = desc or ""
|
|
||||||
dt[name.."."..fname] = fd
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return doc.format(dt)
|
|
||||||
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"
|
||||||
|
133
lib/download.lua
133
lib/download.lua
@ -1,133 +0,0 @@
|
|||||||
local net = require "minitel"
|
|
||||||
local dl = {}
|
|
||||||
dl.protos = {}
|
|
||||||
|
|
||||||
-- Stolen from the old exec/fget
|
|
||||||
local function parseURL(url)
|
|
||||||
local proto,addr = url:match("(.-)://(.+)")
|
|
||||||
addr = addr or url
|
|
||||||
local hp, path = addr:match("(.-)(/.*)")
|
|
||||||
hp, path = hp or addr, path or "/"
|
|
||||||
local host, port = hp:match("(.+):(.+)")
|
|
||||||
host = host or hp
|
|
||||||
return proto, host, port, path
|
|
||||||
end
|
|
||||||
|
|
||||||
function dl.protos.fget(host, optPort, path, dest) -- string string string number -- boolean -- Downloads path from host (on optPort or 70), printing the directory listing, or saving the file to dest.
|
|
||||||
local socket = assert(net.open(host, optPort or 70))
|
|
||||||
socket:write(string.format("t%s\n", path))
|
|
||||||
local status
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
status = socket:read(1)
|
|
||||||
until status ~= ""
|
|
||||||
|
|
||||||
if status == "d" then
|
|
||||||
io.write("Directory Listing:\n")
|
|
||||||
local tmp = ""
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
tmp = socket:read("*a")
|
|
||||||
|
|
||||||
io.write(tmp)
|
|
||||||
until socket.state == "closed" and tmp == ""
|
|
||||||
|
|
||||||
return true
|
|
||||||
elseif status == "y" then
|
|
||||||
if not dest then
|
|
||||||
error("Must provide local path to save remote files.")
|
|
||||||
end
|
|
||||||
|
|
||||||
io.write(string.format("Saving %s to %s...\n", path, dest))
|
|
||||||
local f = assert(io.open(dest, "wb"))
|
|
||||||
local tmp = ""
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
tmp = socket:read("*a")
|
|
||||||
|
|
||||||
f:write(tmp)
|
|
||||||
until socket.state == "closed" and tmp == ""
|
|
||||||
|
|
||||||
f:close()
|
|
||||||
print("Done.")
|
|
||||||
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local err, tmp = "", ""
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
tmp = socket:read("*a")
|
|
||||||
|
|
||||||
err = err .. tmp
|
|
||||||
until socket.state == "closed" and tmp == ""
|
|
||||||
|
|
||||||
error(string.format("Got error from remote host: %s", err))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function dl.protos.http(host, optPort, path, dest, url) -- string string string number -- boolean -- Downloads *url* to *dest* via the internet card, if available.
|
|
||||||
if not component.list("internet")() then
|
|
||||||
local proto,host,sPort,path = parseURL(url)
|
|
||||||
local proxy = os.getenv(proto:upper().."_PROXY")
|
|
||||||
if not proxy and fs.exists("/boot/cfg/"..proto.."_proxy") then
|
|
||||||
local f = io.open("/boot/cfg/"..proto.."_proxy","rb")
|
|
||||||
proxy = f:read()
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
if not proxy then error("No internet card or HTTP(S) proxy available") end
|
|
||||||
print("Internet card unavailable, falling back to proxy "..proxy)
|
|
||||||
if optPort then host=string.format("%s:%i",host,optPort) end
|
|
||||||
return dl.wget(string.format("%s/%s%s",proxy,host,path),dest)
|
|
||||||
end
|
|
||||||
if not dest then
|
|
||||||
error("Must provide local path to save remote files.")
|
|
||||||
end
|
|
||||||
local R,r=component.invoke(component.list("internet")(),"request",url)
|
|
||||||
if not R then error(r) end
|
|
||||||
repeat
|
|
||||||
ok, err = R.finishConnect()
|
|
||||||
if type(ok) ~= "boolean" then
|
|
||||||
if err == url then
|
|
||||||
return 404, "This is a bug in OC, I think?"
|
|
||||||
end
|
|
||||||
return -1, err or "Connection Error"
|
|
||||||
end
|
|
||||||
coroutine.yield()
|
|
||||||
until ok
|
|
||||||
local code, messsage, headers
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
code, message, headers = R.response()
|
|
||||||
until code or message
|
|
||||||
if code > 299 or code < 200 then
|
|
||||||
return false, code, message
|
|
||||||
end
|
|
||||||
local f=io.open(dest,"wb")
|
|
||||||
if not f then error("Unable to open file "..dest) end
|
|
||||||
io.write(string.format("Saving %s to %s...\n", url, dest))
|
|
||||||
repeat
|
|
||||||
coroutine.yield()
|
|
||||||
ns = R.read()
|
|
||||||
f:write(ns or "")
|
|
||||||
until not ns
|
|
||||||
f:close()
|
|
||||||
print("Done.")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
dl.protos.https = dl.protos.http
|
|
||||||
|
|
||||||
function dl.wget(remotePath, dest) -- string string -- -- Downloads from remote *remotePath* to *dest*
|
|
||||||
local proto, host, sPort, path = parseURL(remotePath)
|
|
||||||
if dl.protos[proto] then
|
|
||||||
local port
|
|
||||||
if sPort then
|
|
||||||
port = tonumber(sPort)
|
|
||||||
end
|
|
||||||
|
|
||||||
dl.protos[proto](host, port, path, dest, remotePath)
|
|
||||||
else
|
|
||||||
error("Unsupported protocol: " .. tostring(proto))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable(dl,{__call=function(_,path,dest) return dl.wget(path,dest) end})
|
|
11
lib/ed.lua
11
lib/ed.lua
@ -17,7 +17,7 @@ end
|
|||||||
function ed.bfunc:save(fpath)
|
function ed.bfunc:save(fpath)
|
||||||
local path = fpath or self.path
|
local path = fpath or self.path
|
||||||
if not path then return false, "no path" end
|
if not path then return false, "no path" end
|
||||||
self.path = path
|
self.path = path
|
||||||
local f = io.open(path,"wb")
|
local f = io.open(path,"wb")
|
||||||
if not f then return false, "unable to open file" end
|
if not f then return false, "unable to open file" end
|
||||||
for k,v in ipairs(self) do
|
for k,v in ipairs(self) do
|
||||||
@ -112,7 +112,6 @@ function ed.newBuffer()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function ed.open(buffer)
|
function ed.open(buffer)
|
||||||
local bpath = buffer
|
|
||||||
if ed.buffers[buffer] then
|
if ed.buffers[buffer] then
|
||||||
buffer = ed.buffers[buffer]
|
buffer = ed.buffers[buffer]
|
||||||
end
|
end
|
||||||
@ -121,9 +120,7 @@ function ed.open(buffer)
|
|||||||
nb:load(buffer)
|
nb:load(buffer)
|
||||||
buffer = nb
|
buffer = nb
|
||||||
end
|
end
|
||||||
if type(buffer) ~= "table" then buffer = ed.newBuffer() end
|
if type(buffer) ~= "table" then buffer = ed.newBuffer() buffer[1] = "" end
|
||||||
buffer[1] = buffer[1] or ""
|
|
||||||
buffer.path = buffer.path or "/"..((bpath:sub(1,1) == "/" and table.concat(fs.segments(path),"/")) or table.concat(fs.segments(os.getenv("PWD").."/"..bpath),"/"))
|
|
||||||
return buffer
|
return buffer
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -163,10 +160,10 @@ function ed.visual(buffer)
|
|||||||
if cx ~= ox or cy ~= oy or force then
|
if cx ~= ox or cy ~= oy or force then
|
||||||
io.write("\27[2J\27[H")
|
io.write("\27[2J\27[H")
|
||||||
for i = cy, cy+my do
|
for i = cy, cy+my do
|
||||||
io.write(string.format("\27[1;%iH\27[31m%4i \27[0m%s",(i-cy+1),i,(buffer[i] or "\27[36m~"):sub(cx,cx+mx-6)))
|
print(string.format("\27[31m%4i \27[0m%s",i,(buffer[i] or "\27[36m~"):sub(cx,cx+mx-6)))
|
||||||
end
|
end
|
||||||
elseif mode == "i" then
|
elseif mode == "i" then
|
||||||
io.write(string.format("\27[2K\27[999D\27[31m%4i \27[0m%s",buffer.y,(buffer[buffer.y] or "\27[36m~"):sub(cx,cx+mx-6)))
|
print(string.format("\27[2K\27[999D\27[31m%4i \27[0m%s",buffer.y,(buffer[buffer.y] or "\27[36m~"):sub(cx,cx+mx-6)))
|
||||||
end
|
end
|
||||||
io.write(string.format("\27[1;%iH\27[0;36;%im\27[2K[%s] ced visual: %i,%i/%i, %iK free %i",my+2,(mode == "c" and 7) or 0, mode, buffer.x, buffer.y, #buffer, computer.freeMemory()//1024,mult))
|
io.write(string.format("\27[1;%iH\27[0;36;%im\27[2K[%s] ced visual: %i,%i/%i, %iK free %i",my+2,(mode == "c" and 7) or 0, mode, buffer.x, buffer.y, #buffer, computer.freeMemory()//1024,mult))
|
||||||
io.write(string.format("\27[%i;%iH\27[0m",buffer.x+6-cx,buffer.y-cy+1))
|
io.write(string.format("\27[%i;%iH\27[0m",buffer.x+6-cx,buffer.y-cy+1))
|
||||||
|
63
lib/interminitel.lua
Normal file
63
lib/interminitel.lua
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
local imt = {}
|
||||||
|
|
||||||
|
imt.ttypes = {}
|
||||||
|
imt.ttypes.string=1
|
||||||
|
imt.ttypes.number=2
|
||||||
|
|
||||||
|
imt.ftypes = {tostring,tonumber}
|
||||||
|
|
||||||
|
function imt.to16bn(n)
|
||||||
|
return string.char(math.floor(n/256))..string.char(math.floor(n%256))
|
||||||
|
end
|
||||||
|
function imt.from16bn(s)
|
||||||
|
return (string.byte(s,1,1)*256)+string.byte(s,2,2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function imt.encodePacket(...)
|
||||||
|
local tArgs = {...}
|
||||||
|
local packet = string.char(#tArgs%256)
|
||||||
|
for _,segment in ipairs(tArgs) do
|
||||||
|
local segtype = type(segment)
|
||||||
|
segment = tostring(segment)
|
||||||
|
packet = packet .. imt.to16bn(segment:len()) .. string.char(imt.ttypes[segtype]) .. tostring(segment)
|
||||||
|
end
|
||||||
|
packet = imt.to16bn(packet:len()) .. packet
|
||||||
|
return packet
|
||||||
|
end
|
||||||
|
|
||||||
|
function imt.decodePacket(s)
|
||||||
|
local function getfirst(n)
|
||||||
|
local ns = s:sub(1,n)
|
||||||
|
s=s:sub(n+1)
|
||||||
|
return ns
|
||||||
|
end
|
||||||
|
if s:len() < 2 then return false end
|
||||||
|
local plen = imt.from16bn(getfirst(2))
|
||||||
|
local segments = {}
|
||||||
|
if s:len() < plen then return false end
|
||||||
|
local nsegments = string.byte(getfirst(1))
|
||||||
|
--print(tostring(plen).." bytes, "..tostring(nsegments).." segments")
|
||||||
|
for i = 1, nsegments do
|
||||||
|
local seglen = imt.from16bn(getfirst(2))
|
||||||
|
local segtype = imt.ftypes[string.byte(getfirst(1))]
|
||||||
|
local segment = segtype(getfirst(seglen))
|
||||||
|
--print(seglen,segtype,segment,type(segment))
|
||||||
|
segments[#segments+1] = segment
|
||||||
|
end
|
||||||
|
return table.unpack(segments)
|
||||||
|
end
|
||||||
|
function imt.getRemainder(s)
|
||||||
|
local function getfirst(n)
|
||||||
|
local ns = s:sub(1,n)
|
||||||
|
s=s:sub(n+1)
|
||||||
|
return ns
|
||||||
|
end
|
||||||
|
local plen = imt.from16bn(getfirst(2))
|
||||||
|
if s:len() > plen then
|
||||||
|
getfirst(plen)
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return imt
|
@ -1,53 +0,0 @@
|
|||||||
local lz = require "lzss"
|
|
||||||
local buffer = require "buffer"
|
|
||||||
|
|
||||||
lz16 = {}
|
|
||||||
|
|
||||||
local function readBuffer(fi)
|
|
||||||
local stream = {}
|
|
||||||
if fi:read(4) ~= "lz16" then
|
|
||||||
return false, "not an lz16 archive"
|
|
||||||
end
|
|
||||||
function stream.read()
|
|
||||||
local len = string.unpack(">I2", fi:read(2) or "\0\0")
|
|
||||||
if len < 1 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
if os.sleep then os.sleep(0) else coroutine.yield() end
|
|
||||||
return lz.decompress(fi:read(len))
|
|
||||||
end
|
|
||||||
function stream.close()
|
|
||||||
fi:close()
|
|
||||||
end
|
|
||||||
return buffer.new("rb",stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function writeBuffer(fo)
|
|
||||||
local stream = {}
|
|
||||||
function stream:write(data)
|
|
||||||
local cblock = lz.compress(data)
|
|
||||||
fo:write(string.pack(">I2", cblock:len()) .. cblock)
|
|
||||||
return cblock:len()+2
|
|
||||||
end
|
|
||||||
function stream.close()
|
|
||||||
fo:close()
|
|
||||||
end
|
|
||||||
fo:write("lz16") -- write header
|
|
||||||
return buffer.new("wb",stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
function lz16.buffer(stream) -- table -- table -- Wrap a stream to read or write LZ16.
|
|
||||||
if stream.mode.w then
|
|
||||||
return writeBuffer(stream)
|
|
||||||
end
|
|
||||||
return readBuffer(stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
function lz16.open(fname, mode) -- string string -- table -- Open file *fname* to read or write LZ16-compressed data depending on *mode*
|
|
||||||
local f = io.open(fname, mode)
|
|
||||||
if not f then return false end
|
|
||||||
f.mode.b = true
|
|
||||||
return lz16.buffer(f)
|
|
||||||
end
|
|
||||||
|
|
||||||
return lz16
|
|
@ -1,49 +0,0 @@
|
|||||||
local mtar = {}
|
|
||||||
local versions = {}
|
|
||||||
versions[0] = {nlf = ">I2", flf = ">I2"} -- original version format
|
|
||||||
versions[1] = {nlf = ">I2", flf = ">I8"} -- extended file size format
|
|
||||||
mtar.versions = versions
|
|
||||||
|
|
||||||
local function cleanPath(path)
|
|
||||||
local pt = {}
|
|
||||||
for segment in path:gmatch("[^/]+") do
|
|
||||||
if segment == ".." then
|
|
||||||
pt[#pt] = nil
|
|
||||||
elseif segment ~= "." then
|
|
||||||
pt[#pt+1] = segment
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return table.concat(pt,"/")
|
|
||||||
end
|
|
||||||
|
|
||||||
function mtar.genHeader(fname,len,version) -- string number -- string -- generate a header for file *fname* when provided with file length *len*
|
|
||||||
version=version or 1
|
|
||||||
return string.format("\255\255%s%s%s%s", string.char(version), string.pack(versions[version].nlf,fname:len()), fname, string.pack(versions[version].flf,len))
|
|
||||||
end
|
|
||||||
|
|
||||||
function mtar.iter(stream) -- table -- function -- Given buffer *stream*, returns an iterator suitable for use with *for* that returns, for each iteration, the file name, a function to read from the file, and the length of the file.
|
|
||||||
local remain = 0
|
|
||||||
local function read(n)
|
|
||||||
local rb = stream:read(math.min(n,remain)) or ""
|
|
||||||
remain = remain - rb:len()
|
|
||||||
return rb
|
|
||||||
end
|
|
||||||
return function()
|
|
||||||
while remain > 0 do
|
|
||||||
remain=remain-(#stream:read(math.min(remain,2048)) or "")
|
|
||||||
end
|
|
||||||
local version = 0
|
|
||||||
local nlen = string.unpack(">I2", stream:read(2) or "\0\0")
|
|
||||||
if nlen == 0 then
|
|
||||||
return
|
|
||||||
elseif nlen == 65535 then -- versioned header
|
|
||||||
version = string.byte(stream:read(1))
|
|
||||||
nlen = string.unpack(versions[version].nlf, stream:read(string.packsize(versions[version].nlf)))
|
|
||||||
end
|
|
||||||
local name = cleanPath(stream:read(nlen))
|
|
||||||
remain = string.unpack(versions[version].flf, stream:read(string.packsize(versions[version].flf)))
|
|
||||||
return name, read, remain
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return mtar
|
|
123
lib/lzss.lua
123
lib/lzss.lua
@ -1,123 +0,0 @@
|
|||||||
--[[----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
LZSS - encoder / decoder
|
|
||||||
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
|
|
||||||
--]]----------------------------------------------------------------------------
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
local M = {}
|
|
||||||
local string, table = string, table
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
local POS_BITS = 12
|
|
||||||
local LEN_BITS = 16 - POS_BITS
|
|
||||||
local POS_SIZE = 1 << POS_BITS
|
|
||||||
local LEN_SIZE = 1 << LEN_BITS
|
|
||||||
local LEN_MIN = 3
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function M.compress(input)
|
|
||||||
local offset, output = 1, {}
|
|
||||||
local window = ''
|
|
||||||
|
|
||||||
local function search()
|
|
||||||
for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do
|
|
||||||
local str = string.sub(input, offset, offset + i - 1)
|
|
||||||
local pos = string.find(window, str, 1, true)
|
|
||||||
if pos then
|
|
||||||
return pos, str
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while offset <= #input do
|
|
||||||
local flags, buffer = 0, {}
|
|
||||||
|
|
||||||
for i = 0, 7 do
|
|
||||||
if offset <= #input then
|
|
||||||
local pos, str = search()
|
|
||||||
if pos and #str >= LEN_MIN then
|
|
||||||
local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN)
|
|
||||||
buffer[#buffer + 1] = string.pack('>I2', tmp)
|
|
||||||
else
|
|
||||||
flags = flags | (1 << i)
|
|
||||||
str = string.sub(input, offset, offset)
|
|
||||||
buffer[#buffer + 1] = str
|
|
||||||
end
|
|
||||||
window = string.sub(window .. str, -POS_SIZE)
|
|
||||||
offset = offset + #str
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #buffer > 0 then
|
|
||||||
output[#output + 1] = string.char(flags)
|
|
||||||
output[#output + 1] = table.concat(buffer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function M.decompress(input)
|
|
||||||
local offset, output = 1, {}
|
|
||||||
local window = ''
|
|
||||||
|
|
||||||
while offset <= #input do
|
|
||||||
local flags = string.byte(input, offset)
|
|
||||||
offset = offset + 1
|
|
||||||
|
|
||||||
for i = 1, 8 do
|
|
||||||
local str = nil
|
|
||||||
if (flags & 1) ~= 0 then
|
|
||||||
if offset <= #input then
|
|
||||||
str = string.sub(input, offset, offset)
|
|
||||||
offset = offset + 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if offset + 1 <= #input then
|
|
||||||
local tmp = string.unpack('>I2', input, offset)
|
|
||||||
offset = offset + 2
|
|
||||||
local pos = (tmp >> LEN_BITS) + 1
|
|
||||||
local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN
|
|
||||||
str = string.sub(window, pos, pos + len - 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
flags = flags >> 1
|
|
||||||
if str then
|
|
||||||
output[#output + 1] = str
|
|
||||||
window = string.sub(window .. str, -POS_SIZE)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -1,11 +1,12 @@
|
|||||||
local computer = require "computer"
|
local computer = require "computer"
|
||||||
local minitel = require "minitel"
|
local minitel = require "minitel"
|
||||||
local event = require "event"
|
local event = require "event"
|
||||||
|
local ufs = require "unionfs"
|
||||||
local rpc = require "rpc"
|
local rpc = require "rpc"
|
||||||
local netutil = {}
|
local netutil = {}
|
||||||
|
|
||||||
function netutil.importfs(host,rpath,lpath) -- string string string -- boolean -- Import filesystem *rpath* from *host* and attach it to *lpath*.
|
function netutil.importfs(host,rpath,lpath) -- string string string -- boolean -- Import filesystem *rpath* from *host* and attach it to *lpath*.
|
||||||
local px = rpc.proxy(host,"fs_"..rpath.."_")
|
local px = rpc.proxy(host,rpath.."_")
|
||||||
function px.getLabel()
|
function px.getLabel()
|
||||||
return host..":"..rpath
|
return host..":"..rpath
|
||||||
end
|
end
|
||||||
@ -15,18 +16,10 @@ end
|
|||||||
|
|
||||||
function netutil.exportfs(path) -- string -- boolean -- Export the directory *path* over RPC.
|
function netutil.exportfs(path) -- string -- boolean -- Export the directory *path* over RPC.
|
||||||
local path = "/"..table.concat(fs.segments(path),"/")
|
local path = "/"..table.concat(fs.segments(path),"/")
|
||||||
local px = require("unionfs").create(path)
|
local px = ufs.create(path)
|
||||||
function px.dirstat(p)
|
|
||||||
local rt = {}
|
|
||||||
for k,v in ipairs(px.list(p)) do
|
|
||||||
local fp = p.."/"..v
|
|
||||||
rt[v] = {px.isDirectory(fp), px.size(fp), px.lastModified(fp)}
|
|
||||||
end
|
|
||||||
return rt
|
|
||||||
end
|
|
||||||
for k,v in pairs(px) do
|
for k,v in pairs(px) do
|
||||||
rpc.register("fs_"..path.."_"..k,v)
|
rpc.register(path.."_"..k,v)
|
||||||
print("fs_"..path.."_"..k)
|
print(path.."_"..k)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
146
lib/pkgfs.lua
146
lib/pkgfs.lua
@ -1,146 +0,0 @@
|
|||||||
local mtar = require "libmtar"
|
|
||||||
local w, lz16 = pcall(require, "liblz16")
|
|
||||||
if not w then lz16 = nil end
|
|
||||||
|
|
||||||
pkgfs = {}
|
|
||||||
pkgfs.files = {}
|
|
||||||
local findex = {}
|
|
||||||
local handles = {}
|
|
||||||
local hc = 0
|
|
||||||
|
|
||||||
local function rfalse()
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local function rzero()
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
pkgfs.component = {seek = rfalse, makeDirectory = rfalse, write = rfalse, rename = rfalse, setlabel = rfalse, spaceUsed = rzero, spaceTotal = rzero, lastModified = rzero, address = "pkgfs"}
|
|
||||||
|
|
||||||
local function fopen(path,comp)
|
|
||||||
local f
|
|
||||||
if comp and lz16 then
|
|
||||||
f = lz16.open(path,"rb")
|
|
||||||
else
|
|
||||||
f = io.open(path,"rb")
|
|
||||||
end
|
|
||||||
return f
|
|
||||||
end
|
|
||||||
|
|
||||||
local function fnormalize(s)
|
|
||||||
return table.concat(fs.segments(s),"/")
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.component.exists(path)
|
|
||||||
path = fnormalize(path)
|
|
||||||
return findex[path] and true
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.component.list(path)
|
|
||||||
path = fnormalize(path).."/"
|
|
||||||
local ft,rt = {},{}
|
|
||||||
for k,v in pairs(findex) do
|
|
||||||
k="/"..k
|
|
||||||
if k:match(path.."([^/]+)/.+") then
|
|
||||||
ft[k:match(path.."([^/]+)/.+").."/"] = true
|
|
||||||
elseif k:match(path.."([^/]+)") then
|
|
||||||
ft[k:match(path.."([^/]+)")] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for k,v in pairs(ft) do
|
|
||||||
rt[#rt+1] = k
|
|
||||||
end
|
|
||||||
return rt
|
|
||||||
end
|
|
||||||
function pkgfs.component.isDirectory(path)
|
|
||||||
path = fnormalize(path).."/"
|
|
||||||
for k,v in pairs(findex) do
|
|
||||||
k="/"..k
|
|
||||||
if k:match(path.."([^/]+)/.+") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
function pkgfs.component.size(path)
|
|
||||||
path=fnormalize(path)
|
|
||||||
if not findex[path] then return false end
|
|
||||||
local f = fopen(findex[path][1], findex[path][2])
|
|
||||||
for fname, read, fsize in mtar.iter(f) do
|
|
||||||
if fname == path then
|
|
||||||
return fsize
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.component.open(path,mode)
|
|
||||||
path=fnormalize(path)
|
|
||||||
if mode:find("w") or mode:find("a") or not findex[path] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local f = fopen(findex[path][1],findex[path][2])
|
|
||||||
for fname,read,fsize in mtar.iter(f) do
|
|
||||||
if fname == path then
|
|
||||||
hc = hc + 1
|
|
||||||
handles[hc] = {read, f}
|
|
||||||
return hc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.component.read(handle, n)
|
|
||||||
if not handles[handle] then return false end
|
|
||||||
local rv = handles[handle][1](n)
|
|
||||||
if not rv then return nil end
|
|
||||||
if rv:len() < 1 then return nil end
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.component.close(handle)
|
|
||||||
if not handles[handle] then return false end
|
|
||||||
handles[handle][2]:close()
|
|
||||||
handles[handle] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function index()
|
|
||||||
findex = {}
|
|
||||||
for k,v in pairs(pkgfs.files) do
|
|
||||||
fname, comp = v[1], v[2]
|
|
||||||
if fname:sub(1,1) ~= "/" then
|
|
||||||
fname = "/"..fnormalize(os.getenv("PWD").."/"..fname)
|
|
||||||
end
|
|
||||||
local f = fopen(fname,comp)
|
|
||||||
if not f then error("unable to open file "..fname) end
|
|
||||||
for name, read, fsize in mtar.iter(f) do
|
|
||||||
findex[fnormalize(name)] = {fname,comp}
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkgfs.add(fname,comp) -- string boolean -- boolean -- Add a package as specified in *fname* to the pkgfs component. If *comp* is true, read it as a LZ16-compressed package.
|
|
||||||
pkgfs.files[#pkgfs.files+1] = {fname,comp}
|
|
||||||
return index()
|
|
||||||
end
|
|
||||||
function pkgfs.remove(fname) -- string -- boolean -- Removes the package specified by *fname* from the pkgfs index.
|
|
||||||
for k,v in pairs(pkgfs.files) do
|
|
||||||
if v[1] == fname then
|
|
||||||
table.remove(pkgfs.files,k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return index()
|
|
||||||
end
|
|
||||||
|
|
||||||
fs.makeDirectory("/pkg")
|
|
||||||
fs.mount("/pkg",pkgfs.component)
|
|
||||||
for _,file in ipairs(fs.list("/boot/pkg/")) do
|
|
||||||
if file:sub(-5) == ".mtar" then
|
|
||||||
pcall(pkgfs.add,"/boot/pkg/"..file)
|
|
||||||
elseif file:sub(-9) == ".mtar.lss" then
|
|
||||||
pcall(pkgfs.add,"/boot/pkg/"..file,true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return pkgfs
|
|
193
lib/pkgman.lua
193
lib/pkgman.lua
@ -1,193 +0,0 @@
|
|||||||
local serial = require "serialization"
|
|
||||||
local dl = require "download"
|
|
||||||
local mtar = require "libmtar"
|
|
||||||
local pkg = {}
|
|
||||||
pkg.cfgPath = "/boot/cfg/pkg"
|
|
||||||
pkg.sourcePath = pkg.cfgPath .. "/sources.cfg"
|
|
||||||
pkg.installedPath = pkg.cfgPath .. "/installed.cfg"
|
|
||||||
local w, lz16 = pcall(require,"liblz16")
|
|
||||||
if not w then lz16 = nil end
|
|
||||||
fs.makeDirectory("/boot/pkg")
|
|
||||||
fs.makeDirectory("/boot/cfg/pkg")
|
|
||||||
require "pkgfs"
|
|
||||||
local kver,kvar = _OSVERSION:match("(%x+)%-([^-]-)$")
|
|
||||||
kver,kvar = kver or "unknown", kvar or "base"
|
|
||||||
|
|
||||||
local function getSources()
|
|
||||||
local f = io.open(pkg.sourcePath,"rb")
|
|
||||||
if not f then return {main={path="https://oc.shadowkat.net/psychos/pkg",cache=true,name="main"}} end
|
|
||||||
local c = f:read("*a")
|
|
||||||
f:close()
|
|
||||||
return serial.unserialize(c)
|
|
||||||
end
|
|
||||||
local function saveSources(t)
|
|
||||||
fs.makeDirectory(pkg.cfgPath)
|
|
||||||
local f = io.open(pkg.sourcePath,"wb")
|
|
||||||
f:write(serial.serialize(t))
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getInstalled()
|
|
||||||
local f = io.open(pkg.installedPath,"rb")
|
|
||||||
if not f then return {psychos={version=kver},["kernel-"..kvar]={version=kver}} end
|
|
||||||
local c = f:read("*a")
|
|
||||||
f:close()
|
|
||||||
return serial.unserialize(c)
|
|
||||||
end
|
|
||||||
local function saveInstalled(t)
|
|
||||||
fs.makeDirectory(pkg.cfgPath)
|
|
||||||
local f = io.open(pkg.installedPath,"wb")
|
|
||||||
if not f then return false end
|
|
||||||
f:write(serial.serialize(t))
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getRepoMeta(repo)
|
|
||||||
if not getSources()[repo].cache or not fs.exists("/boot/cfg/pkg/repo-"..repo..".cfg") then
|
|
||||||
dl(getSources()[repo].path.."/packages.cfg","/boot/cfg/pkg/repo-"..repo..".cfg")
|
|
||||||
end
|
|
||||||
local f = io.open("/boot/cfg/pkg/repo-"..repo..".cfg","rb")
|
|
||||||
local rt = serial.unserialize(f:read("*a"))
|
|
||||||
f:close()
|
|
||||||
if not getSources()[repo].cache then
|
|
||||||
fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
|
|
||||||
end
|
|
||||||
return rt
|
|
||||||
end
|
|
||||||
|
|
||||||
local function activatePackage(path,compressed)
|
|
||||||
require("pkgfs").add(path,compressed)
|
|
||||||
end
|
|
||||||
local function deactivatePackage(path)
|
|
||||||
require("pkgfs").remove(path)
|
|
||||||
end
|
|
||||||
local function fnormalize(s)
|
|
||||||
return table.concat(fs.segments(s),"/")
|
|
||||||
end
|
|
||||||
local function installSystemPackage(path,comp)
|
|
||||||
local f
|
|
||||||
if comp and lz16 then
|
|
||||||
f = lz16.open(path,"rb")
|
|
||||||
else
|
|
||||||
f = io.open(path,"rb")
|
|
||||||
end
|
|
||||||
for fname, read, size in mtar.iter(f) do
|
|
||||||
local opath = "/boot/"..fnormalize(fname)
|
|
||||||
print(opath..": "..tostring(size))
|
|
||||||
fs.makeDirectory(opath:match("(.+)/[^/]+"))
|
|
||||||
local of = io.open(opath,"wb")
|
|
||||||
if not of then error("unable to open "..opath.." for writing") end
|
|
||||||
local tmp
|
|
||||||
repeat
|
|
||||||
tmp = read(2048) or ""
|
|
||||||
of:write(tmp)
|
|
||||||
until not tmp or tmp:len() < 1
|
|
||||||
of:close()
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.addRepo(name,path,cache) -- string string boolean -- boolean -- Adds a repository, referred to as *name*, to the list of package sources, with the remote path *path*. If *cache* is set, keep a local copy of the repository index.
|
|
||||||
local sources = getSources()
|
|
||||||
sources[name] = {path=path,cache=cache,name=name}
|
|
||||||
saveSources(sources)
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.delRepo(name) -- string -- boolean -- Removes a repository from the list of repositories.
|
|
||||||
local sources = getSources()
|
|
||||||
sources[name] = nil
|
|
||||||
saveSources(sources)
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.update() -- Re-download cached repository indexes.
|
|
||||||
for repo,meta in pairs(getSources()) do
|
|
||||||
fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
|
|
||||||
if meta.cache then
|
|
||||||
getRepoMeta(repo)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.list(filter,installed) -- string boolean -- -- Print a list of available packages matching *filter*, optionally filtering to only installed packages if *installed* is set.
|
|
||||||
filter = filter or ""
|
|
||||||
local pkglist = {}
|
|
||||||
for repo,_ in pairs(getSources()) do
|
|
||||||
for pkg,meta in pairs(getRepoMeta(repo)) do
|
|
||||||
if pkg:find(filter) or (pkg.meta or ""):find(filter) then
|
|
||||||
meta.repo = repo
|
|
||||||
pkglist[pkg] = meta
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for k,v in pairs(pkglist) do
|
|
||||||
if v.system then io.write("\27[31m") end
|
|
||||||
print(string.format("%s/%s: %s\27[0m\n %s\n Authors: %s",v.repo,k,v.name,v.description,v.authors))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.getMeta(pkgname) -- string -- table -- Returns the metadata for a the package specified in *pkgname*.
|
|
||||||
print("Finding package "..pkgname)
|
|
||||||
for repo,info in pairs(getSources()) do
|
|
||||||
local pkg = getRepoMeta(repo)[pkgname]
|
|
||||||
if pkg then
|
|
||||||
print("Package "..pkgname.." located in repo "..repo.." at "..info.path)
|
|
||||||
pkg.repository = info
|
|
||||||
return pkg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.get(pkgname,auto) -- string boolean -- boolean -- Downloads and mounts a package, identified as *pkgname,* onto the pkgfs. Setting *auto* will flag the package as automatically installed; this is used for dependencies.
|
|
||||||
local pkginfo = pkg.getMeta(pkgname)
|
|
||||||
if not pkginfo then error("unable to locate package "..pkgname) end
|
|
||||||
pkginfo.manual = not auto
|
|
||||||
fs.makeDirectory("/boot/pkg")
|
|
||||||
for k,v in ipairs(pkginfo.dependencies or {}) do
|
|
||||||
if not getInstalled()[v] then
|
|
||||||
pkg.get(v,true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
dl(pkginfo.repository.path.."/"..pkginfo.filename,"/boot/pkg/"..pkginfo.filename)
|
|
||||||
local installed = getInstalled()
|
|
||||||
installed[pkgname] = pkginfo
|
|
||||||
saveInstalled(installed)
|
|
||||||
if pkginfo.system then
|
|
||||||
local rv = installSystemPackage("/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
|
|
||||||
fs.remove("/boot/pkg/"..pkginfo.filename)
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
pcall(activatePackage,"/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.upgrade(force) -- boolean -- -- Upgrades all packages on the system to the current version stored in the relevant repository. If *force* is set, re-download all packages.
|
|
||||||
pkg.update()
|
|
||||||
fs.makeDirectory("/boot/pkg")
|
|
||||||
local installed = getInstalled()
|
|
||||||
for repo,info in pairs(getSources()) do
|
|
||||||
for pkgname,pkginfo in pairs(getRepoMeta(repo)) do
|
|
||||||
if installed[pkgname] and pkginfo.version ~= installed[pkgname].version or force then
|
|
||||||
pkg.remove(pkgname)
|
|
||||||
pkg.get(pkgname,pkginfo.auto)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function pkg.remove(pkgname) -- string -- boolean -- Remove the package *pkgname* from the pkgfs and package directory.
|
|
||||||
local installed = getInstalled()
|
|
||||||
local pkginfo = installed[pkgname]
|
|
||||||
if not pkginfo then return true end
|
|
||||||
pcall(deactivatePackage,"/boot/pkg/"..pkginfo.filename)
|
|
||||||
fs.remove("/boot/pkg/"..pkginfo.filename)
|
|
||||||
if pkginfo.system then
|
|
||||||
for k,v in pairs(pkginfo.files) do
|
|
||||||
fs.remove(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
installed[pkgname] = nil
|
|
||||||
saveInstalled(installed)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return pkg
|
|
22
lib/rc.lua
22
lib/rc.lua
@ -1,11 +1,10 @@
|
|||||||
local serial = require "serialization"
|
local serial = require "serialization"
|
||||||
|
|
||||||
local rc = {}
|
local rc = {}
|
||||||
rc.paths = "/boot/service\n/pkg/service"
|
|
||||||
rc.pids = {}
|
rc.pids = {}
|
||||||
local service = {}
|
local service = {}
|
||||||
local cfg = {}
|
local cfg = {}
|
||||||
cfg.enabled = {"getty","minitel","fsmanager"}
|
cfg.enabled = {"getty","minitel"}
|
||||||
|
|
||||||
local function loadConfig()
|
local function loadConfig()
|
||||||
local f = io.open("/boot/cfg/rc.cfg","rb")
|
local f = io.open("/boot/cfg/rc.cfg","rb")
|
||||||
@ -24,17 +23,16 @@ local function saveConfig()
|
|||||||
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) -- string boolean -- table -- Attempts to load service *name*, and if *force* is true, replaces the current instance.
|
||||||
if not service[name] or force then
|
if force then
|
||||||
for d in rc.paths:gmatch("[^\n]+") do
|
rc.stop(name)
|
||||||
if fs.exists(d.."/"..name..".lua") then
|
service[name] = nil
|
||||||
service[name] = runfile(d.."/"..name..".lua")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if service[name] then
|
if service[name] then return true end
|
||||||
return service[name]
|
service[name] = setmetatable({},{__index=_G})
|
||||||
end
|
local f = io.open("/boot/service/"..name..".lua","rb")
|
||||||
return false, "unable to load service "..name
|
local res = load(f:read("*a"),name,"t",service[name])()
|
||||||
|
f:close()
|
||||||
|
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,...) -- string -- boolean string -- Stops service *name*, supplying *...* to the stop function. Returns false and a reason if this fails.
|
||||||
|
56
lib/rpc.lua
56
lib/rpc.lua
@ -5,36 +5,19 @@ local rpc = {}
|
|||||||
_G.rpcf = {}
|
_G.rpcf = {}
|
||||||
rpc.port = 111
|
rpc.port = 111
|
||||||
|
|
||||||
local function setacl(self, fname, host)
|
|
||||||
self[fname] = self[fname] or {}
|
|
||||||
self[fname][host] = true
|
|
||||||
end
|
|
||||||
-- function rpc.allow(fn, host) -- string string -- -- Enable the allow list for function *fname* and add *host* to it.
|
|
||||||
-- function rpc.deny(fn, host) -- string string -- -- Enable the deny list for function *fname* and add *host* to it.
|
|
||||||
rpc.allow = setmetatable({},{__call=setacl})
|
|
||||||
rpc.deny = setmetatable({},{__call=setacl})
|
|
||||||
|
|
||||||
local function isPermitted(host,fn)
|
|
||||||
if rpc.allow[fn] then
|
|
||||||
return rpc.allow[fn][host] or false
|
|
||||||
end
|
|
||||||
if rpc.deny[fn] and rpc.deny[fn][host] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function rpcexec(_, from, port, data)
|
local function rpcexec(_, from, port, data)
|
||||||
if port ~= rpc.port then return false end
|
if port == rpc.port then
|
||||||
os.spawn(function()
|
local rpcrq = serial.unserialize(data)
|
||||||
local rpcrq = serial.unserialize(data) or {}
|
local rpcn, rpcid = table.remove(rpcrq,1), table.remove(rpcrq,1)
|
||||||
if rpcf[rpcrq[1]] and isPermitted(from,rpcrq[1]) then
|
if rpcf[rpcn] then
|
||||||
os.setenv("RPC_CLIENT",from)
|
local rt = {pcall(rpcf[rpcn],table.unpack(rpcrq))}
|
||||||
minitel.send(from,port,serial.serialize({rpcrq[2],pcall(rpcf[rpcrq[1]],table.unpack(rpcrq,3))}))
|
if rt[1] == true then
|
||||||
elseif type(rpcrq[2]) == "string" then
|
table.remove(rt,1)
|
||||||
minitel.send(from,port,serial.serialize({rpcrq[2],false,"function unavailable"}))
|
end
|
||||||
|
minitel.send(from,port,serial.serialize({rpcid,table.unpack(rt)}))
|
||||||
|
else
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,"rpc worker for "..tostring(from))
|
|
||||||
end
|
end
|
||||||
function rpcf.list()
|
function rpcf.list()
|
||||||
local rt = {}
|
local rt = {}
|
||||||
@ -56,13 +39,10 @@ function rpc.call(hostname,fn,...) -- string string -- boolean -- Calls exported
|
|||||||
local _, from, port, data = event.pull(30, "net_msg", hostname, rpc.port)
|
local _, from, port, data = event.pull(30, "net_msg", hostname, rpc.port)
|
||||||
rt = serial.unserialize(tostring(data)) or {}
|
rt = serial.unserialize(tostring(data)) or {}
|
||||||
until (type(rt) == "table" and rt[1] == rv) or computer.uptime() > st + 30
|
until (type(rt) == "table" and rt[1] == rv) or computer.uptime() > st + 30
|
||||||
if rt[1] == rv then
|
if table.remove(rt,1) == rv then
|
||||||
if rt[2] then
|
return table.unpack(rt)
|
||||||
return table.unpack(rt,3)
|
|
||||||
end
|
|
||||||
error(rt[3])
|
|
||||||
end
|
end
|
||||||
error("timed out")
|
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) -- string string -- table -- Returns a component.proxy()-like table from the functions on *hostname* with names matching *filter*.
|
||||||
filter=(filter or "").."(.+)"
|
filter=(filter or "").."(.+)"
|
||||||
@ -89,17 +69,11 @@ function rpc.register(name,fn) -- string function -- -- Registers a function to
|
|||||||
if not rpcrunning then
|
if not rpcrunning then
|
||||||
os.spawn(function()
|
os.spawn(function()
|
||||||
while true do
|
while true do
|
||||||
pcall(rpcexec,event.pull("net_msg"))
|
rpcexec(event.pull("net_msg"))
|
||||||
end
|
end
|
||||||
end,"rpc daemon")
|
end,"rpc daemon")
|
||||||
end
|
end
|
||||||
rpcf[name] = fn
|
rpcf[name] = fn
|
||||||
end
|
end
|
||||||
|
|
||||||
function rpc.unregister(name) -- string -- -- Removes a function from the RPC function registry, clearing any ACL rules.
|
|
||||||
rpcf[name] = nil
|
|
||||||
rpc.allow[name] = nil
|
|
||||||
rpc.deny[name] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return rpc
|
return rpc
|
||||||
|
@ -21,29 +21,24 @@ end
|
|||||||
function shell.interactive()
|
function shell.interactive()
|
||||||
local shenv = setmetatable({}, {__index=shindex})
|
local shenv = setmetatable({}, {__index=shindex})
|
||||||
local run = true
|
local run = true
|
||||||
os.setenv("PATH",{"/boot/exec","/pkg/exec"})
|
|
||||||
function shenv.quit()
|
function shenv.quit()
|
||||||
run = false
|
run = false
|
||||||
end
|
end
|
||||||
while run do
|
while run do
|
||||||
io.write(string.format("\27[32m%s:%s>\27[0m ",os.getenv("HOSTNAME") or "localhost",(os.getenv("PWD") or _VERSION)))
|
io.write(string.format("\27[32m%s:%s>\27[0m ",os.getenv("HOSTNAME") or "localhost",(os.getenv("PWD") or _VERSION)))
|
||||||
local w,input = pcall(io.read)
|
local input = io.read()
|
||||||
if not w then
|
if input:sub(1,1) == "=" then
|
||||||
print("\27[31m^C")
|
input = "return "..input:sub(2)
|
||||||
|
end
|
||||||
|
local f, r = load(input, "shell", "t", shenv)
|
||||||
|
if not f then
|
||||||
|
print("\27[31m"..r)
|
||||||
else
|
else
|
||||||
if input:sub(1,1) == "=" then
|
local rt = {pcall(f)}
|
||||||
input = "return "..input:sub(2)
|
local rs = table.remove(rt,1)
|
||||||
end
|
if not rs then io.write("\27[31m") end
|
||||||
local f, r = load(input, "shell", "t", shenv)
|
for k,v in pairs(rt) do
|
||||||
if not f then
|
print(formatValue(v))
|
||||||
print("\27[31m"..r)
|
|
||||||
else
|
|
||||||
local rt = {xpcall(f,debug.traceback)}
|
|
||||||
local rs = table.remove(rt,1)
|
|
||||||
if not rs then io.write("\27[31m") end
|
|
||||||
for k,v in pairs(rt) do
|
|
||||||
print(formatValue(v))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -83,7 +83,7 @@ function shutil.df() -- Prints free disk space.
|
|||||||
local fstr = "%-"..tostring(ml).."s %5s %5s"
|
local fstr = "%-"..tostring(ml).."s %5s %5s"
|
||||||
print("fs"..(" "):rep(ml-2).." size used")
|
print("fs"..(" "):rep(ml-2).." size used")
|
||||||
for k,v in pairs(mt) do
|
for k,v in pairs(mt) do
|
||||||
local st, su = fs.spaceTotal("/"..v), fs.spaceUsed("/"..v)
|
local st, su = fs.spaceTotal(v), fs.spaceUsed(v)
|
||||||
print(string.format(fstr,v,wrapUnits(st),wrapUnits(su)))
|
print(string.format(fstr,v,wrapUnits(st),wrapUnits(su)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -119,38 +119,9 @@ function shutil.free() -- Displays used and free memory.
|
|||||||
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
|
||||||
|
|
||||||
function shutil.which(name)
|
|
||||||
local fpath
|
|
||||||
for _,dir in ipairs(os.getenv("PATH")) do
|
|
||||||
fpath = fpath or fs.exists(string.format("%s/%s.lua",dir,name)) and string.format("%s/%s.lua",dir,name) or fs.exists(string.format("%s/%s",dir,name)) and string.format("%s/%s",dir,name)
|
|
||||||
end
|
|
||||||
return fpath
|
|
||||||
end
|
|
||||||
|
|
||||||
shutil.cd = os.chdir
|
shutil.cd = os.chdir
|
||||||
shutil.mkdir = fs.makeDirectory
|
shutil.mkdir = fs.makeDirectory
|
||||||
shutil.cp = fs.copy
|
shutil.cp = fs.copy
|
||||||
shutil.rm = fs.remove
|
shutil.rm = fs.remove
|
||||||
|
|
||||||
return setmetatable({}, {__index = function(t,k)
|
return shutil
|
||||||
if shutil[k] then
|
|
||||||
return shutil[k]
|
|
||||||
end
|
|
||||||
local path = shutil.which(k)
|
|
||||||
if path then
|
|
||||||
local fn, e = loadfile(path)
|
|
||||||
if not fn then error(string.format("\n - %s",e)) end
|
|
||||||
return function(...)
|
|
||||||
local tA = {...}
|
|
||||||
local pid = os.spawn(function() return fn(table.unpack(tA)) end,path)
|
|
||||||
local ret = {require("event").pull("process_finished",pid)}
|
|
||||||
if not ret[3] then
|
|
||||||
error(string.format("\n - %s",ret[4]))
|
|
||||||
end
|
|
||||||
for i = 1, 3 do
|
|
||||||
table.remove(ret,1)
|
|
||||||
end
|
|
||||||
return table.unpack(ret)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end})
|
|
||||||
|
114
lib/unionfs.lua
Normal file
114
lib/unionfs.lua
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
local unionfs = {}
|
||||||
|
|
||||||
|
local function normalise(path)
|
||||||
|
return table.concat(fs.segments(path),"/")
|
||||||
|
end
|
||||||
|
|
||||||
|
function unionfs.create(...) -- string -- table -- Returns a unionfs object of the directories specified in *...*.
|
||||||
|
local paths,fids,fc = {...}, {}, 0
|
||||||
|
for k,v in pairs(paths) do
|
||||||
|
paths[k] = "/"..normalise(v)
|
||||||
|
end
|
||||||
|
local proxy = {}
|
||||||
|
local function realpath(path)
|
||||||
|
path = path or ""
|
||||||
|
for k,v in pairs(paths) do
|
||||||
|
if fs.exists(v.."/"..path) then
|
||||||
|
return v.."/"..path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return paths[1].."/"..path
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.setLabel()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.spaceUsed()
|
||||||
|
return fs.spaceUsed(paths[1])
|
||||||
|
end
|
||||||
|
function proxy.spaceTotal()
|
||||||
|
return fs.spaceTotal(paths[1])
|
||||||
|
end
|
||||||
|
function proxy.isReadOnly()
|
||||||
|
return fs.isReadOnly(paths[1])
|
||||||
|
end
|
||||||
|
function proxy.isDirectory(path)
|
||||||
|
return fs.isDirectory(realpath(path))
|
||||||
|
end
|
||||||
|
function proxy.lastModified(path)
|
||||||
|
return fs.lastModified(realpath(path))
|
||||||
|
end
|
||||||
|
function proxy.getLabel()
|
||||||
|
return fs.getLabel(paths[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.exists(path)
|
||||||
|
return fs.exists(realpath(path))
|
||||||
|
end
|
||||||
|
function proxy.remove(path)
|
||||||
|
return fs.remove(realpath(path))
|
||||||
|
end
|
||||||
|
function proxy.size(path)
|
||||||
|
return fs.size(realpath(path))
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.list(path)
|
||||||
|
local nt,rt = {},{}
|
||||||
|
if #fs.segments(path) < 1 then
|
||||||
|
for k,v in pairs(paths) do
|
||||||
|
for l,m in ipairs(fs.list(v.."/"..path)) do
|
||||||
|
nt[m] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k,v in pairs(nt) do
|
||||||
|
rt[#rt+1] = k
|
||||||
|
end
|
||||||
|
table.sort(rt)
|
||||||
|
return rt
|
||||||
|
else
|
||||||
|
return fs.list(realpath(path))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.open(path,mode)
|
||||||
|
local fh, r = fs.open(realpath(path),mode)
|
||||||
|
if not fh then return fh, r end
|
||||||
|
fids[fc] = fh
|
||||||
|
fc = fc + 1
|
||||||
|
return fc - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy.close(fid)
|
||||||
|
if not fids[fid] then
|
||||||
|
return false, "file not open"
|
||||||
|
end
|
||||||
|
local rfh = fids[fid]
|
||||||
|
fids[fid] = nil
|
||||||
|
return rfh:close()
|
||||||
|
end
|
||||||
|
function proxy.write(fid,d)
|
||||||
|
if not fids[fid] then
|
||||||
|
return false, "file not open"
|
||||||
|
end
|
||||||
|
return fids[fid]:write(d)
|
||||||
|
end
|
||||||
|
function proxy.read(fid,d)
|
||||||
|
if not fids[fid] then
|
||||||
|
return false, "file not open"
|
||||||
|
end
|
||||||
|
local rb = fids[fid]:read(d)
|
||||||
|
if rb == "" then rb = nil end
|
||||||
|
return rb
|
||||||
|
end
|
||||||
|
function proxy.seek(fid,d)
|
||||||
|
if not fids[fid] then
|
||||||
|
return false, "file not open"
|
||||||
|
end
|
||||||
|
return fids[fid]:seek(d)
|
||||||
|
end
|
||||||
|
|
||||||
|
return proxy
|
||||||
|
end
|
||||||
|
|
||||||
|
return unionfs
|
197
lib/vcomponent.lua
Normal file
197
lib/vcomponent.lua
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
local proxylist = {}
|
||||||
|
local proxyobjs = {}
|
||||||
|
local typelist = {}
|
||||||
|
local doclist = {}
|
||||||
|
|
||||||
|
local oproxy = component.proxy
|
||||||
|
function component.proxy(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if proxyobjs[address] ~= nil then
|
||||||
|
return proxyobjs[address]
|
||||||
|
end
|
||||||
|
return oproxy(address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local olist = component.list
|
||||||
|
function component.list(filter, exact)
|
||||||
|
checkArg(1,filter,"string","nil")
|
||||||
|
local result = {}
|
||||||
|
local data = {}
|
||||||
|
for k,v in olist(filter, exact) do
|
||||||
|
data[#data + 1] = k
|
||||||
|
data[#data + 1] = v
|
||||||
|
result[k] = v
|
||||||
|
end
|
||||||
|
for k,v in pairs(typelist) do
|
||||||
|
if filter == nil or (exact and v == filter) or (not exact and v:find(filter, nil, true)) then
|
||||||
|
data[#data + 1] = k
|
||||||
|
data[#data + 1] = v
|
||||||
|
result[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local place = 1
|
||||||
|
return setmetatable(result,
|
||||||
|
{__call=function()
|
||||||
|
local addr,type = data[place], data[place + 1]
|
||||||
|
place = place + 2
|
||||||
|
return addr, type
|
||||||
|
end}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local otype = component.type
|
||||||
|
function component.type(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if typelist[address] ~= nil then
|
||||||
|
return typelist[address]
|
||||||
|
end
|
||||||
|
return otype(address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local odoc = component.doc
|
||||||
|
function component.doc(address, method)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
checkArg(2,method,"string")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
if proxylist[address][method] == nil then
|
||||||
|
error("no such method",2)
|
||||||
|
end
|
||||||
|
if doclist[address] ~= nil then
|
||||||
|
return doclist[address][method]
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return odoc(address, method)
|
||||||
|
end
|
||||||
|
|
||||||
|
local oslot = component.slot
|
||||||
|
function component.slot(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
return -1 -- vcomponents do not exist in a slot
|
||||||
|
end
|
||||||
|
return oslot(address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local omethods = component.methods
|
||||||
|
function component.methods(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
local methods = {}
|
||||||
|
for k,v in pairs(proxylist[address]) do
|
||||||
|
if type(v) == "function" then
|
||||||
|
methods[k] = true -- All vcomponent methods are direct
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return methods
|
||||||
|
end
|
||||||
|
return omethods(address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local oinvoke = component.invoke
|
||||||
|
function component.invoke(address, method, ...)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
checkArg(2,method,"string")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
if proxylist[address][method] == nil then
|
||||||
|
error("no such method",2)
|
||||||
|
end
|
||||||
|
return proxylist[address][method](...)
|
||||||
|
end
|
||||||
|
return oinvoke(address, method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ofields = component.fields
|
||||||
|
function component.fields(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
return {} -- What even is this?
|
||||||
|
end
|
||||||
|
return ofields(address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local componentCallback =
|
||||||
|
{
|
||||||
|
__call = function(self, ...) return proxylist[self.address][self.name](...) end,
|
||||||
|
__tostring = function(self) return (doclist[self.address] ~= nil and doclist[self.address][self.name] ~= nil) and doclist[self.address][self.name] or "function" end
|
||||||
|
}
|
||||||
|
|
||||||
|
local vcomponent = {}
|
||||||
|
|
||||||
|
function vcomponent.register(address, ctype, proxy, doc)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
checkArg(2,ctype,"string")
|
||||||
|
checkArg(3,proxy,"table")
|
||||||
|
if proxylist[address] ~= nil then
|
||||||
|
return nil, "component already at address"
|
||||||
|
elseif component.type(address) ~= nil then
|
||||||
|
return nil, "cannot register over real component"
|
||||||
|
end
|
||||||
|
proxy.address = address
|
||||||
|
proxy.type = ctype
|
||||||
|
local proxyobj = {}
|
||||||
|
for k,v in pairs(proxy) do
|
||||||
|
if type(v) == "function" then
|
||||||
|
proxyobj[k] = setmetatable({name=k,address=address},componentCallback)
|
||||||
|
else
|
||||||
|
proxyobj[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
proxylist[address] = proxy
|
||||||
|
proxyobjs[address] = proxyobj
|
||||||
|
typelist[address] = ctype
|
||||||
|
doclist[address] = doc
|
||||||
|
computer.pushSignal("component_added",address,ctype)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function vcomponent.unregister(address)
|
||||||
|
checkArg(1,address,"string")
|
||||||
|
if proxylist[address] == nil then
|
||||||
|
if component.type(address) ~= nil then
|
||||||
|
return nil, "cannot unregister real component"
|
||||||
|
else
|
||||||
|
return nil, "no component at address"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local thetype = typelist[address]
|
||||||
|
proxylist[address] = nil
|
||||||
|
proxyobjs[address] = nil
|
||||||
|
typelist[address] = nil
|
||||||
|
doclist[address] = nil
|
||||||
|
computer.pushSignal("component_removed",address,thetype)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function vcomponent.list()
|
||||||
|
local list = {}
|
||||||
|
for k,v in pairs(proxylist) do
|
||||||
|
list[#list + 1] = {k,typelist[k],v}
|
||||||
|
end
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
|
function vcomponent.resolve(address, componentType)
|
||||||
|
checkArg(1, address, "string")
|
||||||
|
checkArg(2, componentType, "string", "nil")
|
||||||
|
for k,v in pairs(typelist) do
|
||||||
|
if componentType == nil or v == componentType then
|
||||||
|
if k:sub(1, #address) == address then
|
||||||
|
return k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, "no such component"
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = math.random
|
||||||
|
function vcomponent.uuid()
|
||||||
|
return string.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||||
|
r(0,255),r(0,255),r(0,255),r(0,255),
|
||||||
|
r(0,255),r(0,255),
|
||||||
|
r(64,79),r(0,255),
|
||||||
|
r(128,191),r(0,255),
|
||||||
|
r(0,255),r(0,255),r(0,255),r(0,255),r(0,255),r(0,255))
|
||||||
|
end
|
||||||
|
|
||||||
|
return vcomponent
|
153
lib/vtansi.lua
153
lib/vtansi.lua
@ -1,35 +1,7 @@
|
|||||||
local vtansi = {}
|
local vtansi = {}
|
||||||
local keyboardIgnore = {}
|
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
|
||||||
vtansi.activeBuffers = {}
|
|
||||||
vtansi.sequences = {
|
|
||||||
[28] = "\n", -- newline
|
|
||||||
[200] = "\27[A", -- up
|
|
||||||
[203] = "\27[D", -- left
|
|
||||||
[205] = "\27[C", -- right
|
|
||||||
[208] = "\27[B", -- down
|
|
||||||
[201] = "\27[5~", -- page up
|
|
||||||
[209] = "\27[6~" -- page down
|
|
||||||
}
|
|
||||||
vtansi.keys = {}
|
|
||||||
vtansi.keys[0x38] = "lalt"
|
|
||||||
vtansi.keys[0xB8] = "ralt"
|
|
||||||
function vtansi.saveToBuffer(gpu,idx)
|
|
||||||
gpu.bitblt(idx, nil, nil, nil, nil, 0)
|
|
||||||
end
|
|
||||||
function vtansi.loadFromBuffer(gpu,idx)
|
|
||||||
gpu.bitblt(0, nil, nil, nil, nil, idx)
|
|
||||||
end
|
|
||||||
function vtansi.switchToBuffer(gpu,idx)
|
|
||||||
-- copy screen to the active buffer
|
|
||||||
vtansi.saveToBuffer(gpu,vtansi.activeBuffers[gpu.address])
|
|
||||||
-- copy the new buffer to the screen
|
|
||||||
vtansi.loadFromBuffer(gpu,idx)
|
|
||||||
vtansi.activeBuffers[gpu.address] = idx
|
|
||||||
end
|
|
||||||
function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal, either allocating a new buffer or using *bn*.
|
|
||||||
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 buffer = nil
|
|
||||||
local cx, cy = 1, 1
|
local cx, cy = 1, 1
|
||||||
local pc = " "
|
local pc = " "
|
||||||
local lc = ""
|
local lc = ""
|
||||||
@ -40,19 +12,8 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
local bg, fg = 0, 0xFFFFFF
|
local bg, fg = 0, 0xFFFFFF
|
||||||
|
|
||||||
-- setup
|
-- setup
|
||||||
if gpu.getActiveBuffer then
|
gpu.setResolution(mx,my)
|
||||||
buffer = bn or gpu.allocateBuffer(mx,my)
|
gpu.fill(1,1,mx,my," ")
|
||||||
vtansi.activeBuffers[gpu.address] = vtansi.activeBuffers[gpu.address] or buffer
|
|
||||||
local oldActiveBuffer = vtansi.activeBuffers[gpu.address]
|
|
||||||
gpu.setActiveBuffer(buffer)
|
|
||||||
gpu.setResolution(mx,my)
|
|
||||||
gpu.fill(1,1,mx,my," ")
|
|
||||||
gpu.setActiveBuffer(oldActiveBuffer)
|
|
||||||
else
|
|
||||||
gpu.setResolution(mx,my)
|
|
||||||
gpu.fill(1,1,mx,my," ")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function checkCursor()
|
local function checkCursor()
|
||||||
if cx > mx and lw then
|
if cx > mx and lw then
|
||||||
cx, cy = 1, cy+1
|
cx, cy = 1, cy+1
|
||||||
@ -67,13 +28,6 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function termwrite(s)
|
local function termwrite(s)
|
||||||
if buffer then
|
|
||||||
if vtansi.activeBuffers[gpu.address] == buffer then
|
|
||||||
gpu.setActiveBuffer(0)
|
|
||||||
else
|
|
||||||
gpu.setActiveBuffer(buffer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local wb = ""
|
local wb = ""
|
||||||
local lb, ec = nil, nil
|
local lb, ec = nil, nil
|
||||||
local function flushwb()
|
local function flushwb()
|
||||||
@ -96,9 +50,8 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
if cc == "\n" then
|
if cc == "\n" then
|
||||||
flushwb()
|
flushwb()
|
||||||
cx,cy = 1, cy+1
|
cx,cy = 1, cy+1
|
||||||
checkCursor()
|
|
||||||
elseif cc == "\t" then
|
elseif cc == "\t" then
|
||||||
wb=wb..(" "):rep((((cx//8)+1) * 8) - cx + 1)
|
wb=wb..(" "):rep(8*((cx+9)//8))
|
||||||
elseif cc == "\27" then
|
elseif cc == "\27" then
|
||||||
flushwb()
|
flushwb()
|
||||||
mode = 1
|
mode = 1
|
||||||
@ -123,31 +76,20 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
if cc == "H" then
|
if cc == "H" then
|
||||||
cx, cy = math.min(mx,tA[1] or 1), math.min(my,tA[2] or 1)
|
cx, cy = math.min(mx,tA[1] or 1), math.min(my,tA[2] or 1)
|
||||||
elseif cc == "A" then
|
elseif cc == "A" then
|
||||||
for i = 1, (tA[1] or 1) do
|
cy = cy - (tA[1] or 1)
|
||||||
cy = cy - 1
|
|
||||||
checkCursor()
|
|
||||||
end
|
|
||||||
elseif cc == "B" then
|
elseif cc == "B" then
|
||||||
for i = 1, (tA[1] or 1) do
|
cy = cy + (tA[1] or 1)
|
||||||
cy = cy + 1
|
|
||||||
checkCursor()
|
|
||||||
end
|
|
||||||
elseif cc == "C" then
|
elseif cc == "C" then
|
||||||
for i = 1, (tA[1] or 1) do
|
cx = cx + (tA[1] or 1)
|
||||||
cx = cx + 1
|
|
||||||
checkCursor()
|
|
||||||
end
|
|
||||||
elseif cc == "D" then
|
elseif cc == "D" then
|
||||||
for i = 1, (tA[1] or 1) do
|
cx = cx - (tA[1] or 1)
|
||||||
cx = cx - 1
|
|
||||||
checkCursor()
|
|
||||||
end
|
|
||||||
elseif cc == "s" then
|
elseif cc == "s" then
|
||||||
sx, sy = cx, cy
|
sx, sy = cx, cy
|
||||||
elseif cc == "u" then
|
elseif cc == "u" then
|
||||||
cx, cy = sx, sy
|
cx, cy = sx, sy
|
||||||
elseif cc == "n" and tA[1] == 6 then
|
elseif cc == "n" and tA[1] == 6 then
|
||||||
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
|
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
|
||||||
|
dprint(string.format("reporting %d;%d as current cursor position",cx,cy))
|
||||||
elseif cc == "K" and tA[1] == 1 then
|
elseif cc == "K" and tA[1] == 1 then
|
||||||
gpu.fill(1,cy,cx,1," ")
|
gpu.fill(1,cy,cx,1," ")
|
||||||
elseif cc == "K" and tA[1] == 2 then
|
elseif cc == "K" and tA[1] == 2 then
|
||||||
@ -164,7 +106,7 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
elseif cc == "m" then
|
elseif cc == "m" then
|
||||||
for _,num in ipairs(tA) do
|
for _,num in ipairs(tA) do
|
||||||
if num == 0 then
|
if num == 0 then
|
||||||
fg,bg,ec,lb = 0xFFFFFF,0,false,true
|
fg,bg,ec,lb = 0xFFFFFF,0,true,true
|
||||||
elseif num == 7 then
|
elseif num == 7 then
|
||||||
local nfg,nbg = bg, fg
|
local nfg,nbg = bg, fg
|
||||||
fg, bg = nfg, nbg
|
fg, bg = nfg, nbg
|
||||||
@ -197,65 +139,48 @@ function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component
|
|||||||
return rs, lb, ec
|
return rs, lb, ec
|
||||||
end
|
end
|
||||||
|
|
||||||
return termwrite, buffer
|
return termwrite
|
||||||
end
|
end
|
||||||
|
|
||||||
function vtansi.vtsession(gpua,scra,bn) -- string string number -- function function function -- creates a process to handle the GPU and screen address combination *gpua*/*scra*, optionally using buffer number *bn* specifically. Returns read, write and "close" functions.
|
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.
|
||||||
local modifiers = {}
|
|
||||||
local gpu = component.proxy(gpua)
|
local gpu = component.proxy(gpua)
|
||||||
-- gpu.bind(scra)
|
gpu.bind(scra)
|
||||||
local write, bn = vtansi.vtemu(gpu,bn)
|
local write = vtansi.vtemu(gpu)
|
||||||
local kba = {}
|
local kba = {}
|
||||||
for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
|
for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
|
||||||
kba[v]=true
|
kba[v]=true
|
||||||
end
|
end
|
||||||
local buf, lbuf, echo = "", false, false
|
local buf, lbuf, echo = "", true, true
|
||||||
os.spawn(function()
|
os.spawn(function() dprint(pcall(function()
|
||||||
while true do
|
while true do
|
||||||
local ty,ka,ch,kc = coroutine.yield()
|
local ty,ka,ch = coroutine.yield()
|
||||||
if kba[ka] and keyboardIgnore[ka] == bn then
|
if ty == "key_down" and kba[ka] then
|
||||||
keyboardIgnore[ka] = nil
|
if ch == 13 then ch = 10 end
|
||||||
end
|
if ch == 8 and lbuf then
|
||||||
if kba[ka] and vtansi.keys[kc] then
|
if buf:len() > 0 then
|
||||||
modifiers[vtansi.keys[kc]] = ty == "key_down"
|
if echo then write("\8 \8") end
|
||||||
end
|
buf = buf:sub(1,-2)
|
||||||
if ty == "key_down" and kba[ka] and (bn == nil or vtansi.activeBuffers[gpua] == bn) then
|
|
||||||
if bn and ty == "key_down" and kba[ka] and ch == 46 and kc == 52 and (modifiers.lalt or modifiers.ralt) then
|
|
||||||
-- next buffer
|
|
||||||
local allBuffers = gpu.buffers()
|
|
||||||
for k,v in ipairs(allBuffers) do
|
|
||||||
if v == vtansi.activeBuffers[gpu.address] and allBuffers[k+1] and not keyboardIgnore[ka] then
|
|
||||||
keyboardIgnore[ka] = bn
|
|
||||||
vtansi.switchToBuffer(gpu,allBuffers[k+1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif bn and ty == "key_down" and kba[ka] and ch == 44 and kc == 51 and (modifiers.lalt or modifiers.ralt) then
|
|
||||||
-- previous buffer
|
|
||||||
local allBuffers = gpu.buffers()
|
|
||||||
for k,v in ipairs(allBuffers) do
|
|
||||||
if v == vtansi.activeBuffers[gpu.address] and allBuffers[k-1] and not keyboardIgnore[ka] then
|
|
||||||
keyboardIgnore[ka] = bn
|
|
||||||
vtansi.switchToBuffer(gpu,allBuffers[k-1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local outs
|
|
||||||
if ch > 0 then
|
|
||||||
outs = string.char(ch)
|
|
||||||
end
|
|
||||||
outs = vtansi.sequences[kc] or outs
|
|
||||||
if outs then
|
|
||||||
if echo then write(outs) end
|
|
||||||
buf=buf..outs
|
|
||||||
end
|
end
|
||||||
|
elseif ch > 0 then
|
||||||
|
if echo then write(string.char(ch)) end
|
||||||
|
buf=buf..string.char(ch)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,string.format("ttyd[%s:%s/%i]",gpua:sub(1,8),scra:sub(1,8),tonumber(bn) or 0))
|
end)) end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8)))
|
||||||
local function bread(n)
|
local function bread(n)
|
||||||
coroutine.yield()
|
local r
|
||||||
local r = buf
|
if lbuf then
|
||||||
buf = ""
|
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
|
return r
|
||||||
end
|
end
|
||||||
local function bwrite(d)
|
local function bwrite(d)
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
--#include "module/syslog.lua"
|
|
||||||
--#include "module/sched.lua"
|
|
||||||
--#include "module/buffer.lua"
|
|
||||||
--#include "module/osutil.lua"
|
|
||||||
--#include "module/fs.lua"
|
|
||||||
--#include "module/io.lua"
|
|
||||||
--#include "module/devfs.lua"
|
|
||||||
--#include "module/devfs/syslog.lua"
|
|
||||||
--#include "module/component-get.lua"
|
|
||||||
--#include "module/loadfile.lua"
|
|
@ -234,114 +234,26 @@ function buffer:read(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function readLine(chop)
|
local function readLine(chop)
|
||||||
if not self.mode.t then
|
local start = 1
|
||||||
local start = 1
|
while true do
|
||||||
while true do
|
local l = self.bufferRead:find("\n", start, true)
|
||||||
local l = self.bufferRead:find("\n", start, true)
|
if l then
|
||||||
if l then
|
local result = self.bufferRead:sub(1, l + (chop and -1 or 0))
|
||||||
local result = self.bufferRead:sub(1, l + (chop and -1 or 0))
|
self.bufferRead = self.bufferRead:sub(l + 1)
|
||||||
self.bufferRead = self.bufferRead:sub(l + 1)
|
return result
|
||||||
return result
|
else
|
||||||
else
|
start = #self.bufferRead
|
||||||
start = #self.bufferRead
|
local result, reason = readChunk()
|
||||||
local result, reason = readChunk()
|
if not result then
|
||||||
if not result then
|
if reason then
|
||||||
if reason then
|
return nil, reason
|
||||||
return nil, reason
|
else -- eof
|
||||||
else -- eof
|
local result = #self.bufferRead > 0 and self.bufferRead or nil
|
||||||
local result = #self.bufferRead > 0 and self.bufferRead or nil
|
self.bufferRead = ""
|
||||||
self.bufferRead = ""
|
return result
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
-- this whole thing is a house of cards. good luck.
|
|
||||||
io.write("\27[s\27[8m\27[6n")
|
|
||||||
if not (self.mx or self.my) then
|
|
||||||
io.write("\27[9999;9999H\27[6n\27[u")
|
|
||||||
end
|
|
||||||
local pos, buffer, hIndex, sx, sy = 1, "", 0
|
|
||||||
self.history = self.history or {}
|
|
||||||
local function redraw()
|
|
||||||
-- scroll until the buffer will fit on the screen
|
|
||||||
while sx and sy and self.mx and self.my and #buffer > (self.mx * ((self.my - sy) + 1)) - sx do
|
|
||||||
sy = sy - 1
|
|
||||||
io.write("\27[9999;9999H ")
|
|
||||||
io.write(string.format("\27[2K\27[%i;%iH\27[s", sx, sy))
|
|
||||||
end
|
|
||||||
io.write(string.format("\27[u%s \27[u\27[%iC",buffer,(#buffer-pos)+1))
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
char = readBytesOrChars(1)
|
|
||||||
if char == "\27" then
|
|
||||||
if readBytesOrChars(1) == "[" then
|
|
||||||
local args = {""}
|
|
||||||
repeat
|
|
||||||
char = readBytesOrChars(1)
|
|
||||||
if char:match("%d") then
|
|
||||||
args[#args] = args[#args]..char
|
|
||||||
else
|
|
||||||
args[#args] = tonumber(args[#args])
|
|
||||||
args[#args+1] = ""
|
|
||||||
end
|
|
||||||
until not char:match("[%d;]")
|
|
||||||
if char == "C" then -- right
|
|
||||||
if pos > 1 then
|
|
||||||
pos = pos - 1
|
|
||||||
end
|
|
||||||
elseif char == "D" then -- left
|
|
||||||
if pos <= #buffer then
|
|
||||||
pos = pos + 1
|
|
||||||
end
|
|
||||||
elseif char == "A" then -- up
|
|
||||||
hIndex = hIndex + 1
|
|
||||||
io.write("\27[u"..(" "):rep(buffer:len()+1))
|
|
||||||
buffer = self.history[1+#self.history-hIndex] or buffer
|
|
||||||
pos = 1
|
|
||||||
elseif char == "B" then -- down
|
|
||||||
hIndex = hIndex - 1
|
|
||||||
io.write("\27[u"..(" "):rep(buffer:len()+1))
|
|
||||||
if hIndex == 0 then
|
|
||||||
hIndex = hIndex - 1
|
|
||||||
buffer = ""
|
|
||||||
end
|
|
||||||
buffer = self.history[1+#self.history-hIndex] or buffer
|
|
||||||
pos = 1
|
|
||||||
elseif char == "R" then -- cursor position report
|
|
||||||
self.mx, self.my = sx and math.max(self.mx or 0, args[1]) or self.mx, sy and math.max(self.my or 0, args[2]) or self.my
|
|
||||||
sx, sy = sx or args[1], sy or args[2]
|
|
||||||
end
|
|
||||||
hIndex = math.max(math.min(hIndex,#self.history),0)
|
|
||||||
end
|
|
||||||
elseif char == "\8" then -- backspace
|
|
||||||
if #buffer > 0 and pos <= #buffer then
|
|
||||||
buffer = buffer:sub(1, (#buffer - pos)) .. buffer:sub((#buffer - pos) + 2)
|
|
||||||
end
|
|
||||||
elseif char == "\3" then -- ^C, error
|
|
||||||
error("terminated")
|
|
||||||
elseif char == "\1" then -- ^A, go to start of line
|
|
||||||
pos = buffer:len()+1
|
|
||||||
elseif char == "\5" then -- ^E, go to end of line
|
|
||||||
pos = 1
|
|
||||||
elseif char == "\2" then -- ^B, back one word
|
|
||||||
local nc = buffer:reverse():find(" ",pos+1)
|
|
||||||
pos = nc or #buffer+1
|
|
||||||
elseif char == "\6" then -- ^F, forward one word
|
|
||||||
local nc = buffer:find(" ",math.max(#buffer-pos+3,0))
|
|
||||||
pos = (nc and #buffer-nc+2) or 1
|
|
||||||
elseif char == "\13" or char == "\10" or char == "\n" then -- return / newline
|
|
||||||
io.write("\n")
|
|
||||||
self.history[#self.history+1] = buffer ~= "" and buffer ~= self.history[#self.history] and buffer or nil
|
|
||||||
if #self.history > (self.maxhistory or 16) then table.remove(self.history,1) end
|
|
||||||
if chop then buffer = buffer .. "\n" end
|
|
||||||
return buffer
|
|
||||||
else
|
|
||||||
buffer = buffer:sub(1, (#buffer - pos) + 1) .. char .. buffer:sub((#buffer - pos) + 2)
|
|
||||||
end
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
function component.get(addr, ctype)
|
|
||||||
for c in component.list(ctype, true) do
|
|
||||||
if c:sub(1, addr:len()) == addr then
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil, "no such component"
|
|
||||||
end
|
|
@ -54,7 +54,7 @@ function fs.open(path,mode) -- string string -- table -- Opens file *path* with
|
|||||||
if mode:find("r") then
|
if mode:find("r") then
|
||||||
fobj.read = fread
|
fobj.read = fread
|
||||||
end
|
end
|
||||||
if mode:find("w") or mode:find("a") then
|
if mode:find("w") then
|
||||||
fobj.write = fwrite
|
fobj.write = fwrite
|
||||||
end
|
end
|
||||||
return fobj
|
return fobj
|
||||||
@ -68,11 +68,7 @@ function fs.copy(from,to) -- string string -- boolean -- copies a file from *fro
|
|||||||
if not of or not df then
|
if not of or not df then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local tmp
|
df:write(of:read("*a"))
|
||||||
repeat
|
|
||||||
tmp = of:read(2048)
|
|
||||||
df:write(tmp or "")
|
|
||||||
until not tmp
|
|
||||||
df:close()
|
df:close()
|
||||||
of:close()
|
of:close()
|
||||||
return true
|
return true
|
||||||
@ -107,7 +103,6 @@ function fs.mounts() -- -- table -- Returns a table containing the mount points
|
|||||||
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"
|
||||||
end
|
end
|
||||||
table.sort(rt)
|
|
||||||
return rt
|
return rt
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -117,12 +112,12 @@ function fs.address(path) -- string -- string -- Returns the address of the file
|
|||||||
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) -- string -- string -- 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].fstype or fsmounts[fsi].type or "filesystem"
|
return fsmounts[fsi].type or "filesystem"
|
||||||
end
|
end
|
||||||
|
|
||||||
fsmounts["/"] = component.proxy(computer.tmpAddress())
|
fsmounts["/"] = component.proxy(computer.tmpAddress())
|
||||||
fs.makeDirectory("temp")
|
fs.makeDirectory("temp")
|
||||||
if computer.getBootAddress and component.type(computer.getBootAddress()) == "filesystem" then
|
if computer.getBootAddress then
|
||||||
fs.makeDirectory("boot")
|
fs.makeDirectory("boot")
|
||||||
fs.mount("boot",component.proxy(computer.getBootAddress()))
|
fs.mount("boot",component.proxy(computer.getBootAddress()))
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
_OSVERSION=_OSVERSION or "PsychOS 2"
|
--#include "module/syslog.lua"
|
||||||
|
--#include "module/sched.lua"
|
||||||
|
--#include "module/buffer.lua"
|
||||||
|
--#include "module/osutil.lua"
|
||||||
|
--#include "module/fs.lua"
|
||||||
|
--#include "module/io.lua"
|
||||||
|
--#include "module/devfs.lua"
|
||||||
|
--#include "module/devfs/syslog.lua"
|
||||||
|
--#include "module/loadfile.lua"
|
||||||
|
|
||||||
os.spawn(function()
|
os.spawn(function()
|
||||||
os.setenv("PWD","/boot")
|
os.setenv("PWD","/boot")
|
||||||
@ -12,9 +20,6 @@ os.spawn(function()
|
|||||||
end
|
end
|
||||||
os.setenv("HOSTNAME",hostname)
|
os.setenv("HOSTNAME",hostname)
|
||||||
syslog(string.format("Hostname set to %s",hostname))
|
syslog(string.format("Hostname set to %s",hostname))
|
||||||
if fs.exists("/boot/pkg") then
|
|
||||||
pcall(require,"pkgfs")
|
|
||||||
end
|
|
||||||
local pids = {}
|
local pids = {}
|
||||||
local rc = require "rc"
|
local rc = require "rc"
|
||||||
for k,v in pairs(rc.cfg.enabled) do
|
for k,v in pairs(rc.cfg.enabled) do
|
||||||
|
@ -8,7 +8,7 @@ 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) -- table -- table -- 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,"rbt")
|
fd=io.open(fd,"rb")
|
||||||
end
|
end
|
||||||
if fd then
|
if fd then
|
||||||
os.setenv("STDIN",fd)
|
os.setenv("STDIN",fd)
|
||||||
@ -32,9 +32,8 @@ function io.write(...) -- Writes its arguments to the default output stream.
|
|||||||
io.output():write(...)
|
io.output():write(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function print(...) -- Writes each argument to the default output stream, separated by space.
|
function print(...) -- Writes each argument to the default output stream, separated by newlines.
|
||||||
for k,v in ipairs({...}) do
|
for k,v in ipairs({...}) do
|
||||||
io.write((k>1 and "\t" or "")..tostring(v))
|
io.write(tostring(v).."\n")
|
||||||
end
|
end
|
||||||
io.write("\n")
|
|
||||||
end
|
end
|
||||||
|
@ -7,20 +7,20 @@ end
|
|||||||
function runfile(p,...) -- string -- -- runs file *p* with arbitrary arguments in the current thread
|
function runfile(p,...) -- string -- -- 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*.
|
||||||
|
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)
|
||||||
|
end
|
||||||
_G.package = {}
|
_G.package = {}
|
||||||
package.path="/boot/lib/?.lua;/pkg/lib/?.lua;/boot/lib/?/init.lua;/pkg/lib/?/init.lua;./?;./?.lua;./?/init.lua"
|
package.loaded = {computer=computer,component=component,fs=fs,buffer=buffer}
|
||||||
package.loaded = {buffer=buffer, component=component, computer=computer, coroutine=coroutine, fs=fs, math=math, os=os, package=package, string=string, table=table}
|
|
||||||
package.alias = {filesystem="fs"}
|
|
||||||
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) -- 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
|
||||||
f=package.alias[f] or f
|
|
||||||
if not package.loaded[f] or force then
|
if not package.loaded[f] or force then
|
||||||
local ln = f:gsub("%.","/")
|
local lib = os.getenv("LIB") or "/boot/lib"
|
||||||
for d in package.path:gmatch("[^;]+") do
|
for d in lib:gmatch("[^\n]+") do
|
||||||
local p = d:gsub("%?",ln)
|
if fs.exists(d.."/"..f) then
|
||||||
if fs.exists(p) and not fs.isDirectory(p) then
|
package.loaded[f] = runfile(d.."/"..f)
|
||||||
package.loaded[f] = runfile(p)
|
elseif fs.exists(d.."/"..f..".lua") then
|
||||||
break
|
package.loaded[f] = runfile(d.."/"..f..".lua")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
--#includepkglib "diskpart" "lib/diskpart.lua" "diskpart"
|
|
||||||
--#includepkglib "rtfs" "lib/fs/rtfs/init.lua" "fs.rtfs"
|
|
||||||
--#includepkglib "rtfs" "lib/fs/rtfs/v1.lua" "fs.rtfs.v1"
|
|
||||||
do
|
|
||||||
local a = computer.getBootAddress()
|
|
||||||
if component.type(a) == "drive" or component.type(a) == "tape_drive" then
|
|
||||||
local diskpart = require "diskpart"
|
|
||||||
for k,v in ipairs(diskpart.getPartitions(a)) do
|
|
||||||
if v[2] == "rtfs" and v[1] == computer.address():sub(1,8) .. "-boot" then
|
|
||||||
syslog("Partition with suitable name detected, attempting to mount...")
|
|
||||||
local rtfs = require "fs.rtfs"
|
|
||||||
fs.makeDirectory("boot")
|
|
||||||
local m = rtfs.mount(diskpart.proxyPartition(a,k))
|
|
||||||
m.address = string.format("%s/%i/rtfs",a,k)
|
|
||||||
fs.mount("boot",m)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,20 +1,11 @@
|
|||||||
do
|
do
|
||||||
local tTasks,nPid,nTimeout,cPid = {},1,0.25,0 -- table of tasks, next process ID, default 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) -- function string -- number -- creates a process from function *f* with name *n*
|
||||||
tTasks[nPid] = {
|
tTasks[nPid] = {
|
||||||
c=coroutine.create(function()
|
c=coroutine.create(f), -- actual coroutine
|
||||||
local rt = {pcall(f)}
|
|
||||||
if not rt[1] then
|
|
||||||
syslog(rt[2])
|
|
||||||
end
|
|
||||||
computer.pushSignal("process_finished",os.pid(),table.unpack(rt))
|
|
||||||
end), -- actual coroutine
|
|
||||||
n=n, -- process name
|
n=n, -- process name
|
||||||
p=nPid, -- process PID
|
p=nPid, -- process PID
|
||||||
P=cPid, -- parent PID
|
P=cPid, -- parent PID
|
||||||
t=0, -- CPU time
|
|
||||||
T=0, -- total uptime
|
|
||||||
E=nTimeout, -- event timeout
|
|
||||||
e={} -- environment variables
|
e={} -- environment variables
|
||||||
}
|
}
|
||||||
if tTasks[cPid] then
|
if tTasks[cPid] then
|
||||||
@ -41,21 +32,16 @@ end
|
|||||||
function os.taskInfo(pid) -- number -- table -- returns info on process *pid* as a table with name and parent values
|
function os.taskInfo(pid) -- number -- table -- 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,cputime=tTasks[pid].t,iotime=tTasks[pid].T,timeout=tTasks[pid].E}
|
return {name=tTasks[pid].n,parent=tTasks[pid].P}
|
||||||
end
|
end
|
||||||
function os.sched() -- the actual scheduler function
|
function os.sched() -- the actual scheduler function
|
||||||
os.sched = nil
|
os.sched = nil
|
||||||
local sTimeout = nTimeout
|
|
||||||
while #tTasks > 0 do
|
while #tTasks > 0 do
|
||||||
local tEv = {computer.pullSignal(sTimeout)}
|
local tEv = {computer.pullSignal(nTimeout)}
|
||||||
sTimeout = nTimeout
|
|
||||||
for k,v in pairs(tTasks) do
|
for k,v in pairs(tTasks) do
|
||||||
if coroutine.status(v.c) ~= "dead" then
|
if coroutine.status(v.c) ~= "dead" then
|
||||||
cPid = k
|
cPid = k
|
||||||
local sT, sC = os.clock(), computer.uptime()
|
|
||||||
coroutine.resume(v.c,table.unpack(tEv))
|
coroutine.resume(v.c,table.unpack(tEv))
|
||||||
v.t, v.T = v.t + os.clock() - sT, v.T + computer.uptime() - sC
|
|
||||||
sTimeout=math.min(sTimeout, v.E)
|
|
||||||
else
|
else
|
||||||
tTasks[k] = nil
|
tTasks[k] = nil
|
||||||
end
|
end
|
||||||
@ -72,9 +58,11 @@ function os.getenv(k) -- gets a process' *k* environment variable
|
|||||||
return tTasks[cPid].e[k]
|
return tTasks[cPid].e[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function os.setTimeout(n,pid)
|
function os.setTimeout(n)
|
||||||
assert(type(n) == "number" and n >= 0)
|
if type(n) == "number" and n >= 0 then
|
||||||
tTasks[pid or cPid].E = n
|
nTimeout = n
|
||||||
return true
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
local fs = require "filesystem"
|
|
||||||
|
|
||||||
local wdir = "/tmp/psbootstrap"
|
|
||||||
local dlfiles = {
|
|
||||||
["/lib/libmtar.lua"] = "https://git.shadowkat.net/izaya/OC-misc/raw/branch/master/mtar/libmtar.lua",
|
|
||||||
["/lib/fs/rtfs/init.lua"] = "https://git.shadowkat.net/izaya/PsychOSPackages/raw/branch/master/rtfs/lib/fs/rtfs/init.lua",
|
|
||||||
["/lib/fs/rtfs/v1.lua"] = "https://git.shadowkat.net/izaya/PsychOSPackages/raw/branch/master/rtfs/lib/fs/rtfs/v1.lua",
|
|
||||||
["/lib/diskpart.lua"] = "https://git.shadowkat.net/izaya/PsychOSPackages/raw/branch/master/diskpart/lib/diskpart.lua",
|
|
||||||
["/etc/rc.d/partman.lua"] = "https://git.shadowkat.net/izaya/OC-misc/raw/branch/master/partition/OpenOS/etc/rc.d/partman.lua",
|
|
||||||
["/bin/slicer.lua"] = "https://git.shadowkat.net/izaya/PsychOSPackages/raw/branch/master/slicer/exec/slicer.lua",
|
|
||||||
["/bin/boopu.lua"] = "https://git.shadowkat.net/izaya/PsychOSPackages/raw/branch/master/boopu/exec/boopu.lua",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function run(cmd)
|
|
||||||
print(cmd)
|
|
||||||
os.execute(cmd)
|
|
||||||
end
|
|
||||||
|
|
||||||
print("Downloading and linking files...")
|
|
||||||
for k,v in pairs(dlfiles) do
|
|
||||||
local tpath = string.format("%s/%s", wdir, k)
|
|
||||||
local isdir = fs.isDirectory(fs.path(tpath)) or fs.makeDirectory(fs.path(tpath))
|
|
||||||
run(string.format("wget '%s' '%s'", v, tpath))
|
|
||||||
local lt = k
|
|
||||||
while not fs.isDirectory(fs.path(lt)) and not fs.isLink(fs.path(lt)) do
|
|
||||||
lt = fs.path(lt)
|
|
||||||
end
|
|
||||||
if fs.get(tpath) ~= fs.get(lt) then
|
|
||||||
run(string.format("ln '%s/%s' '%s'", wdir, lt, lt))
|
|
||||||
end
|
|
||||||
end
|
|
121
preproc.lua
121
preproc.lua
@ -1,121 +0,0 @@
|
|||||||
local preproc = {}
|
|
||||||
preproc.directives = {}
|
|
||||||
|
|
||||||
function preproc.parsewords(line) -- string -- table -- Returns a table of words from the string *line*, parsing quotes and escapes.
|
|
||||||
local rt = {""}
|
|
||||||
local escaped, quoted = false, false
|
|
||||||
for c in line:gmatch(".") do
|
|
||||||
if escaped then
|
|
||||||
rt[#rt] = rt[#rt]..c
|
|
||||||
elseif c == '"' or c == "'" then
|
|
||||||
quoted = not quoted
|
|
||||||
elseif c == "\\" then
|
|
||||||
escaped = true
|
|
||||||
elseif c:match("%s") and not quoted and rt[#rt]:len() > 0 then
|
|
||||||
rt[#rt+1] = ""
|
|
||||||
else
|
|
||||||
rt[#rt] = rt[#rt]..c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return rt
|
|
||||||
end
|
|
||||||
|
|
||||||
function preproc.line(line) -- string -- -- Returns either a function - which can be called to get lines until it returns nil - or a string from processing *line* using preprocessor directives.
|
|
||||||
if line:match("^%-%-#") then
|
|
||||||
local directive, args = line:match("^%-%-#(%S+)%s(.+)")
|
|
||||||
print(directive,args)
|
|
||||||
local args = preproc.parsewords(args)
|
|
||||||
if preproc.directives[directive] then
|
|
||||||
return preproc.directives[directive](table.unpack(args))
|
|
||||||
else
|
|
||||||
error("unknown preprocessor directive: "..directive)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function preproc.preproc(...) -- string -- string -- Returns the output from preprocessing the files listed in *...*.
|
|
||||||
local tA = {...}
|
|
||||||
local output = ""
|
|
||||||
for _,fname in ipairs(tA) do
|
|
||||||
local f,e = io.open(fname)
|
|
||||||
if not f then error("unable to open file "..fname..": "..e) end
|
|
||||||
for line in f:lines() do
|
|
||||||
local r = preproc.line(line)
|
|
||||||
if type(r) == "function" then
|
|
||||||
while true do
|
|
||||||
local rs = r()
|
|
||||||
if not rs then break end
|
|
||||||
output = output .. rs .. "\n"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
output = output .. r .. "\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
|
|
||||||
preproc.directives.include = preproc.preproc
|
|
||||||
|
|
||||||
function preproc.directives.includelib(file, name) -- string string -- string -- Returns a preprocessed inlined library
|
|
||||||
return string.format("package.loaded['%s'] = (function()\n%s\nend)()", name, preproc.preproc(file))
|
|
||||||
end
|
|
||||||
|
|
||||||
function preproc.directives.includepkgfile(package, file)
|
|
||||||
if (_OSVERSION or ""):sub(1,7) == "PsychOS" then
|
|
||||||
return preproc.preproc(string.format("/pkg/%s", file))
|
|
||||||
else
|
|
||||||
for path in (os.getenv("PSYCHOSPACKAGES") or "../PsychOSPackages"):gmatch("[^:]+") do
|
|
||||||
local f = io.open(string.format("%s/%s/%s", path, package, file), "r")
|
|
||||||
if f then
|
|
||||||
f:close()
|
|
||||||
return preproc.preproc(string.format("%s/%s/%s", path, package, file))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error(string.format("unable to locate file %s from package %s", file, package))
|
|
||||||
end
|
|
||||||
|
|
||||||
function preproc.directives.includepkglib(package, file, name) -- string string -- string -- Returns a preprocessed inlined library
|
|
||||||
return string.format("package.loaded['%s'] = (function()\n%s\nend)()", name, preproc.directives.includepkgfile(package, file))
|
|
||||||
end
|
|
||||||
|
|
||||||
local minify = true
|
|
||||||
local minifyFilters = {
|
|
||||||
{"%-%-%[%[.-%]%]",""},
|
|
||||||
{"%-%-.-\n","\n"},
|
|
||||||
{"\n[ \t]+","\n"},
|
|
||||||
{"%s?%.%.%s?",".."},
|
|
||||||
{"%s?==%s?","=="},
|
|
||||||
{"%s?~=%s?","~="},
|
|
||||||
{"%s?>=%s?",">="},
|
|
||||||
{"%s?<=%s?","<="},
|
|
||||||
{"%s?>%s?",">"},
|
|
||||||
{"%s?<%s?","<"},
|
|
||||||
{"%s?=%s?","="},
|
|
||||||
{"%s?,%s?",","},
|
|
||||||
{",\n",","},
|
|
||||||
{"\n\n+","\n"},
|
|
||||||
{"[ \t]\n","\n"},
|
|
||||||
{"%{%s+","{"},
|
|
||||||
{"%s+%}","}"}
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable(preproc,{__call=function(_,...)
|
|
||||||
local tA = {...}
|
|
||||||
local out = table.remove(tA,#tA)
|
|
||||||
local f,e = io.open(out,"wb")
|
|
||||||
if not f then error("unable to open file "..out..": "..e) end
|
|
||||||
local out = preproc.preproc(table.unpack(tA))
|
|
||||||
if preproc.minify then
|
|
||||||
local olen = #out
|
|
||||||
for k,v in ipairs(minifyFilters) do
|
|
||||||
out = out:gsub(v[1],v[2])
|
|
||||||
end
|
|
||||||
print(olen, #out)
|
|
||||||
end
|
|
||||||
f:write(out)
|
|
||||||
f:close()
|
|
||||||
end})
|
|
@ -1,17 +1,11 @@
|
|||||||
|
local lc = computer.uptime()
|
||||||
_G.clip = ""
|
_G.clip = ""
|
||||||
|
while true do
|
||||||
function start()
|
local eT = {coroutine.yield()}
|
||||||
return os.spawn(function()
|
if eT[1] == "clipboard" then
|
||||||
local lc
|
if computer.uptime() > lc + 5 then
|
||||||
while true do
|
_G.clip = ""
|
||||||
local eT = {coroutine.yield()}
|
end
|
||||||
if eT[1] == "clipboard" then
|
_G.clip = _G.clip .. eT[3]
|
||||||
if computer.uptime() > lc + 5 then
|
end
|
||||||
_G.clip = ""
|
|
||||||
end
|
|
||||||
lc = computer.uptime()
|
|
||||||
_G.clip = _G.clip .. eT[3]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
95
service/fserv.lua
Normal file
95
service/fserv.lua
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
local minitel = require "minitel"
|
||||||
|
local serial = require "serialization"
|
||||||
|
|
||||||
|
local cfg = {["path"]="/boot/srv/frequest",["port"]=70}
|
||||||
|
|
||||||
|
f=io.open("/boot/cfg/fserv.cfg","rb")
|
||||||
|
if f then
|
||||||
|
local ncfg = serial.unserialize(f:read("*a"))
|
||||||
|
f:close()
|
||||||
|
for k,v in pairs(ncfg) do
|
||||||
|
cfg[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fileHandler(socket,rtype,path)
|
||||||
|
syslog(string.format("[%s:%d] %s %s",socket.addr,socket.port,rtype,path),syslog.info,"fserv")
|
||||||
|
if rtype == "t" then
|
||||||
|
if fs.exists(path) and fs.isDirectory(path) then
|
||||||
|
socket:write("d")
|
||||||
|
for _,file in ipairs(fs.list(path)) do
|
||||||
|
socket:write(file.."\n")
|
||||||
|
end
|
||||||
|
elseif fs.exists(path) and not fs.isDirectory(path) then
|
||||||
|
local f,err = io.open(path,"rb")
|
||||||
|
if f then
|
||||||
|
socket:write("y")
|
||||||
|
while true do
|
||||||
|
local c = f:read(4096)
|
||||||
|
if not c or c == "" then break end
|
||||||
|
socket:write(c)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
socket:write("fFailed to open file: "..err)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
socket:write("nFile not found")
|
||||||
|
end
|
||||||
|
elseif rtype == "s" then
|
||||||
|
if fs.exists(path) then
|
||||||
|
local ftype = "f"
|
||||||
|
if fs.isDirectory(path) then
|
||||||
|
ftype = "d"
|
||||||
|
end
|
||||||
|
socket:write(string.format("y%s\n%d",ftype,fs.size(path)))
|
||||||
|
else
|
||||||
|
socket:write("nFile not found.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
socket:write("fUnknown request type")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function httpHandler(socket,rtype,path)
|
||||||
|
local tPath = fs.segments(path)
|
||||||
|
local proto = table.remove(tPath,1)
|
||||||
|
local url = string.format("%s://%s",proto,table.concat(tPath,"/"))
|
||||||
|
local request = component.invoke(component.list("internet")(),"request",url)
|
||||||
|
repeat
|
||||||
|
coroutine.yield()
|
||||||
|
until request.finishConnect()
|
||||||
|
local code, message, headers = request.response()
|
||||||
|
if code < 200 or code > 299 then
|
||||||
|
socket:write(string.format("f%d\n%s",code,message))
|
||||||
|
else
|
||||||
|
local data = ""
|
||||||
|
repeat
|
||||||
|
coroutine.yield()
|
||||||
|
data = request.read()
|
||||||
|
if data then
|
||||||
|
socket:write(data)
|
||||||
|
end
|
||||||
|
until not data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function socketHandler(socket)
|
||||||
|
return function()
|
||||||
|
local line = nil
|
||||||
|
repeat
|
||||||
|
coroutine.yield()
|
||||||
|
line = socket:read()
|
||||||
|
until line
|
||||||
|
local rtype, path = line:match("(.)(.+)")
|
||||||
|
if fs.segments(path)[1] == "http" or fs.segments(path)[1] == "https" then
|
||||||
|
httpHandler(socket,rtype,path)
|
||||||
|
else
|
||||||
|
path = (cfg.path .. "/" .. path:gsub("../","")):gsub("/+","/")
|
||||||
|
fileHandler(socket,rtype,path)
|
||||||
|
end
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
os.spawn(socketHandler(minitel.listen(70)),"fserv worker process")
|
||||||
|
end
|
@ -1,44 +1,26 @@
|
|||||||
local fsmanager = {}
|
local function mount(addr)
|
||||||
fsmanager.filesystems = {}
|
dest = component.invoke(addr,"getLabel") or "mnt/"..addr:sub(1,3)
|
||||||
local run = true
|
dest = "/"..dest
|
||||||
|
|
||||||
function fsmanager.mount(addr)
|
|
||||||
for k,v in ipairs(fs.mounts()) do
|
|
||||||
if fs.address(v) == addr then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
dest = "/" .. (component.invoke(addr,"getLabel") or "mnt/"..addr:sub(1,3))
|
|
||||||
syslog("Mounting "..addr.." to "..dest)
|
syslog("Mounting "..addr.." to "..dest)
|
||||||
fs.makeDirectory(dest)
|
fs.makeDirectory(dest)
|
||||||
local w,r = fs.mount(dest,component.proxy(addr))
|
local w,r = fs.mount(dest,component.proxy(addr))
|
||||||
if not w then
|
if not w then
|
||||||
syslog("Failed to mount: "..r)
|
syslog("Failed to mount: "..r)
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
fsmanager.filesystems[addr] = dest
|
end
|
||||||
|
for addr, _ in component.list("filesystem") do
|
||||||
|
mount(addr)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fsmanager.start()
|
function start()
|
||||||
run = true
|
|
||||||
return os.spawn(function()
|
return os.spawn(function()
|
||||||
for addr, _ in component.list("filesystem") do
|
while true do
|
||||||
fsmanager.mount(addr)
|
|
||||||
end
|
|
||||||
while run do
|
|
||||||
local tE = {coroutine.yield()}
|
local tE = {coroutine.yield()}
|
||||||
if tE[1] == "component_added" and tE[3] == "filesystem" then
|
if tE[1] == "component_added" and tE[3] == "filesystem" then
|
||||||
fsmanager.mount(tE[2])
|
mount(tE[2])
|
||||||
elseif tE[1] == "component_removed" and fsmanager.filesystems[tE[2]] and tE[3] == "filesystem" then
|
elseif tE[1] == "component_removed" and tE[3] == "filesystem" then
|
||||||
syslog("Unmounting "..tE[2].." from "..fsmanager.filesystems[tE[2]])
|
fs.umount("/mnt/"..tE[2]:sub(1,3))
|
||||||
fs.umount(fsmanager.filesystems[tE[2]])
|
|
||||||
fsmanager.filesystems[tE[2]] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,"fsmanager")
|
end,"fsmanager")
|
||||||
end
|
end
|
||||||
function fsmanager.stop()
|
|
||||||
run = false
|
|
||||||
end
|
|
||||||
|
|
||||||
return fsmanager
|
|
||||||
|
@ -37,31 +37,20 @@ local function spawnShell(fin,fout)
|
|||||||
io.input(fin)
|
io.input(fin)
|
||||||
io.output(fout):setvbuf("no")
|
io.output(fout):setvbuf("no")
|
||||||
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
|
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
|
||||||
print((os.getenv("HOSTNAME") or "unknown") .. " on " .. fin)
|
|
||||||
return os.spawn(shell.interactive, "shell: "..tostring(fin))
|
return os.spawn(shell.interactive, "shell: "..tostring(fin))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function allocate()
|
local function allocate()
|
||||||
for k,v in pairs(gpus) do
|
for k,v in pairs(gpus) do
|
||||||
dprint("Setting up display "..k)
|
dprint(k)
|
||||||
local sA = nextScreen(v[2])
|
local sA = nextScreen(v[2])
|
||||||
if v[1] == false and sA then
|
if v[1] == false and sA then
|
||||||
local terminals = 1
|
local r,w = vtansi.vtsession(k,sA)
|
||||||
local gpu = component.proxy(k)
|
devfs.register("tty"..tostring(ttyn), function() return r,w,function() end end)
|
||||||
gpu.bind(sA)
|
|
||||||
if gpu.buffers then
|
|
||||||
gpu.freeAllBuffers()
|
|
||||||
local mw, mh = gpu.maxResolution()
|
|
||||||
terminals = math.floor(gpu.freeMemory() / (mw * mh))
|
|
||||||
end
|
|
||||||
for i = 1, terminals do
|
|
||||||
local r,w = vtansi.vtsession(k,sA)
|
|
||||||
devfs.register("tty"..tostring(ttyn), function() return r,w,function() end end)
|
|
||||||
pids["tty"..tostring(ttyn)] = {-1}
|
|
||||||
ttyn = ttyn + 1
|
|
||||||
end
|
|
||||||
gpus[k][1] = true
|
gpus[k][1] = true
|
||||||
screens[sA][1] = true
|
screens[sA][1] = true
|
||||||
|
pids["tty"..tostring(ttyn)] = {-1}
|
||||||
|
ttyn = ttyn + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -70,15 +59,14 @@ function start()
|
|||||||
basepid = os.spawn(function()
|
basepid = os.spawn(function()
|
||||||
scan()
|
scan()
|
||||||
allocate()
|
allocate()
|
||||||
dprint("Display setup complete.")
|
dprint("screens ready")
|
||||||
while true do
|
while true do
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
for k,v in pairs(pids) do
|
for k,v in pairs(pids) do
|
||||||
if not os.taskInfo(v[1]) then
|
if not os.taskInfo(v[1]) then
|
||||||
dprint("Spawning new shell for "..k)
|
dprint("Spawning new shell for "..k)
|
||||||
pids[k][1] = spawnShell(v[2] or "/dev/"..k, v[3] or "/dev/"..k)
|
pids[k][1] = spawnShell(v[2] or "/dev/"..k, v[3] or "/dev/"..k)
|
||||||
pids[k][2], pids[k][3] = pids[k][2] or "/dev/"..k, pids[k][3] or "/dev/"..k
|
pids[k][2], pids[k][3] = pids[k][2] or io.input(), pids[k][3] or io.output()
|
||||||
coroutine.yield()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -89,4 +77,3 @@ function stop()
|
|||||||
os.kill(basepid)
|
os.kill(basepid)
|
||||||
basepid = nil
|
basepid = nil
|
||||||
end
|
end
|
||||||
return {start=start,stop=stop}
|
|
||||||
|
@ -271,4 +271,3 @@ function del_route(to)
|
|||||||
cfg.sroutes[to] = nil
|
cfg.sroutes[to] = nil
|
||||||
saveconfig()
|
saveconfig()
|
||||||
end
|
end
|
||||||
return {start=start,stop=stop,set=set,set_route=set_route,del_route=del_route}
|
|
||||||
|
162
service/vtunnel.lua
Normal file
162
service/vtunnel.lua
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
local vcomponent = require "vcomponent"
|
||||||
|
local serial = require "serialization"
|
||||||
|
local component = require "component"
|
||||||
|
local computer = require "computer"
|
||||||
|
local event = require "event"
|
||||||
|
local imt = require "interminitel"
|
||||||
|
|
||||||
|
local cfg = {}
|
||||||
|
cfg.peers = {}
|
||||||
|
cfg.rtimer = 5
|
||||||
|
cfg.katimer = 30
|
||||||
|
local listeners = {}
|
||||||
|
local proxies = {}
|
||||||
|
|
||||||
|
local function loadcfg()
|
||||||
|
local f = io.open("/boot/cfg/vtunnel.cfg","rb")
|
||||||
|
if not f then return false end
|
||||||
|
for k,v in pairs(serial.unserialize(f:read("*a")) or {}) do
|
||||||
|
cfg[k] = v
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
local function savecfg()
|
||||||
|
local f = io.open("/boot/cfg/vtunnel.cfg","wb")
|
||||||
|
if not f then
|
||||||
|
print("Warning: unable to save configuration.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
f:write(serial.serialize(cfg))
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createTunnel(host,port,addr,raddr)
|
||||||
|
local proxy = {address=addr,buffer=""}
|
||||||
|
function proxy.connect()
|
||||||
|
if proxy.socket then
|
||||||
|
proxy.socket.close()
|
||||||
|
end
|
||||||
|
proxy.socket = component.invoke(component.list("internet")(),"connect",host,port)
|
||||||
|
local st = computer.uptime()
|
||||||
|
repeat
|
||||||
|
coroutine.yield()
|
||||||
|
until proxy.socket.finishConnect() or computer.uptime() > st+5
|
||||||
|
end
|
||||||
|
function proxy.send(...)
|
||||||
|
rt = 0
|
||||||
|
while not proxy.socket.write(imt.encodePacket(...)) and rt < 10 do
|
||||||
|
proxy.connect()
|
||||||
|
rt = rt + 1
|
||||||
|
end
|
||||||
|
proxy.last = computer.uptime()
|
||||||
|
end
|
||||||
|
function proxy.read()
|
||||||
|
local rb, r
|
||||||
|
local rt = 0
|
||||||
|
while true do
|
||||||
|
rb,r = proxy.socket.read(4096)
|
||||||
|
if rb or rt > 10 then break end
|
||||||
|
if type(rb) == "nil" then
|
||||||
|
proxy.connect()
|
||||||
|
end
|
||||||
|
rt = rt + 1
|
||||||
|
end
|
||||||
|
proxy.buffer = proxy.buffer .. rb
|
||||||
|
while imt.decodePacket(proxy.buffer) do
|
||||||
|
computer.pushSignal("modem_message",addr,raddr,0,0,imt.decodePacket(proxy.buffer))
|
||||||
|
proxy.buffer = imt.getRemainder(proxy.buffer) or ""
|
||||||
|
end
|
||||||
|
if computer.uptime() > proxy.last + cfg.katimer then
|
||||||
|
proxy.socket.write("\0\1\0")
|
||||||
|
proxy.last = computer.uptime()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function proxy.getWakeMessage()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
proxy.setWakeMessage = proxy.getWakeMessage
|
||||||
|
function proxy.maxPacketSize()
|
||||||
|
return 8192
|
||||||
|
end
|
||||||
|
function proxy.getChannel()
|
||||||
|
return host..":"..tostring(port)
|
||||||
|
end
|
||||||
|
proxy.connect()
|
||||||
|
proxy.last = computer.uptime()
|
||||||
|
return proxy
|
||||||
|
end
|
||||||
|
|
||||||
|
vt = {}
|
||||||
|
function start()
|
||||||
|
loadcfg()
|
||||||
|
for k,v in pairs(cfg.peers) do
|
||||||
|
print(string.format("Connecting to %s:%d",v.host,v.port))
|
||||||
|
v.addr = v.addr or vcomponent.uuid()
|
||||||
|
v.raddr = v.raddr or vcomponent.uuid()
|
||||||
|
local px = createTunnel(v.host, v.port, v.addr, v.raddr)
|
||||||
|
vcomponent.register(v.addr, "tunnel", px)
|
||||||
|
proxies[v.addr] = px
|
||||||
|
end
|
||||||
|
for k,v in pairs(os.tasks()) do
|
||||||
|
if os.taskInfo(v).name:match("minitel") then
|
||||||
|
os.kill(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function vt.stop()
|
||||||
|
for k,v in pairs(proxies) do
|
||||||
|
vcomponent.unregister(k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vt.listpeers()
|
||||||
|
for k,v in pairs(cfg.peers) do
|
||||||
|
print(string.format("#%d (%s:%d)\n Local address: %s\n Remote address: %s",k,v.host,v.port,v.addr,v.raddr))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function vt.addpeer(host,port)
|
||||||
|
port = tonumber(port) or 4096
|
||||||
|
local t = {}
|
||||||
|
t.host = host
|
||||||
|
t.port = port
|
||||||
|
t.addr = vcomponent.uuid()
|
||||||
|
t.raddr = vcomponent.uuid()
|
||||||
|
cfg.peers[#cfg.peers+1] = t
|
||||||
|
print(string.format("Added peer #%d (%s:%d) to the configuration.\nRestart to apply changes.",#cfg.peers,host,port))
|
||||||
|
savecfg()
|
||||||
|
end
|
||||||
|
function vt.delpeer(n)
|
||||||
|
n=tonumber(n)
|
||||||
|
if not n then
|
||||||
|
print("delpeer requires a number, representing the peer number, as an argument.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local dp = table.remove(cfg.peers, n)
|
||||||
|
savecfg()
|
||||||
|
print(string.format("Removed peer %s:%d",dp.host, dp.port))
|
||||||
|
end
|
||||||
|
|
||||||
|
function vt.settimer(time)
|
||||||
|
time = tonumber(time)
|
||||||
|
if not time then
|
||||||
|
print("Timer must be a number.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
cfg.rtime = time
|
||||||
|
savecfg()
|
||||||
|
end
|
||||||
|
|
||||||
|
vt.start = start
|
||||||
|
_G.libs.vtunnel = vt
|
||||||
|
|
||||||
|
start()
|
||||||
|
local last = computer.uptime()
|
||||||
|
while true do
|
||||||
|
local tE = {coroutine.yield()}
|
||||||
|
if computer.uptime() > last + cfg.rtimer then
|
||||||
|
for k,v in pairs(proxies) do
|
||||||
|
v.read()
|
||||||
|
end
|
||||||
|
last = computer.uptime()
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user