2020-03-04 15:26:49 +11:00
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
2020-03-06 22:10:16 +11:00
for p in s : gmatch ( " . " ) do
2020-03-04 15:26:49 +11:00
n = n << 8
2020-03-06 22:10:16 +11:00
n = n | string.byte ( p )
2020-03-04 15:26:49 +11:00
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
2020-05-29 10:54:00 +10:00
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
2020-03-06 22:10:16 +11:00
function mtar . unarchive ( stream , dest , verbose ) -- Extract mtar archive read from *stream* to *dest*. If *verbose*, print status.
2020-03-04 15:26:49 +11:00
dest = dest or " . "
while true do
2020-03-06 22:10:16 +11:00
local nlen = toint ( stream : read ( 2 ) or " \0 \0 " )
2020-03-04 15:26:49 +11:00
if nlen == 0 then
break
end
local name = cleanPath ( stream : read ( nlen ) )
local fsize = toint ( stream : read ( 2 ) )
2020-03-06 22:10:16 +11:00
if verbose then
io.write ( name .. " " .. tostring ( fsize ) .. " ... " )
end
2020-03-04 15:26:49 +11:00
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
2020-03-06 22:10:16 +11:00
buf = stream : read ( math.min ( rsize , 2048 ) )
2020-03-04 15:26:49 +11:00
f : write ( buf )
rsize = rsize - buf : len ( )
2020-03-06 22:10:16 +11:00
if verbose then
io.write ( tostring ( rsize ) .. " " )
end
2020-03-04 15:26:49 +11:00
until rsize <= 1
f : close ( )
end
2020-03-06 22:10:16 +11:00
if verbose then
print ( " done. " )
end
2020-03-04 15:26:49 +11:00
end
end
return mtar