Compare commits

...

2 Commits

Author SHA1 Message Date
Sam Roxanne 76de8ca302 find, filter, skeletons 2020-06-03 15:06:58 -05:00
Sam Roxanne 50cfb2bbf2 s m o l 2020-06-02 19:09:37 -05:00
26 changed files with 292 additions and 598 deletions

View File

@ -0,0 +1,6 @@
function actions.init()
os.execute("mkdir -p target")
os.execute("luacomp module/bootloader.lua -O target/init.lua")
end
actions[#actions+1] = "init"

19
.buildactions/10_zlua.lua Normal file
View File

@ -0,0 +1,19 @@
local function makezlua(file)
local path = file:match("^(.+)/.+%..+$")
os.execute("mkdir -p target/"..path)
os.execute("cat "..file.." | luamin -c | lua zlua.lua > target/"..file)
end
function actions.files()
local libs = find("lib", "*.lua")
local svcs = find("service", "*.lua")
for i=1, #libs do
makezlua(libs[i])
end
for i=1, #svcs do
makezlua(svcs[i])
end
end
actions[#actions+1] = "files"

View File

@ -0,0 +1,6 @@
function actions.package()
os.execute("cp -r cfg target")
os.execute("cd target; find $".."(ls) -depth | tsar -o > ../psychos.tsar")
end
actions[#actions+1] = "package"

View File

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

14
build.sh.old Executable file
View File

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

View File

@ -1 +1 @@
{enabled={"getty","minitel"}}
{enabled={"getty","minitel","clipboard","fsmanager"}}

22
escape.lua Normal file
View File

@ -0,0 +1,22 @@
local function mkstr(d)
local dat = "\""
for i=1, #d do
if (d:sub(i,i) == "\\") then
dat = dat .. ("\\\\")
elseif (d:sub(i,i) == "\"") then
dat = dat .. ("\\\"")
elseif (d:sub(i,i) == "\n") then
dat = dat .. ("\\n")
elseif (d:sub(i,i) == "\r") then
dat = dat .. ("\\r")
elseif (d:sub(i,i) == "\t") then
dat = dat .. ("\\t")
else
dat = dat .. (d:sub(i,i))
end
end
dat = dat .. ("\"")
return dat
end
io.stdout:write(mkstr(io.stdin:read("*a")))

View File

@ -1,128 +0,0 @@
local _,serial = pcall(require,"serialization")
local doc = {}
doc.searchers = {}
doc.tctab = {
["string"] = 31,
["table"] = 32,
["number"] = 33,
["boolean"] = 35,
["function"] = 36
}
function doc.parsefile(path) -- string -- table -- parses file from *path* to return a documentation table
local fdoc = {}
local f = io.open(path)
local lines = {}
for l in f:read("*a"):gmatch("[^\n]+") do
if l:find("function") and not l:find("local") then
lines[#lines+1] = l
end
end
for k,v in pairs(lines) do
local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)")
if name and args and desc then
local fd = {["description"]=desc or desc,["args"]={},["atypes"]={}}
for word in args:gmatch("[^%s,]+") do
fd.args[#fd.args+1] = {word}
fd.atypes[word] = "unknown"
end
local argtypes, outtypes, description = desc:match("(.-)%-%-(.-)%-%-%s*(.+)")
if argtypes and outtypes and description then
local wc = 1
for word in argtypes:gmatch("%S+") do
fd.args[wc][2] = word
fd.atypes[fd.args[wc][1]] = word
wc = wc + 1
end
local wc = 1
for word in outtypes:gmatch("%S+") do
fd.outtypes = fd.outtypes or {}
fd.outtypes[#fd.outtypes+1] = word
end
fd.description = description
end
fdoc[name] = fd
end
end
return fdoc
end
function doc.format(fdoc) -- table -- string -- returns VT100 formatted documentation from documentation table *fdoc*
local rs = "" -- string to return
for fname,finfo in pairs(fdoc) do
if rs:len() > 0 then rs = rs .. "\n\n" end
local as = "" -- string containing arguments for a given function, with colours for type
for k,v in ipairs(finfo.args) do
local c = doc.tctab[v[2]] or 0
if k > 1 then
as = as .. ", "
end
if v[2] then
as = string.format("%s%s: \27[%im%s\27[0m",as,v[2],c,v[1])
else
as = string.format("%s\27[%im%s\27[0m",as,c,v[1])
end
end
local rv = ""
if finfo.outtypes then
rv = ": "
for k,v in ipairs(finfo.outtypes) do
if k > 1 then
rv = rv .. ", "
end
local c = doc.tctab[v] or 0
rv = string.format("%s\27[%im%s\27[0m",rv,c,v)
end
end
local nd = finfo.description
for k,v in pairs(finfo.atypes) do
local c = doc.tctab[v] or 7
nd=nd:gsub("%*"..k.."%*","\27["..tostring(c).."m"..k.."\27[0m")
end
rs = string.format("%s\27[36m%s\27[0m(%s)%s\n%s",rs,fname,as,rv,nd)
end
return rs
end
function doc.searchers.lib(name) -- string -- string string -- Tries to find a documentation from a library with *name*. Returns either a string of documentation, or false and a reason.
local lib = os.getenv("LIB") or "/boot/lib"
local dt
for d in lib:gmatch("[^\n]+") do
if fs.exists(d.."/"..name) then
dt = doc.parsefile(d.."/"..name)
elseif fs.exists(d.."/"..name..".lua") then
dt = doc.parsefile(d.."/"..name..".lua")
end
end
if not dt then return false, "unable to find documentation for "..tostring(name) end
return doc.format(dt)
end
function doc.searchers.cdoc(topic) -- string -- string string -- Searches for documentation labelled as *topic* in .dict files under /boot/doc/
if not serial then return end
for k,v in ipairs(fs.list("/boot/doc")) do
if v:sub(-5) == ".dict" then
local f=io.open("/boot/doc/"..v,"rb")
for line in f:lines() do
local mname, docs = line:match("^(.-)\t(.+)$")
if mname == topic or mname == topic..".lua" then
return doc.format(serial.unserialize(docs))
end
end
end
end
end
function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc().
local lib = os.getenv("LIB") or "/boot/lib"
local dt
for k,v in pairs(doc.searchers) do
dt=v(topic)
if dt then
print(dt)
return true
end
end
error("unable to find documentation for "..tostring(name))
end
return setmetatable(doc,{__call=function(_,topic) return doc.docs(topic) end})

0
lib/foxfs.lua Normal file
View File

View File

@ -1,63 +0,0 @@
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

0
lib/osdi.lua Normal file
View File

View File

@ -2,7 +2,6 @@ local component = require "component"
local fs = require "fs"
local shell = require "shell"
local ed = require "ed"
local doc = require "doc"
local shutil = {}
shutil.ed = ed.interactive
shutil.vi = ed.visual
@ -124,4 +123,42 @@ shutil.mkdir = fs.makeDirectory
shutil.cp = fs.copy
shutil.rm = fs.remove
local function find_dir(t, dir)
local f = fs.list(dir)
for i=1, #f do
if (f[i]:sub(#f[i]) == "/") then
find_dir(t, dir..f[i])
end
t[#t+1] = dir..f[i]
end
end
function shutil.find(...)
local paths = {...}
if not paths[1] then paths = {"."} end
for i=1, #paths do
if (paths[i]:sub(#paths[i]) ~= "/") then
paths[i] = paths[i] .. "/"
end
end
local t = {}
for i=1, #paths do
if fs.isDirectory(paths[i]) then
find_dir(t, paths[i])
end
t[#t+1] = paths[i]
end
return t
end
function shutil.filter(t, func)
local t2 = {}
for i=1, #t do
if (func(t[i])) then
t2[#t2+1] = t[i]
end
end
return t2
end
return shutil

13
lib/tsar.lua Normal file
View File

@ -0,0 +1,13 @@
local tsar = package.loaded.tsar or {}
function tsar.extract(fd, path, ...)
end
function tsar.create(paths, fd)
end
function tsar.list(fd)
end

14
lib/tsuki.lua Normal file
View File

@ -0,0 +1,14 @@
local foxfs = require "foxfs"
local tsuki = {}
function tsuki.reset_security_node(path)
end
function tsuki.reset_dev_permissions(path)
end
function tsuki.make_security_node(path)
end

7
lib/velx.lua Normal file
View File

@ -0,0 +1,7 @@
local velx = package.loaded.velx or {}
function velx.parse(fd)
end
return velx

47
luabuild.lua Normal file
View File

@ -0,0 +1,47 @@
local actions = {}
local function list(dir)
local h = io.popen("ls \""..dir.."\"", "r")
local ents = {}
for l in h:lines() do
ents[#ents+1] = l
end
h:close()
return ents
end
local function find(dir, pattern, inverse, ctype)
local h = io.popen("find \""..dir.."\" "..(((inverse) and "-not ") or "")..(((ctype) and "-type "..ctype.." ") or "").."-name \""..pattern.."\"", "r")
local ents = {}
for l in h:lines() do
ents[#ents+1] = l
end
h:close()
return ents
end
local function lastmod(file)
local h = io.popen("stat -c %Y \""..file.."\"")
local num = tonumber(h:read("*a"))
h:close()
return num
end
@[[local h = io.popen("ls .buildactions", "r")
for line in h:lines() do]]
--#include @[{".buildactions/"..line}]
@[[end]]
function actions.all()
for i=1, #actions do
actions[actions[i]]()
end
end
if not arg[1] then
arg[1] = "all"
end
actions[arg[1]]()
print("Build complete.")

21
module/bootloader.lua Normal file
View File

@ -0,0 +1,21 @@
_OSVERSION="PsychOS 2.0a2-$[[git rev-parse --short HEAD]]"
function lzss_decompress(a)local b,c,d,e,j,i,h,g=1,'',''while b<=#a do
e=c.byte(a,b)b=b+1
for k=0,7 do h=c.sub
g=h(a,b,b)if e>>k&1<1 and b<#a then
i=c.unpack('>I2',a,b)j=1+(i>>4)g=h(d,j,j+(i&15)+2)b=b+1
end
b=b+1
c=c..g
d=h(d..g,-4^6)end
end
return c end
local oload = load
function load(src, ...)
if (src:sub(1,4) == "\27ZLS") then
src = lzss_decompress(src:sub(5))
end
return oload(src, ...)
end
load($[[ luacomp -mluamin module/init.lua | lua zlua.lua | lua escape.lua ]])()

BIN
psychos.tsar Normal file

Binary file not shown.

View File

@ -1,95 +0,0 @@
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

View File

@ -1,25 +0,0 @@
local lastkey = computer.uptime()
local state = true
local delay = 60
function start()
return os.spawn(function()
while true do
tEv = {coroutine.yield()}
if tEv[1] == "key_down" then
lastkey = computer.uptime()
if not state then
for addr in component.list("screen") do
component.invoke(addr,"turnOn")
end
state = true
end
end
if computer.uptime() > lastkey + delay and state then
for addr in component.list("screen") do
component.invoke(addr,"turnOff")
end
state = false
end
end
end,"screenblank")
end

View File

@ -1,28 +0,0 @@
local counter = 0
local td = {}
local function addNode(addr)
devfs.register("tape"..tonumber(counter),function()
local tape = component.proxy(addr)
tape.seek(-math.huge)
return tape.read, tape.write, function() end, tape.seek
end)
devfs.register("tapen"..tonumber(counter),function()
local tape = component.proxy(addr)
return tape.read, tape.write, function() end, tape.seek
end)
td[addr] = counter
counter = counter + 1
end
for addr in component.list("tape_drive") do
addNode(addr)
end
while true do
local tE = {coroutine.yield()}
if tE[1] == "component_added" and tE[3] == "tape_drive" then
addNode(tE[2])
elseif tE[1] == "component_removed" and tE[3] == "tape_drive" then
if td[tE[2]] then
fs.remove("/dev/tape"..tostring(td[tE[2]]))
end
end
end

View File

@ -1,63 +0,0 @@
local minitel = require "minitel"
local shell = require "shell"
local port = 22
local function sread(self, len)
while true do
local d=self.sock:read(len)
if d then
return d
end
coroutine.yield()
end
end
local function swrite(self, data)
while self.flushing do
coroutine.yield()
end
if data and data:len() > 0 then
self.wb = self.wb .. (data or "")
end
end
local function sclose(self)
self.sock:close()
end
local function sflush(self)
self.flushing = true
self.sock:write(self.wb)
self.wb = ""
self.flushing = false
end
function start()
return os.spawn(function()
local oout = io.output()
local pname = os.taskInfo(os.pid()).name
while true do
local sock = minitel.listen(port)
print(string.format("Connection from %s:%d",sock.addr,sock.port))
os.spawn(function() _G.worked = {pcall(function()
local fh = {}
fh.sock = sock
fh.read = sread
fh.write = swrite
fh.close = sclose
fh.flush = sflush
fh.wb = ""
io.input(fh)
io.output(fh)
print(_OSVERSION.." - "..tostring(math.floor(computer.totalMemory()/1024)).."K RAM")
local pid = os.spawn(shell.interactive,string.format(pname.." shell [%s:%d]",sock.addr,sock.port))
repeat
coroutine.yield()
if fh.wb:len() > 0 then
fh:flush()
end
until sock.state ~= "open" or not os.taskInfo(pid)
sock:close()
os.kill(pid)
oout:write(string.format("Session %s:%d ended",sock.addr,sock.port))
end)} end,string.format(pname.." [%s:%d]",sock.addr,sock.port))
end
end,"termsrv")
end

View File

@ -1,162 +0,0 @@
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

View File

@ -1,18 +0,0 @@
local delay = 60
local port = 3442
local message = "WoLBeacon"
for modem in component.list("modem") do
component.invoke(modem,"setWakeMessage",message)
end
local ltime = computer.uptime()
while true do
if computer.uptime() > ltime+delay then
for modem in component.list("modem") do
component.invoke(modem,"broadcast",port,message)
end
ltime=computer.uptime()
end
coroutine.yield()
end

BIN
test.lua Normal file

Binary file not shown.

82
zlua.lua Normal file
View File

@ -0,0 +1,82 @@
local f = io.stdin:read("*a")
--[[----------------------------------------------------------------------------
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
io.stdout:write("\27ZLS"..M.compress(f))