moved the unarchive function out of libmtar, making it significantly simpler and more compact, and allowing implementations to deal with the implementation details

This commit is contained in:
Izaya 2020-05-30 09:51:47 +10:00
parent f20719f3cb
commit 235684b979
2 changed files with 63 additions and 92 deletions

View File

@ -1,5 +1,6 @@
local shell = require "shell" local shell = require "shell"
local mtar = require "libmtar" local mtar = require "libmtar"
local fs = require "filesystem"
local args, opts = shell.parse(...) local args, opts = shell.parse(...)
@ -30,43 +31,78 @@ local function vp(...)
end end
end end
if opts.x then 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 unarchive(stream,dest,verbose,csize)
csize = csize or 2048
function vwrite(...)
if verbose then
io.write(...)
end
end
for fname, read, len in mtar.iter(stream) do
local written = 0
fname = cleanPath(fname)
vwrite(string.format("%s %i... ",fname,len))
local dir = fname:match("(.+)/.*%.?.+")
if dir then
if dir:sub(1,1) ~= "/" then
dir = os.getenv("PWD").."/"..dir
end
fs.makeDirectory(dest.."/"..dir)
end
local f = io.open(dest.."/"..fname,"wb")
if f then
repeat
local rb = read(csize)
written = written + rb:len()
f:write(rb)
vwrite(written)
vwrite(" ")
until written == len
f:close()
vwrite("done\n")
else
vwrite("failed to open "..fname.."\n")
end
end
end
function open(fn,mode)
local f local f
if opts.z then if opts.z then
f = lz16.open(args[1],"rb") f = lz16.open(fn, mode)
else else
f = io.open(args[1],"rb") f = io.open(fn, mode)
end end
return f
end
if opts.x then
local f = open(args[1],"rb")
if not f then error("unable to open file") end if not f then error("unable to open file") end
local w, r = mtar.unarchive(f,args[2] or ".",opts.v) local w, r = unarchive(f,args[2] or ".",opts.v)
f:close() f:close()
elseif opts.t then elseif opts.t then
local f local f = open(args[1],"rb")
if opts.z then
f = lz16.open(args[1],"rb")
else
f = io.open(args[1],"rb")
end
if not f then error("unable to open file") end if not f then error("unable to open file") end
while true do for name, _, fsize in mtar.iter(f) do
local nlen = toint(f:read(2))
if nlen == 0 then
break
end
local name = f:read(nlen)
local fsize = toint(f:read(2) or "\0\0")
f:read(fsize)
print(name..": "..tostring(fsize)) print(name..": "..tostring(fsize))
end end
f:close() f:close()
elseif opts.c then elseif opts.c then
local mode = (opts.r and "ab") or "wb" local mode = (opts.r and "ab") or "wb"
local f local f = open(args[1],mode)
if opts.z then
f = lz16.open(args[1],mode)
else
f = io.open(args[1],mode)
end
if not f then error("unable to open file") end if not f then error("unable to open file") end
table.remove(args,1) table.remove(args,1)
local fs = require "filesystem" local fs = require "filesystem"
@ -122,5 +158,6 @@ Operations:
t: list contents of archive t: list contents of archive
Flags: Flags:
v: be verbose v: be verbose
z: use LZSS compression (requires liblz16)]]) z: use LZSS compression (requires liblz16)
r: append rather than overwrite]])
end end

View File

@ -1,24 +1,5 @@
local mtar = {} 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 function toint(s)
local n = 0 local n = 0
local i = 1 local i = 1
@ -38,18 +19,6 @@ local function cint(n,l)
return string.reverse(string.char(table.unpack(t)):sub(1,l)) return string.reverse(string.char(table.unpack(t)):sub(1,l))
end 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* 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)) return string.format("%s%s%s",cint(fname:len(),2),fname,cint(len,2))
end end
@ -67,46 +36,11 @@ function mtar.iter(stream) -- table -- function -- Given buffer *stream*, return
if nlen == 0 then if nlen == 0 then
return return
end end
local name = cleanPath(stream:read(nlen)) local name = stream:read(nlen)
local fsize = toint(stream:read(2)) local fsize = toint(stream:read(2))
remain = fsize remain = fsize
return name, read, fsize return name, read, fsize
end end
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 return mtar