diff options
-rw-r--r-- | src/luarocks/tools/zip.lua | 187 |
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 | ||
2 | module("luarocks.tools.zip", package.seeall) | 2 | module("luarocks.tools.zip", package.seeall) |
3 | 3 | ||
4 | local zlib = require "zlib" | 4 | local zlib = require("zlib") |
5 | local fs = require("luarocks.fs") | ||
6 | local dir = require("luarocks.dir") | ||
7 | |||
8 | local size_buf = 65535 | ||
5 | 9 | ||
6 | local function number_to_bytestring(number, nbytes) | 10 | local 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) |
14 | end | 18 | end |
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. | ||
19 | function 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 | ||
31 | end | ||
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. |
37 | function write_open_new_file_in_zip(zf, filename) | 24 | local 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 |
55 | end | 43 | end |
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. |
61 | function write_in_file_in_zip(zf, buf) | 49 | local 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 |
72 | end | 60 | end |
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. |
77 | function write_close_file_in_zip(zf) | 65 | local 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 |
114 | end | 102 | end |
115 | 103 | ||
104 | -- @return boolean or (boolean, string): true on success, | ||
105 | -- false and an error message on failure. | ||
106 | local 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 | ||
138 | end | ||
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. |
119 | function write_close(zf) | 143 | local 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 |
160 | end | 184 | end |
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. |
164 | local function add_to_zip(zf, file) | 188 | -- @return a zip handle, or nil in case of error. |
165 | local fin | 189 | function 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 | ||
196 | end | 207 | end |
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. |
204 | function zip(zipfile, ...) | 215 | function 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 |
230 | end | 242 | end |
243 | |||