From 0f1b324cc44ed84f0cf0d8a970367bbb747d7f3e Mon Sep 17 00:00:00 2001 From: XeonSquared Date: Fri, 5 Jun 2020 12:10:48 +1000 Subject: [PATCH] initial pkgfs work, seems functional --- lib/libmtar.lua | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/pkgfs.lua | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 lib/libmtar.lua create mode 100644 lib/pkgfs.lua diff --git a/lib/libmtar.lua b/lib/libmtar.lua new file mode 100644 index 0000000..0da8f7d --- /dev/null +++ b/lib/libmtar.lua @@ -0,0 +1,112 @@ +local mtar = {} + +-- detect OS hopefully +if _OSVERSION then + if _OSVERSION:sub(1,8) == "OpenOS" then + OPENOS = true + elseif _OSVERSION:sub(1,9) == "PsychOS" then + PSYCHOS = true + end +else + LINUX = true +end + +local function mkdir(dir) + if OPENOS or LINUX then + os.execute("mkdir "..dest.."/"..dir.." &> /dev/null") + elseif PSYCHOS then + -- todo: write PsychOS support + end +end + +local function toint(s) + local n = 0 + local i = 1 + for p in s:gmatch(".") do + n = n << 8 + n = n | string.byte(p) + i=i+1 + end + return n +end + +local function cint(n,l) + local t={} + for i = 0, 7 do + t[i+1] = (n >> (i * 8)) & 0xFF + end + return string.reverse(string.char(table.unpack(t)):sub(1,l)) +end + +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) -- generate a header for file *fname* when provided with file length *len* + return string.format("%s%s%s",cint(fname:len(),2),fname,cint(len,2)) +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)) + remain = remain - rb:len() + return rb + end + return function() + stream:read(remain) + local nlen = toint(stream:read(2) or "\0\0") + if nlen == 0 then + return + end + local name = cleanPath(stream:read(nlen)) + local fsize = toint(stream:read(2)) + remain = fsize + return name, read, fsize + end +end + +function mtar.unarchive(stream,dest,verbose) -- Extract mtar archive read from *stream* to *dest*. If *verbose*, print status. + dest = dest or "." + while true do + local nlen = toint(stream:read(2) or "\0\0") + if nlen == 0 then + break + end + local name = cleanPath(stream:read(nlen)) + local fsize = toint(stream:read(2)) + if verbose then + io.write(name.." "..tostring(fsize).."... ") + end + local dir = name:match("(.+)/.*%.?.+") + if (dir) then + mkdir(dir) + end + local f = io.open(dest.."/"..name,"wb") + local rsize,buf = fsize, "" + if f then + repeat + buf = stream:read(math.min(rsize,2048)) + f:write(buf) + rsize = rsize - buf:len() + if verbose then + io.write(tostring(rsize).." ") + end + until rsize <= 1 + f:close() + end + if verbose then + print("done.") + end + end +end + +return mtar diff --git a/lib/pkgfs.lua b/lib/pkgfs.lua new file mode 100644 index 0000000..81332b5 --- /dev/null +++ b/lib/pkgfs.lua @@ -0,0 +1,109 @@ +local mtar = require "libmtar" +local w, lz16 = pcall(require, "liblz16") +if not w then lz16 = nil end + +pkgfs = {} +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} + +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.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 + +function pkgfs.add(fname,comp) + local f = fopen(fname,comp) + for name, read, fsize in mtar.iter(f) do + findex[fnormalize(name)] = {fname,comp} + end + f:close() +end + +return pkgfs