Welcome to mirror list, hosted at ThFree Co, Russian Federation.

tar.lua « tools « luarocks « src - github.com/torch/luajit-rocks.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b2bd930a2560c1f6390448be3e5e613edc8199d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

--- A pure-Lua implementation of untar (unpacking .tar archives)
--module("luarocks.tools.tar", package.seeall)
local tar = {}

local fs = require("luarocks.fs")
local dir = require("luarocks.dir")
local util = require("luarocks.util")

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
   for i = #octal,1,-1 do
      local digit = tonumber(octal:sub(i,i)) 
      if digit then
         number = number + (digit * 8^exp)
         exp = exp + 1
      end
   end
   return number
end

local function checksum_header(block)
   local sum = 256
   for i = 1,148 do
      sum = sum + block:byte(i)
   end
   for i = 157,500 do
      sum = sum + block:byte(i)
   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))
   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 "..header.magic
   end
   if header.version ~= "00" and header.version ~= " \0" then
      return false, "Unknown version "..header.version
   end
   if not checksum_header(block) == header.chksum then
      return false, "Failed header checksum"
   end
   return header
end

function tar.untar(filename, destdir)
   assert(type(filename) == "string")
   assert(type(destdir) == "string")

   local tar_handle = io.open(filename, "r")
   if not tar_handle then return nil, "Error opening file "..filename end
   
   local long_name, long_link_name
   while true do
      local block
      repeat 
         block = tar_handle:read(blocksize)
      until (not block) or checksum_header(block) > 256
      if not block then break end
      local header, err = read_header_block(block)
      if not header then
         util.printerr(err)
      end

      local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size)

      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)
      if header.typeflag == "directory" then
         local ok, err = fs.make_dir(pathname)
         if not ok then return nil, err end
      elseif header.typeflag == "file" then
         local dirname = dir.dir_name(pathname)
         if dirname ~= "" then
            local ok, err = fs.make_dir(dirname)
            if not ok then return nil, err end
         end
         local file_handle = io.open(pathname, "wb")
         file_handle:write(file_data)
         file_handle:close()
         fs.set_time(pathname, header.mtime)
         if fs.chmod then
            fs.chmod(pathname, header.mode)
         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 true
end

return tar