From bb80ad3ceaaaabc57d7a732a09d9052d0b26d72e Mon Sep 17 00:00:00 2001 From: V1K1NGbg Date: Thu, 22 Aug 2024 17:48:59 -0300 Subject: Teal: convert luarocks.tools.tar --- src/luarocks/tools/tar.lua | 191 ----------------------------------------- src/luarocks/tools/tar.tl | 210 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+), 191 deletions(-) delete mode 100644 src/luarocks/tools/tar.lua create mode 100644 src/luarocks/tools/tar.tl diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua deleted file mode 100644 index bac7b2a9..00000000 --- a/src/luarocks/tools/tar.lua +++ /dev/null @@ -1,191 +0,0 @@ - ---- A pure-Lua implementation of untar (unpacking .tar archives) -local tar = {} - -local fs = require("luarocks.fs") -local dir = require("luarocks.dir") -local fun = require("luarocks.fun") - -local blocksize = 512 - -local function get_typeflag(flag) - if flag == "0" or flag == "\0" then return "file" - elseif flag == "1" then return "link" - elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU - elseif flag == "3" then return "character" - elseif flag == "4" then return "block" - elseif flag == "5" then return "directory" - elseif flag == "6" then return "fifo" - elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU - elseif flag == "x" then return "next file" - elseif flag == "g" then return "global extended header" - elseif flag == "L" then return "long name" - elseif flag == "K" then return "long link name" - end - return "unknown" -end - -local function octal_to_number(octal) - local exp = 0 - local number = 0 - octal = octal:gsub("%s", "") - for i = #octal,1,-1 do - local digit = tonumber(octal:sub(i,i)) - if not digit then - break - end - number = number + (digit * 8^exp) - exp = exp + 1 - end - return number -end - -local function checksum_header(block) - local sum = 256 - - if block:byte(1) == 0 then - return 0 - end - - for i = 1,148 do - local b = block:byte(i) or 0 - sum = sum + b - end - for i = 157,500 do - local b = block:byte(i) or 0 - sum = sum + b - end - - return sum -end - -local function nullterm(s) - return s:match("^[^%z]*") -end - -local function read_header_block(block) - local header = {} - header.name = nullterm(block:sub(1,100)) - header.mode = nullterm(block:sub(101,108)):gsub(" ", "") - header.uid = octal_to_number(nullterm(block:sub(109,116))) - header.gid = octal_to_number(nullterm(block:sub(117,124))) - header.size = octal_to_number(nullterm(block:sub(125,136))) - header.mtime = octal_to_number(nullterm(block:sub(137,148))) - header.chksum = octal_to_number(nullterm(block:sub(149,156))) - header.typeflag = get_typeflag(block:sub(157,157)) - header.linkname = nullterm(block:sub(158,257)) - header.magic = block:sub(258,263) - header.version = block:sub(264,265) - header.uname = nullterm(block:sub(266,297)) - header.gname = nullterm(block:sub(298,329)) - header.devmajor = octal_to_number(nullterm(block:sub(330,337))) - header.devminor = octal_to_number(nullterm(block:sub(338,345))) - header.prefix = block:sub(346,500) - - -- if header.magic ~= "ustar " and header.magic ~= "ustar\0" then - -- return false, ("Invalid header magic %6x"):format(bestring_to_number(header.magic)) - -- end - -- if header.version ~= "00" and header.version ~= " \0" then - -- return false, "Unknown version "..header.version - -- end - if header.typeflag == "unknown" then - if checksum_header(block) ~= header.chksum then - return false, "Failed header checksum" - end - end - return header -end - -function tar.untar(filename, destdir) - assert(type(filename) == "string") - assert(type(destdir) == "string") - - local tar_handle = io.open(filename, "rb") - if not tar_handle then return nil, "Error opening file "..filename end - - local long_name, long_link_name - local ok, err - local make_dir = fun.memoize(fs.make_dir) - while true do - local block - repeat - block = tar_handle:read(blocksize) - until (not block) or block:byte(1) > 0 - if not block then break end - if #block < blocksize then - ok, err = nil, "Invalid block size -- corrupted file?" - break - end - - local header - header, err = read_header_block(block) - if not header then - ok = false - break - end - - local file_data = "" - if header.size > 0 then - local nread = math.ceil(header.size / blocksize) * blocksize - file_data = tar_handle:read(header.size) - if nread > header.size then - tar_handle:seek("cur", nread - header.size) - end - end - - if header.typeflag == "long name" then - long_name = nullterm(file_data) - elseif header.typeflag == "long link name" then - long_link_name = nullterm(file_data) - else - if long_name then - header.name = long_name - long_name = nil - end - if long_link_name then - header.name = long_link_name - long_link_name = nil - end - end - local pathname = dir.path(destdir, header.name) - pathname = fs.absolute_name(pathname) - if header.typeflag == "directory" then - ok, err = make_dir(pathname) - if not ok then - break - end - elseif header.typeflag == "file" then - local dirname = dir.dir_name(pathname) - if dirname ~= "" then - ok, err = make_dir(dirname) - if not ok then - break - end - end - local file_handle - file_handle, err = io.open(pathname, "wb") - if not file_handle then - ok = nil - break - end - file_handle:write(file_data) - file_handle:close() - fs.set_time(pathname, header.mtime) - if header.mode:match("[75]") then - fs.set_permissions(pathname, "exec", "all") - else - fs.set_permissions(pathname, "read", "all") - end - end - --[[ - for k,v in pairs(header) do - util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) - end - util.printout() - --]] - end - tar_handle:close() - return ok, err -end - -return tar diff --git a/src/luarocks/tools/tar.tl b/src/luarocks/tools/tar.tl new file mode 100644 index 00000000..a156bf9f --- /dev/null +++ b/src/luarocks/tools/tar.tl @@ -0,0 +1,210 @@ + +--- A pure-Lua implementation of untar (unpacking .tar archives) +local record tar + record Header + name: string + mode: string + uid: integer + gid: integer + size: integer + mtime: integer + chksum: integer + typeflag: string + linkname: string + magic: string + version: string + uname: string + gname: string + devmajor: integer + devminor: integer + prefix: string + end +end + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local fun = require("luarocks.fun") + +local type Header = tar.Header + +local blocksize = 512 + +local function get_typeflag(flag: string): string + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal: string): integer + local exp = 0 + local number: integer = 0 + octal = octal:gsub("%s", "") + for i = #octal,1,-1 do + local digit = math.tointeger(octal:sub(i,i)) + if not digit then + break + end + number = number + (digit * math.tointeger(8^exp)) + exp = exp + 1 + end + return number +end + +local function checksum_header(block: string): number + local sum = 256 + + if block:byte(1) == 0 then + return 0 + end + + for i = 1,148 do + local b = block:byte(i) or 0 + sum = sum + b + end + for i = 157,500 do + local b = block:byte(i) or 0 + sum = sum + b + end + + return sum +end + +local function nullterm(s: string): string + return s:match("^[^%z]*") +end + +local function read_header_block(block: string): boolean | Header, string + local header: Header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)):gsub(" ", "") + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + + -- if header.magic ~= "ustar " and header.magic ~= "ustar\0" then + -- return false, ("Invalid header magic %6x"):format(bestring_to_number(header.magic)) + -- end + -- if header.version ~= "00" and header.version ~= " \0" then + -- return false, "Unknown version "..header.version + -- end + if header.typeflag == "unknown" then + if checksum_header(block) ~= header.chksum then + return false, "Failed header checksum" + end + end + return header +end + +function tar.untar(filename: string, destdir: string): boolean, string + + local tar_handle = io.open(filename, "rb") + if not tar_handle then return nil, "Error opening file "..filename end + + local long_name, long_link_name: string, string + local ok, err: boolean, string + local make_dir = fun.memoize(fs.make_dir) + while true do + local block: string + repeat + block = tar_handle:read(blocksize) + until (not block) or block:byte(1) > 0 + if not block then break end + if #block < blocksize then + ok, err = nil, "Invalid block size -- corrupted file?" + break + end + + local headerp: boolean | Header + headerp, err = read_header_block(block) + if not headerp then + ok = false + break + end + local header: Header = headerp as Header --! cast + local file_data = "" + if header.size > 0 then + local nread = math.ceil(header.size / blocksize) * blocksize + file_data = tar_handle:read(header.size) + if nread > header.size then + tar_handle:seek("cur", nread - header.size) + end + end + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = dir.path(destdir, header.name) + pathname = fs.absolute_name(pathname) + if header.typeflag == "directory" then + ok, err = make_dir(pathname) + if not ok then + break + end + elseif header.typeflag == "file" then + local dirname = dir.dir_name(pathname) + if dirname ~= "" then + ok, err = make_dir(dirname) + if not ok then + break + end + end + local file_handle: FILE + file_handle, err = io.open(pathname, "wb") + if not file_handle then + ok = nil + break + end + file_handle:write(file_data) + file_handle:close() + fs.set_time(pathname, header.mtime) + if header.mode:match("[75]") then + fs.set_permissions(pathname, "exec", "all") + else + fs.set_permissions(pathname, "read", "all") + end + end + --[[ + for k,v in pairs(header) do + util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) + end + util.printout() + --]] + end + tar_handle:close() + return ok, err +end + +return tar -- cgit v1.2.3-55-g6feb