Skip to content

Instantly share code, notes, and snippets.

@rxi
Created January 25, 2018 19:29
Show Gist options
  • Save rxi/8fde54c7b8f4cc43953a35369dab1e1d to your computer and use it in GitHub Desktop.
Save rxi/8fde54c7b8f4cc43953a35369dab1e1d to your computer and use it in GitHub Desktop.
--
-- tarreader.lua
--
-- Copyright (c) 2018 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to
-- deal in the Software without restriction, including without limitation the
-- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-- sell copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- 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 OR COPYRIGHT HOLDERS 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.
--
local TarReader = {}
TarReader.__index = TarReader
local FileStream = {}
FileStream.__index = FileStream
function FileStream.new(filename)
local file = assert( io.open(filename, "rb") )
local self = setmetatable({}, FileStream)
self.file = file
return self
end
function FileStream:read(n)
return self.file:read(n)
end
function FileStream:seek(n)
self.file:seek("set", n)
end
function FileStream:close()
self.file:close()
end
local function round_up(n, incr)
return n + (incr - n % incr) % incr;
end
local function read_string(stream, n)
return stream:read(n):gsub("%z", "")
end
local function read_header(stream)
return {
name = read_string(stream, 100 ),
mode = tonumber( read_string(stream, 8 ), 8),
owner = tonumber( read_string(stream, 8 ), 8),
group = read_string(stream, 8 ),
size = tonumber( read_string(stream, 12 ), 8),
mtime = tonumber( read_string(stream, 12 ), 8),
checksum = read_string(stream, 8 ),
type = read_string(stream, 1 ),
linkname = read_string(stream, 100 ),
}
end
function TarReader.new(stream)
if type(stream) == "string" then
stream = FileStream.new(stream)
end
local self = setmetatable({}, TarReader)
self.stream = assert(stream)
return self
end
function TarReader:close()
self.stream:close()
end
function TarReader:each()
local pos = 0
return function()
self.stream:seek(pos)
local header = read_header(self.stream)
header.position = pos + 512
if header.name == "" then
return nil
end
pos = pos + 512 + round_up(header.size, 512)
return header
end
end
function TarReader:read(filename)
for header in self:each() do
if header.name == filename then
self.stream:seek(header.position)
return self.stream:read(header.size)
end
end
return nil, "could not find file"
end
return TarReader
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment