~rootmos/lua-hack

74d664f9095ffc8d05def8ce9cd0a5b8142e35dc — Gustav Behm 1 year, 2 months ago f443c78 tar
Move tar code into its module
2 files changed, 207 insertions(+), 187 deletions(-)

M tar/go.lua
M tar/tar.lua
M tar/go.lua => tar/go.lua +2 -187
@@ 1,195 1,10 @@
local tar = require("tar")
local hex = require("hex")

local function extract_string(bs, o, l)
    local i = o + 1
    local j = i + l - 1

    if bs:byte(i) == 0 then
        return ""
    end

    local k = i
    while k+1 <= j and bs:byte(k+1) > 0 do
        k = k + 1
    end

    return bs:sub(i, k)
end

local function exp8(x)
    local p = 1
    while x > 0 do
        p = 8 * p
        x = x - 1
    end
    return p
end

local function extract_integer(bs, o, l)
    local i = o + 1
    local j = i + l - 1

    assert(bs:byte(j) == 0, "number should end with a nul character")

    local s = 0
    while i < j do
        s = s + exp8(j - i - 1)*(bs:byte(i) - 48)
        i = i + 1
    end

    return s
end

local function end_of_archive(bs)
    if bs:byte(156) ~= 0 then
        return false
    end

    for i = 1, 512 do
        assert(bs:byte(i) == 0)
    end

    return true
end

local function checksum(bs)
    local chk = extract_integer(bs, 148, 7)
    assert(bs:byte(156) == 32)

    local s = 0
    for i = 1, 512 do
        if 149 <= i and i <= 156 then
            s = s + 32
        else
            s = s + bs:byte(i)
        end
    end
    if s ~= chk then
        error("checksum verification failed")
    end
end

local function extract_type(bs)
    local t = bs:byte(157)
    if t == 48 then
        return "regular"
    elseif t == 53 then
        return "directory"
    end
    error(string.format("unsupported type: %d", t))
end

local function header(bs)
    local hdr = {}

    checksum(bs)

    local magic = extract_string(bs, 257, 8)
    assert(magic == "ustar  ")

    hdr.filename = extract_string(bs, 0, 100)
    hdr.mode = extract_integer(bs, 100, 8)
    hdr.uid = extract_integer(bs, 108, 8)
    hdr.gid = extract_integer(bs, 116, 8)
    hdr.size = extract_integer(bs, 124, 12)
    hdr.mtime = extract_integer(bs, 136, 12)

    hdr.type = extract_type(bs)

    hdr.linked_filename = extract_string(bs, 157, 100)
    hdr.uname = extract_string(bs, 265, 32)
    hdr.gname = extract_string(bs, 297, 32)

    --hdr.devmajor = extract_integer(bs, 329, 8)
    --hdr.devminor = extract_integer(bs, 337, 8)

    hdr.prefix = extract_string(bs, 345, 155)

    return hdr
end

local function fnext(st, o)
    local bs = st.f:read(512)
    if bs == nil then
        return nil
    end
    assert(#bs == 512)
    local p = st.o
    st.o = o + 512
    return p, bs
end

local function blocks_from_file(fn)
    local st = {
        f = io.open(fn),
        o = 0,
    }
    return fnext, st, st.o, st.f
end

local function bsnext(st, o)
    if #st.bs == 0 then
        return nil
    end
    local bs = st.bs:sub(1,512)
    st.bs = st.bs:sub(513)
    local p = st.o
    st.o = o + 512
    return p, bs
end

local function blocks_from_string(bs)
    local st = {
        bs = bs,
        o = 0,
    }
    return bsnext, st, st.o
end

local bs = io.open(arg[1]):read("a")

local tarball = {}
local hdr = nil
local content = ""
local l = 0
local e = 0
--for o, bs in blocks_from_file(arg[1]) do
for o, bs in blocks_from_string(bs) do
    if e > 0 then
        if not end_of_archive(bs) then
            error("unexpected non-zero block")
        end
        e = e + 1
    elseif hdr == nil then
        if end_of_archive(bs) then
            e = e + 1
        else
            hdr = header(bs)
            if hdr.size == 0 then
                tarball[hdr.prefix .. hdr.filename] = ""
                hdr = nil
            end
        end
    else
        if l + 512 >= hdr.size then
            content = content .. bs:sub(1, hdr.size - l)
            tarball[hdr.prefix .. hdr.filename] = content
            hdr = nil
            content = ""
            l = 0
        else
            content = content .. bs
        end
    end
end
assert(e >= 2)

for fn, bs in pairs(tarball) do
--for fn, bs in pairs(tar.from_file(arg[1])) do
for fn, bs in pairs(tar.from_string(bs)) do
    print(fn)
    print(bs)
end

--local bs = io.open(arg[1]):read("a")

--print(header(bs).filename)

M tar/tar.lua => tar/tar.lua +205 -0
@@ 1,3 1,208 @@
local M = {}

local function extract_string(bs, o, l)
    local i = o + 1
    local j = i + l - 1

    if bs:byte(i) == 0 then
        return ""
    end

    local k = i
    while k+1 <= j and bs:byte(k+1) > 0 do
        k = k + 1
    end

    return bs:sub(i, k)
end

local function exp8(x)
    local p = 1
    while x > 0 do
        p = 8 * p
        x = x - 1
    end
    return p
end

local function extract_integer(bs, o, l)
    local i = o + 1
    local j = i + l - 1

    assert(bs:byte(j) == 0, "number should end with a nul character")

    local s = 0
    while i < j do
        s = s + exp8(j - i - 1)*(bs:byte(i) - 48)
        i = i + 1
    end

    return s
end

local function end_of_archive(bs)
    if bs:byte(156) ~= 0 then
        return false
    end

    for i = 1, 512 do
        assert(bs:byte(i) == 0)
    end

    return true
end

local function checksum(bs)
    local chk = extract_integer(bs, 148, 7)
    assert(bs:byte(156) == 32)

    local s = 0
    for i = 1, 512 do
        if 149 <= i and i <= 156 then
            s = s + 32
        else
            s = s + bs:byte(i)
        end
    end
    if s ~= chk then
        error("checksum verification failed")
    end
end

local function extract_type(bs)
    local t = bs:byte(157)
    if t == 48 then
        return "regular"
    elseif t == 53 then
        return "directory"
    end
    error(string.format("unsupported type: %d", t))
end

local function header(bs)
    local hdr = {}

    checksum(bs)

    local magic = extract_string(bs, 257, 8)
    assert(magic == "ustar  ")

    hdr.filename = extract_string(bs, 0, 100)
    hdr.mode = extract_integer(bs, 100, 8)
    hdr.uid = extract_integer(bs, 108, 8)
    hdr.gid = extract_integer(bs, 116, 8)
    hdr.size = extract_integer(bs, 124, 12)
    hdr.mtime = extract_integer(bs, 136, 12)

    hdr.type = extract_type(bs)

    hdr.linked_filename = extract_string(bs, 157, 100)
    hdr.uname = extract_string(bs, 265, 32)
    hdr.gname = extract_string(bs, 297, 32)

    --hdr.devmajor = extract_integer(bs, 329, 8)
    --hdr.devminor = extract_integer(bs, 337, 8)

    hdr.prefix = extract_string(bs, 345, 155)

    return hdr
end

local function fnext(st, o)
    local bs = st.f:read(512)
    if bs == nil then
        return nil
    end
    assert(#bs == 512)
    local p = st.o
    st.o = o + 512
    return p, bs
end

local function blocks_from_file(fn)
    local st = {
        f = io.open(fn),
        o = 0,
    }
    return fnext, st, st.o, st.f
end

local function bsnext(st, o)
    if #st.bs == 0 then
        return nil
    end
    local bs = st.bs:sub(1,512)
    st.bs = st.bs:sub(513)
    local p = st.o
    st.o = o + 512
    return p, bs
end

local function blocks_from_string(bs)
    local st = {
        bs = bs,
        o = 0,
    }
    return bsnext, st, st.o
end

local function mk_entry(hdr, content)
    return {
        filename = hdr.prefix .. hdr.filename,
        mode = hdr.mode,
        size = hdr.size,
        content = content,
    }
end

local function mk(f)
    local tarball = {}
    local hdr = nil
    local content = ""
    local l = 0
    local e = 0
    for o, bs in f() do
        if e > 0 then
            if not end_of_archive(bs) then
                error("unexpected non-zero block")
            end
            e = e + 1
        elseif hdr == nil then
            if end_of_archive(bs) then
                e = e + 1
            else
                hdr = header(bs)
                if hdr.size == 0 then
                    local entry = mk_entry(hdr, content)
                    tarball[entry.filename] = entry
                    hdr = nil
                end
            end
        else
            if l + 512 >= hdr.size then
                content = content .. bs:sub(1, hdr.size - l)

                local entry = mk_entry(hdr, content)
                tarball[entry.filename] = entry

                hdr = nil
                content = ""
                l = 0
            else
                content = content .. bs
            end
        end
    end
    assert(e >= 2)
    return tarball
end

function M.from_file(fn)
    return mk(function() return blocks_from_file(fn) end)
end

function M.from_string(s)
    return mk(function() return blocks_from_string(s) end)
end

return M