aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/luarocks/tools/zip.lua187
1 files changed, 100 insertions, 87 deletions
diff --git a/src/luarocks/tools/zip.lua b/src/luarocks/tools/zip.lua
index 18ad967e..caeb4ac9 100644
--- a/src/luarocks/tools/zip.lua
+++ b/src/luarocks/tools/zip.lua
@@ -1,7 +1,11 @@
1 1
2module("luarocks.tools.zip", package.seeall) 2module("luarocks.tools.zip", package.seeall)
3 3
4local zlib = require "zlib" 4local zlib = require("zlib")
5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir")
7
8local size_buf = 65535
5 9
6local function number_to_bytestring(number, nbytes) 10local function number_to_bytestring(number, nbytes)
7 local out = {} 11 local out = {}
@@ -13,34 +17,17 @@ local function number_to_bytestring(number, nbytes)
13 return table.concat(out) 17 return table.concat(out)
14end 18end
15 19
16--- Return a zip handle open for writing.
17-- @param name filename of the zipfile to be created.
18-- @return a zip handle, or nil in case of error.
19function write_open(name)
20
21 local zf = {}
22
23 zf.ziphandle = io.open(name, "w")
24 if not zf.ziphandle then
25 return nil
26 end
27 zf.files = {}
28 zf.in_open_file = false
29
30 return zf
31end
32
33--- Begin a new file to be stored inside the zipfile. 20--- Begin a new file to be stored inside the zipfile.
34-- @param zf handle of the zipfile being written. 21-- @param self handle of the zipfile being written.
35-- @param filename filenome of the file to be added to the zipfile. 22-- @param filename filenome of the file to be added to the zipfile.
36-- @return true if succeeded, nil in case of failure. 23-- @return true if succeeded, nil in case of failure.
37function write_open_new_file_in_zip(zf, filename) 24local function zipwriter_open_new_file_in_zip(self, filename)
38 if zf.in_open_file then 25 if self.in_open_file then
39 close_file_in_zip(zf) 26 self:close_file_in_zip()
40 return nil 27 return nil
41 end 28 end
42 local lfh = {} 29 local lfh = {}
43 zf.local_file_header = lfh 30 self.local_file_header = lfh
44 lfh.last_mod_file_time = 0 -- TODO 31 lfh.last_mod_file_time = 0 -- TODO
45 lfh.last_mod_file_date = 0 -- TODO 32 lfh.last_mod_file_date = 0 -- TODO
46 lfh.crc32 = 0 -- initial value 33 lfh.crc32 = 0 -- initial value
@@ -49,40 +36,41 @@ function write_open_new_file_in_zip(zf, filename)
49 lfh.file_name_length = #filename 36 lfh.file_name_length = #filename
50 lfh.extra_field_length = 0 37 lfh.extra_field_length = 0
51 lfh.file_name = filename:gsub("\\", "/") 38 lfh.file_name = filename:gsub("\\", "/")
52 zf.in_open_file = true 39 lfh.external_attr = 0 -- TODO properly store permissions
53 zf.data = {} 40 self.in_open_file = true
41 self.data = {}
54 return true 42 return true
55end 43end
56 44
57--- Write data to the file currently being stored in the zipfile. 45--- Write data to the file currently being stored in the zipfile.
58-- @param zf handle of the zipfile being written. 46-- @param self handle of the zipfile being written.
59-- @param buf string containing data to be written. 47-- @param buf string containing data to be written.
60-- @return true if succeeded, nil in case of failure. 48-- @return true if succeeded, nil in case of failure.
61function write_in_file_in_zip(zf, buf) 49local function zipwriter_write_file_in_zip(self, buf)
62 if not zf.in_open_file then 50 if not self.in_open_file then
63 return nil 51 return nil
64 end 52 end
65 local lfh = zf.local_file_header 53 local lfh = self.local_file_header
66 local cbuf = zlib.compress(buf):sub(3, -5) 54 local cbuf = zlib.compress(buf):sub(3, -5)
67 lfh.crc32 = zlib.crc32(lfh.crc32, buf) 55 lfh.crc32 = zlib.crc32(lfh.crc32, buf)
68 lfh.compressed_size = lfh.compressed_size + #cbuf 56 lfh.compressed_size = lfh.compressed_size + #cbuf
69 lfh.uncompressed_size = lfh.uncompressed_size + #buf 57 lfh.uncompressed_size = lfh.uncompressed_size + #buf
70 table.insert(zf.data, cbuf) 58 table.insert(self.data, cbuf)
71 return true 59 return true
72end 60end
73 61
74--- Complete the writing of a file stored in the zipfile. 62--- Complete the writing of a file stored in the zipfile.
75-- @param zf handle of the zipfile being written. 63-- @param self handle of the zipfile being written.
76-- @return true if succeeded, nil in case of failure. 64-- @return true if succeeded, nil in case of failure.
77function write_close_file_in_zip(zf) 65local function zipwriter_close_file_in_zip(self)
78 local zh = zf.ziphandle 66 local zh = self.ziphandle
79 67
80 if not zf.in_open_file then 68 if not self.in_open_file then
81 return nil 69 return nil
82 end 70 end
83 71
84 -- Local file header 72 -- Local file header
85 local lfh = zf.local_file_header 73 local lfh = self.local_file_header
86 lfh.offset = zh:seek() 74 lfh.offset = zh:seek()
87 zh:write(number_to_bytestring(0x04034b50, 4)) -- signature 75 zh:write(number_to_bytestring(0x04034b50, 4)) -- signature
88 zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 76 zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0
@@ -98,7 +86,7 @@ function write_close_file_in_zip(zf)
98 zh:write(lfh.file_name) 86 zh:write(lfh.file_name)
99 87
100 -- File data 88 -- File data
101 for _, cbuf in ipairs(zf.data) do 89 for _, cbuf in ipairs(self.data) do
102 zh:write(cbuf) 90 zh:write(cbuf)
103 end 91 end
104 92
@@ -107,23 +95,59 @@ function write_close_file_in_zip(zf)
107 zh:write(number_to_bytestring(lfh.compressed_size, 4)) 95 zh:write(number_to_bytestring(lfh.compressed_size, 4))
108 zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) 96 zh:write(number_to_bytestring(lfh.uncompressed_size, 4))
109 97
110 table.insert(zf.files, lfh) 98 table.insert(self.files, lfh)
111 zf.in_open_file = false 99 self.in_open_file = false
112 100
113 return true 101 return true
114end 102end
115 103
104-- @return boolean or (boolean, string): true on success,
105-- false and an error message on failure.
106local function zipwriter_add(self, file)
107 local fin
108 local ok, err = self:open_new_file_in_zip(file)
109 if not ok then
110 err = "error in opening "..file.." in zipfile"
111 else
112 fin = io.open(file, "rb")
113 if not fin then
114 ok = false
115 err = "error opening "..file.." for reading"
116 end
117 end
118 while ok do
119 local buf = fin:read(size_buf)
120 if not buf then
121 break
122 end
123 ok = self:write_file_in_zip(buf)
124 if not ok then
125 err = "error in writing "..file.." in the zipfile"
126 end
127 end
128 if fin then
129 fin:close()
130 end
131 if ok then
132 ok = self:close_file_in_zip()
133 if not ok then
134 err = "error in writing "..file.." in the zipfile"
135 end
136 end
137 return ok == true, err
138end
139
116--- Complete the writing of the zipfile. 140--- Complete the writing of the zipfile.
117-- @param zf handle of the zipfile being written. 141-- @param self handle of the zipfile being written.
118-- @return true if succeeded, nil in case of failure. 142-- @return true if succeeded, nil in case of failure.
119function write_close(zf) 143local function zipwriter_close(self)
120 local zh = zf.ziphandle 144 local zh = self.ziphandle
121 145
122 local central_directory_offset = zh:seek() 146 local central_directory_offset = zh:seek()
123 147
124 local size_of_central_directory = 0 148 local size_of_central_directory = 0
125 -- Central directory structure 149 -- Central directory structure
126 for _, lfh in ipairs(zf.files) do 150 for _, lfh in ipairs(self.files) do
127 zh:write(number_to_bytestring(0x02014b50, 4)) -- signature 151 zh:write(number_to_bytestring(0x02014b50, 4)) -- signature
128 zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX 152 zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX
129 zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 153 zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0
@@ -139,7 +163,7 @@ function write_close(zf)
139 zh:write(number_to_bytestring(0, 2)) -- file comment length 163 zh:write(number_to_bytestring(0, 2)) -- file comment length
140 zh:write(number_to_bytestring(0, 2)) -- disk number start 164 zh:write(number_to_bytestring(0, 2)) -- disk number start
141 zh:write(number_to_bytestring(0, 2)) -- internal file attributes 165 zh:write(number_to_bytestring(0, 2)) -- internal file attributes
142 zh:write(number_to_bytestring(0, 4)) -- external file attributes 166 zh:write(number_to_bytestring(lfh.external_attr, 4)) -- external file attributes
143 zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header 167 zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header
144 zh:write(lfh.file_name) 168 zh:write(lfh.file_name)
145 size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length 169 size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length
@@ -149,8 +173,8 @@ function write_close(zf)
149 zh:write(number_to_bytestring(0x06054b50, 4)) -- signature 173 zh:write(number_to_bytestring(0x06054b50, 4)) -- signature
150 zh:write(number_to_bytestring(0, 2)) -- number of this disk 174 zh:write(number_to_bytestring(0, 2)) -- number of this disk
151 zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory 175 zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory
152 zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir on this disk 176 zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir on this disk
153 zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir 177 zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir
154 zh:write(number_to_bytestring(size_of_central_directory, 4)) 178 zh:write(number_to_bytestring(size_of_central_directory, 4))
155 zh:write(number_to_bytestring(central_directory_offset, 4)) 179 zh:write(number_to_bytestring(central_directory_offset, 4))
156 zh:write(number_to_bytestring(0, 2)) -- zip file comment length 180 zh:write(number_to_bytestring(0, 2)) -- zip file comment length
@@ -159,40 +183,27 @@ function write_close(zf)
159 return true 183 return true
160end 184end
161 185
162-- @return boolean or (boolean, string): true on success, 186--- Return a zip handle open for writing.
163-- false and an error message on failure. 187-- @param name filename of the zipfile to be created.
164local function add_to_zip(zf, file) 188-- @return a zip handle, or nil in case of error.
165 local fin 189function new_zipwriter(name)
166 local ok, err = write_open_new_file_in_zip(zf, file) 190
167 if not ok then 191 local zw = {}
168 err = "error in opening "..file.." in zipfile" 192
169 else 193 zw.ziphandle = io.open(name, "w")
170 fin = io.open(file, "rb") 194 if not zw.ziphandle then
171 if not fin then 195 return nil
172 ok = false
173 err = "error opening "..file.." for reading"
174 end
175 end
176 while ok do
177 local buf = fin:read(size_buf)
178 if not buf then
179 break
180 end
181 ok = write_in_file_in_zip(zf, buf)
182 if not ok then
183 err = "error in writing "..file.." in the zipfile"
184 end
185 end
186 if fin then
187 fin:close()
188 end
189 if ok then
190 ok = write_close_file_in_zip(zf)
191 if not ok then
192 err = "error in writing "..file.." in the zipfile"
193 end
194 end 196 end
195 return ok == true, err 197 zw.files = {}
198 zw.in_open_file = false
199
200 zw.add = zipwriter_add
201 zw.close = zipwriter_close
202 zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip
203 zw.write_file_in_zip = zipwriter_write_file_in_zip
204 zw.close_file_in_zip = zipwriter_close_file_in_zip
205
206 return zw
196end 207end
197 208
198--- Compress files in a .zip archive. 209--- Compress files in a .zip archive.
@@ -202,29 +213,31 @@ end
202-- @return boolean or (boolean, string): true on success, 213-- @return boolean or (boolean, string): true on success,
203-- false and an error message on failure. 214-- false and an error message on failure.
204function zip(zipfile, ...) 215function zip(zipfile, ...)
205 local zf = write_open(filename) 216 local zw = new_zipwriter(zipfile)
206 if not zf then 217 if not zw then
207 return nil, "error opening "..filename 218 return nil, "error opening "..zipfile
208 end 219 end
209 220
210 local ok, err 221 local ok, err
211 for _, file in pairs({...}) do 222 for _, file in pairs({...}) do
212 if fs.is_dir(file) then 223 if fs.is_dir(file) then
213 for _, file in pairs(fs.find(file)) do 224 for _, entry in pairs(fs.find(file)) do
214 if fs.is_file(file) then 225 local fullname = dir.path(file, entry)
215 ok, err = add_to_zip(file) 226 if fs.is_file(fullname) then
227 ok, err = zw:add(fullname)
216 if not ok then break end 228 if not ok then break end
217 end 229 end
218 end 230 end
219 else 231 else
220 ok, err = add_to_zip(file) 232 ok, err = zw:add(file)
221 if not ok then break end 233 if not ok then break end
222 end 234 end
223 end 235 end
224 236
225 local ok = write_close(zf) 237 local ok = zw:close()
226 if not ok then 238 if not ok then
227 return false, "error closing "..filename 239 return false, "error closing "..zipfile
228 end 240 end
229 return ok, err 241 return ok, err
230end 242end
243