From 6ecf9ab8ee490cc562cdd199f1533a4cf3f1b918 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 8 Nov 2010 14:58:18 -0200 Subject: Fixes for native zip packing module. --- src/luarocks/tools/zip.lua | 187 ++++++++++++++++++++++++--------------------- 1 file 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 @@ module("luarocks.tools.zip", package.seeall) -local zlib = require "zlib" +local zlib = require("zlib") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +local size_buf = 65535 local function number_to_bytestring(number, nbytes) local out = {} @@ -13,34 +17,17 @@ local function number_to_bytestring(number, nbytes) return table.concat(out) end ---- Return a zip handle open for writing. --- @param name filename of the zipfile to be created. --- @return a zip handle, or nil in case of error. -function write_open(name) - - local zf = {} - - zf.ziphandle = io.open(name, "w") - if not zf.ziphandle then - return nil - end - zf.files = {} - zf.in_open_file = false - - return zf -end - --- Begin a new file to be stored inside the zipfile. --- @param zf handle of the zipfile being written. +-- @param self handle of the zipfile being written. -- @param filename filenome of the file to be added to the zipfile. -- @return true if succeeded, nil in case of failure. -function write_open_new_file_in_zip(zf, filename) - if zf.in_open_file then - close_file_in_zip(zf) +local function zipwriter_open_new_file_in_zip(self, filename) + if self.in_open_file then + self:close_file_in_zip() return nil end local lfh = {} - zf.local_file_header = lfh + self.local_file_header = lfh lfh.last_mod_file_time = 0 -- TODO lfh.last_mod_file_date = 0 -- TODO lfh.crc32 = 0 -- initial value @@ -49,40 +36,41 @@ function write_open_new_file_in_zip(zf, filename) lfh.file_name_length = #filename lfh.extra_field_length = 0 lfh.file_name = filename:gsub("\\", "/") - zf.in_open_file = true - zf.data = {} + lfh.external_attr = 0 -- TODO properly store permissions + self.in_open_file = true + self.data = {} return true end --- Write data to the file currently being stored in the zipfile. --- @param zf handle of the zipfile being written. +-- @param self handle of the zipfile being written. -- @param buf string containing data to be written. -- @return true if succeeded, nil in case of failure. -function write_in_file_in_zip(zf, buf) - if not zf.in_open_file then +local function zipwriter_write_file_in_zip(self, buf) + if not self.in_open_file then return nil end - local lfh = zf.local_file_header + local lfh = self.local_file_header local cbuf = zlib.compress(buf):sub(3, -5) lfh.crc32 = zlib.crc32(lfh.crc32, buf) lfh.compressed_size = lfh.compressed_size + #cbuf lfh.uncompressed_size = lfh.uncompressed_size + #buf - table.insert(zf.data, cbuf) + table.insert(self.data, cbuf) return true end --- Complete the writing of a file stored in the zipfile. --- @param zf handle of the zipfile being written. +-- @param self handle of the zipfile being written. -- @return true if succeeded, nil in case of failure. -function write_close_file_in_zip(zf) - local zh = zf.ziphandle +local function zipwriter_close_file_in_zip(self) + local zh = self.ziphandle - if not zf.in_open_file then + if not self.in_open_file then return nil end -- Local file header - local lfh = zf.local_file_header + local lfh = self.local_file_header lfh.offset = zh:seek() zh:write(number_to_bytestring(0x04034b50, 4)) -- signature zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 @@ -98,7 +86,7 @@ function write_close_file_in_zip(zf) zh:write(lfh.file_name) -- File data - for _, cbuf in ipairs(zf.data) do + for _, cbuf in ipairs(self.data) do zh:write(cbuf) end @@ -107,23 +95,59 @@ function write_close_file_in_zip(zf) zh:write(number_to_bytestring(lfh.compressed_size, 4)) zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) - table.insert(zf.files, lfh) - zf.in_open_file = false + table.insert(self.files, lfh) + self.in_open_file = false return true end +-- @return boolean or (boolean, string): true on success, +-- false and an error message on failure. +local function zipwriter_add(self, file) + local fin + local ok, err = self:open_new_file_in_zip(file) + if not ok then + err = "error in opening "..file.." in zipfile" + else + fin = io.open(file, "rb") + if not fin then + ok = false + err = "error opening "..file.." for reading" + end + end + while ok do + local buf = fin:read(size_buf) + if not buf then + break + end + ok = self:write_file_in_zip(buf) + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + if fin then + fin:close() + end + if ok then + ok = self:close_file_in_zip() + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + return ok == true, err +end + --- Complete the writing of the zipfile. --- @param zf handle of the zipfile being written. +-- @param self handle of the zipfile being written. -- @return true if succeeded, nil in case of failure. -function write_close(zf) - local zh = zf.ziphandle +local function zipwriter_close(self) + local zh = self.ziphandle local central_directory_offset = zh:seek() local size_of_central_directory = 0 -- Central directory structure - for _, lfh in ipairs(zf.files) do + for _, lfh in ipairs(self.files) do zh:write(number_to_bytestring(0x02014b50, 4)) -- signature zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 @@ -139,7 +163,7 @@ function write_close(zf) zh:write(number_to_bytestring(0, 2)) -- file comment length zh:write(number_to_bytestring(0, 2)) -- disk number start zh:write(number_to_bytestring(0, 2)) -- internal file attributes - zh:write(number_to_bytestring(0, 4)) -- external file attributes + zh:write(number_to_bytestring(lfh.external_attr, 4)) -- external file attributes zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header zh:write(lfh.file_name) size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length @@ -149,8 +173,8 @@ function write_close(zf) zh:write(number_to_bytestring(0x06054b50, 4)) -- signature zh:write(number_to_bytestring(0, 2)) -- number of this disk zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory - zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir on this disk - zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir on this disk + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir zh:write(number_to_bytestring(size_of_central_directory, 4)) zh:write(number_to_bytestring(central_directory_offset, 4)) zh:write(number_to_bytestring(0, 2)) -- zip file comment length @@ -159,40 +183,27 @@ function write_close(zf) return true end --- @return boolean or (boolean, string): true on success, --- false and an error message on failure. -local function add_to_zip(zf, file) - local fin - local ok, err = write_open_new_file_in_zip(zf, file) - if not ok then - err = "error in opening "..file.." in zipfile" - else - fin = io.open(file, "rb") - if not fin then - ok = false - err = "error opening "..file.." for reading" - end - end - while ok do - local buf = fin:read(size_buf) - if not buf then - break - end - ok = write_in_file_in_zip(zf, buf) - if not ok then - err = "error in writing "..file.." in the zipfile" - end - end - if fin then - fin:close() - end - if ok then - ok = write_close_file_in_zip(zf) - if not ok then - err = "error in writing "..file.." in the zipfile" - end +--- Return a zip handle open for writing. +-- @param name filename of the zipfile to be created. +-- @return a zip handle, or nil in case of error. +function new_zipwriter(name) + + local zw = {} + + zw.ziphandle = io.open(name, "w") + if not zw.ziphandle then + return nil end - return ok == true, err + zw.files = {} + zw.in_open_file = false + + zw.add = zipwriter_add + zw.close = zipwriter_close + zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip + zw.write_file_in_zip = zipwriter_write_file_in_zip + zw.close_file_in_zip = zipwriter_close_file_in_zip + + return zw end --- Compress files in a .zip archive. @@ -202,29 +213,31 @@ end -- @return boolean or (boolean, string): true on success, -- false and an error message on failure. function zip(zipfile, ...) - local zf = write_open(filename) - if not zf then - return nil, "error opening "..filename + local zw = new_zipwriter(zipfile) + if not zw then + return nil, "error opening "..zipfile end local ok, err for _, file in pairs({...}) do if fs.is_dir(file) then - for _, file in pairs(fs.find(file)) do - if fs.is_file(file) then - ok, err = add_to_zip(file) + for _, entry in pairs(fs.find(file)) do + local fullname = dir.path(file, entry) + if fs.is_file(fullname) then + ok, err = zw:add(fullname) if not ok then break end end end else - ok, err = add_to_zip(file) + ok, err = zw:add(file) if not ok then break end end end - local ok = write_close(zf) + local ok = zw:close() if not ok then - return false, "error closing "..filename + return false, "error closing "..zipfile end return ok, err end + -- cgit v1.2.3-55-g6feb