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