include "stdio.s7i";
include "inflate.s7i";
include "deflate.s7i";
include "unicode.s7i";
include "bytedata.s7i";
include "bin32.s7i";
include "time.s7i";
include "crc32.s7i";
include "filesys.s7i";
include "filebits.s7i";
include "fileutil.s7i";
include "archive_base.s7i";
const string: ZIP_CENTRAL_HEADER_SIGNATURE is "PK\1;\2;";
const string: ZIP_LOCAL_HEADER_SIGNATURE is "PK\3;\4;";
const string: ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE is "PK\5;\6;";
const string: ZIP_DATA_DESCRIPTOR_SIGNATURE is "PK\7;\8;";
const string: ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE is "PK\6;\6;";
const string: ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE is "PK\6;\7;";
const integer: ZIP_CENTRAL_HEADER_FIXED_SIZE is 46;
const integer: ZIP_LOCAL_HEADER_FIXED_SIZE is 30;
const integer: ZIP_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE is 22;
const integer: ZIP64_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE is 56;
const integer: ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE is 20;
const bin32: ZIP_HAS_DATA_DESCRIPTOR is bin32(16#0008);
const bin32: ZIP_FILE_NAME_IS_UTF8 is bin32(16#0800);
const integer: ZIP_HOST_SYSTEM_MS_DOS is 0;
const integer: ZIP_HOST_SYSTEM_UNIX is 3;
const integer: DOS_EPOCH is 2#100001;
const func integer: rposOfMagic (inout file: inFile, in string: magic,
in integer: minRecLen, in integer: maxRecLen) is func
result
var integer: posFound is 0;
local
var integer: pos is 0;
var string: data is "";
var integer: magicPos is 0;
begin
pos := length(inFile) - maxRecLen + 1;
if pos <= 0 then
pos := 1;
end if;
seek(inFile, pos);
data := gets(inFile, maxRecLen);
magicPos := rpos(data, magic);
if magicPos <> 0 then
posFound := pos + magicPos - 1;
end if;
end func;
const type: zipExtraFieldType is hash [integer] string;
const func zipExtraFieldType: getExtraFieldMap (in string: field) is func
result
var zipExtraFieldType: extraFieldMap is zipExtraFieldType.value;
local
var integer: pos is 1;
var integer: id is 0;
var integer: length is 0;
var string: value is "";
begin
while pos <= length(field) - 3 do
id := bytes2Int(field[pos fixLen 2], UNSIGNED, LE);
length := bytes2Int(field[pos + 2 fixLen 2], UNSIGNED, LE);
if length <> 0 then
value := field[pos + 4 fixLen length];
else
value := "";
end if;
extraFieldMap @:= [id] value;
pos +:= 4 + length;
end while;
end func;
const func string: extraFieldFromMap (in zipExtraFieldType: extraFieldMap) is func
result
var string: field is "";
local
var integer: id is 0;
var string: value is "";
begin
for id range sort(keys(extraFieldMap)) do
value := extraFieldMap[id];
field &:= bytes(id, UNSIGNED, LE, 2) & bytes(length(value), UNSIGNED, LE, 2) & value;
end for;
end func;
const proc: writeExtraField (in string: field) is func
local
var integer: pos is 1;
var integer: id is 0;
var integer: length is 0;
var string: value is "";
begin
while pos <= length(field) - 3 do
id := bytes2Int(field[pos fixLen 2], UNSIGNED, LE);
length := bytes2Int(field[pos + 2 fixLen 2], UNSIGNED, LE);
if length <> 0 then
value := field[pos + 4 fixLen length];
else
value := "";
end if;
writeln("field: " <& id radix 16 lpad0 4 <& " " <& length <& " " <& literal(value));
pos +:= 4 + length;
end while;
end func;
const type: local_file_header is new struct
var string: signature is "";
var integer: version_needed_to_extract is 0;
var bin32: general_purpose_bit_flag is bin32(0);
var integer: compression_method is 0;
var integer: last_mod_file_time is 0;
var integer: last_mod_file_date is DOS_EPOCH;
var bin32: crc_32 is bin32(0);
var integer: compressed_size is 0;
var integer: uncompressed_size is 0;
var string: file_name is "";
var string: extra_field is "";
var string: filePath is "";
var zipExtraFieldType: extraFieldMap is zipExtraFieldType.value;
end struct;
const proc: write (in local_file_header: header) is func
begin
writeln("signature: " rpad 45 <& literal(header.signature) lpad 16);
writeln("version_needed_to_extract: " rpad 45 <& header.version_needed_to_extract lpad 16);
writeln("general_purpose_bit_flag: " rpad 45 <& header.general_purpose_bit_flag radix 2 lpad0 16);
writeln("compression_method: " rpad 45 <& header.compression_method lpad 16);
writeln("last_mod_file_time: " rpad 45 <& header.last_mod_file_time lpad 16);
writeln("last_mod_file_date: " rpad 45 <& header.last_mod_file_date lpad 16);
writeln("crc_32: " rpad 45 <& header.crc_32 lpad 16);
writeln("compressed_size: " rpad 45 <& header.compressed_size lpad 16);
writeln("uncompressed_size: " rpad 45 <& header.uncompressed_size lpad 16);
writeln("file_name_length: " rpad 45 <& length(header.file_name) lpad 16);
writeln("extra_field_length: " rpad 45 <& length(header.extra_field) lpad 16);
writeln("file_name: " rpad 45 <& literal(header.file_name) lpad 16);
writeln("extra_field: " <& literal(header.extra_field) lpad 16);
writeExtraField(header.extra_field);
end func;
const proc: considerZip64ExtraField (inout local_file_header: header,
in string: zip64ExtraField) is func
local
var integer: pos is 1;
begin
if header.uncompressed_size = 16#ffffffff and length(zip64ExtraField) >= pos + 7 then
header.uncompressed_size := bytes2Int(zip64ExtraField[pos fixLen 8], UNSIGNED, LE);
pos +:= 8;
end if;
if header.compressed_size = 16#ffffffff and length(zip64ExtraField) >= pos + 7 then
header.compressed_size := bytes2Int(zip64ExtraField[pos fixLen 8], UNSIGNED, LE);
pos +:= 8;
end if;
end func;
const func local_file_header: get_local_header (inout file: inFile) is func
result
var local_file_header: header is local_file_header.value;
local
var string: stri is "";
var integer: file_name_length is 0;
var integer: extra_field_length is 0;
begin
stri := gets(inFile, ZIP_LOCAL_HEADER_FIXED_SIZE);
if length(stri) = ZIP_LOCAL_HEADER_FIXED_SIZE and
stri[.. 4] = ZIP_LOCAL_HEADER_SIGNATURE then
header.signature := ZIP_LOCAL_HEADER_SIGNATURE;
header.version_needed_to_extract := bytes2Int(stri[ 5 fixLen 2], UNSIGNED, LE);
header.general_purpose_bit_flag := bin32(bytes2Int(stri[ 7 fixLen 2], UNSIGNED, LE));
header.compression_method := bytes2Int(stri[ 9 fixLen 2], UNSIGNED, LE);
header.last_mod_file_time := bytes2Int(stri[11 fixLen 2], UNSIGNED, LE);
header.last_mod_file_date := bytes2Int(stri[13 fixLen 2], UNSIGNED, LE);
header.crc_32 := bin32(bytes2Int(stri[15 fixLen 4], UNSIGNED, LE));
header.compressed_size := bytes2Int(stri[19 fixLen 4], UNSIGNED, LE);
header.uncompressed_size := bytes2Int(stri[23 fixLen 4], UNSIGNED, LE);
file_name_length := bytes2Int(stri[27 fixLen 2], UNSIGNED, LE);
extra_field_length := bytes2Int(stri[29 fixLen 2], UNSIGNED, LE);
header.file_name := gets(inFile, file_name_length);
header.extra_field := gets(inFile, extra_field_length);
header.extraFieldMap := getExtraFieldMap(header.extra_field);
if header.general_purpose_bit_flag & ZIP_FILE_NAME_IS_UTF8 <> bin32(0) then
block
header.filePath := fromUtf8(header.file_name);
exception
catch RANGE_ERROR:
header.filePath := header.file_name;
end block;
else
header.filePath := header.file_name;
end if;
if header.filePath <> "/" and endsWith(header.filePath, "/") then
header.filePath := header.filePath[.. pred(length(header.filePath))];
end if;
if 16#0001 in header.extraFieldMap then
considerZip64ExtraField(header, header.extraFieldMap[16#0001]);
end if;
end if;
end func;
const func string: str (in local_file_header: header) is func
result
var string: stri is "";
begin
stri := ZIP_LOCAL_HEADER_SIGNATURE &
bytes( header.version_needed_to_extract, UNSIGNED, LE, 2) &
bytes(ord( header.general_purpose_bit_flag), UNSIGNED, LE, 2) &
bytes( header.compression_method, UNSIGNED, LE, 2) &
bytes( header.last_mod_file_time, UNSIGNED, LE, 2) &
bytes( header.last_mod_file_date, UNSIGNED, LE, 2) &
bytes(ord( header.crc_32), UNSIGNED, LE, 4) &
bytes( header.compressed_size, UNSIGNED, LE, 4) &
bytes( header.uncompressed_size, UNSIGNED, LE, 4) &
bytes(length(header.file_name), UNSIGNED, LE, 2) &
bytes(length(header.extra_field), UNSIGNED, LE, 2) &
header.file_name &
header.extra_field;
end func;
const proc: writeHead (inout file: outFile, in local_file_header: header) is func
begin
write(outFile, str(header));
end func;
const type: zip64_end_of_central_dir_locator is new struct
var string: signature is "";
var integer: disk_number_with_zip64_end_of_central_directory is 0;
var integer: offset_of_zip64_end_of_central_directory is 0;
var integer: total_number_of_disks is 0;
end struct;
const proc: write (in zip64_end_of_central_dir_locator: endOfCentDirLocator) is func
begin
writeln("signature: " rpad 50 <& literal(endOfCentDirLocator.signature) lpad 16);
writeln("disk_number_with_zip64_end_of_central_directory: " rpad 50 <& endOfCentDirLocator.disk_number_with_zip64_end_of_central_directory);
writeln("offset_of_zip64_end_of_central_directory: " rpad 50 <& endOfCentDirLocator.offset_of_zip64_end_of_central_directory);
writeln("total_number_of_disks: " rpad 50 <& endOfCentDirLocator.total_number_of_disks);
end func;
const func zip64_end_of_central_dir_locator: get_zip64_end_of_central_dir_locator (inout file: inFile) is func
result
var zip64_end_of_central_dir_locator: endOfCentDirLocator is zip64_end_of_central_dir_locator.value;
local
var string: stri is "";
begin
stri := gets(inFile, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE);
if length(stri) = ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE and
stri[.. 4] = ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE then
endOfCentDirLocator.signature := ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE;
endOfCentDirLocator.disk_number_with_zip64_end_of_central_directory := bytes2Int(stri[ 5 fixLen 4], UNSIGNED, LE);
endOfCentDirLocator.offset_of_zip64_end_of_central_directory := bytes2Int(stri[ 9 fixLen 8], UNSIGNED, LE);
endOfCentDirLocator.total_number_of_disks := bytes2Int(stri[17 fixLen 4], UNSIGNED, LE);
end if;
end func;
const type: zip64_end_of_central_directory is new struct
var string: signature is "";
var integer: size_of_eocd64_minus_12 is 0;
var integer: version_made_by is 0;
var integer: version_needed_to_extract is 0;
var integer: number_of_this_disk is 0;
var integer: disk_number_with_start_of_central_directory is 0;
var integer: entries_in_central_directory_on_this_disk is 0;
var integer: entries_in_central_directory is 0;
var integer: size_of_central_directory is 0;
var integer: offset_of_start_of_central_directory is 0;
var string: comment is "";
end struct;
const proc: write (in zip64_end_of_central_directory: endOfCentDir) is func
begin
writeln("signature: " rpad 45 <& literal(endOfCentDir.signature) lpad 16);
writeln("size_of_eocd64_minus_12: " rpad 45 <& endOfCentDir.size_of_eocd64_minus_12 lpad 16);
writeln("version_made_by: " rpad 45 <& endOfCentDir.version_made_by lpad 16);
writeln("version_needed_to_extract: " rpad 45 <& endOfCentDir.version_needed_to_extract lpad 16);
writeln("number_of_this_disk: " rpad 45 <& endOfCentDir.number_of_this_disk lpad 16);
writeln("disk_number_with_start_of_central_directory: " rpad 45 <& endOfCentDir.disk_number_with_start_of_central_directory lpad 16);
writeln("entries_in_central_directory_on_this_disk: " rpad 45 <& endOfCentDir.entries_in_central_directory_on_this_disk lpad 16);
writeln("entries_in_central_directory: " rpad 45 <& endOfCentDir.entries_in_central_directory lpad 16);
writeln("size_of_central_directory: " rpad 45 <& endOfCentDir.size_of_central_directory lpad 16);
writeln("offset_of_start_of_central_directory: " rpad 45 <& endOfCentDir.offset_of_start_of_central_directory lpad 16);
writeln("comment: " rpad 45 <& literal(endOfCentDir.comment) lpad 16);
end func;
const func zip64_end_of_central_directory: get_zip64_end_of_central_directory (inout file: inFile) is func
result
var zip64_end_of_central_directory: endOfCentDir is zip64_end_of_central_directory.value;
local
var string: stri is "";
var integer: commentLength is 0;
begin
stri := gets(inFile, ZIP64_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE);
if length(stri) = ZIP64_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE and
stri[.. 4] = ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE then
endOfCentDir.signature := ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE;
endOfCentDir.size_of_eocd64_minus_12 := bytes2Int(stri[ 5 fixLen 8], UNSIGNED, LE);
endOfCentDir.version_made_by := bytes2Int(stri[13 fixLen 2], UNSIGNED, LE);
endOfCentDir.version_needed_to_extract := bytes2Int(stri[15 fixLen 2], UNSIGNED, LE);
endOfCentDir.number_of_this_disk := bytes2Int(stri[17 fixLen 4], UNSIGNED, LE);
endOfCentDir.disk_number_with_start_of_central_directory := bytes2Int(stri[21 fixLen 4], UNSIGNED, LE);
endOfCentDir.entries_in_central_directory_on_this_disk := bytes2Int(stri[25 fixLen 8], UNSIGNED, LE);
endOfCentDir.entries_in_central_directory := bytes2Int(stri[33 fixLen 8], UNSIGNED, LE);
endOfCentDir.size_of_central_directory := bytes2Int(stri[41 fixLen 8], UNSIGNED, LE);
endOfCentDir.offset_of_start_of_central_directory := bytes2Int(stri[49 fixLen 8], UNSIGNED, LE);
commentLength := endOfCentDir.size_of_eocd64_minus_12 + 12 - ZIP64_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE;
endOfCentDir.comment := gets(inFile, commentLength);
end if;
end func;
const type: end_of_central_directory is new struct
var string: signature is "";
var integer: number_of_this_disk is 0;
var integer: disk_number_with_start_of_central_directory is 0;
var integer: entries_in_central_directory_on_this_disk is 0;
var integer: entries_in_central_directory is 0;
var integer: size_of_central_directory is 0;
var integer: offset_of_start_of_central_directory is 0;
var string: file_comment is "";
end struct;
const proc: write (in end_of_central_directory: endOfCentDir) is func
begin
writeln("signature: " rpad 45 <& literal(endOfCentDir.signature) lpad 16);
writeln("number_of_this_disk: " rpad 45 <& endOfCentDir.number_of_this_disk lpad 16);
writeln("disk_number_with_start_of_central_directory: " rpad 45 <& endOfCentDir.disk_number_with_start_of_central_directory lpad 16);
writeln("entries_in_central_directory_on_this_disk: " rpad 45 <& endOfCentDir.entries_in_central_directory_on_this_disk lpad 16);
writeln("entries_in_central_directory: " rpad 45 <& endOfCentDir.entries_in_central_directory lpad 16);
writeln("size_of_central_directory: " rpad 45 <& endOfCentDir.size_of_central_directory lpad 16);
writeln("offset_of_start_of_central_directory: " rpad 45 <& endOfCentDir.offset_of_start_of_central_directory lpad 16);
writeln("file_comment_length: " rpad 45 <& length(endOfCentDir.file_comment) lpad 16);
writeln("file_comment: " rpad 45 <& literal(endOfCentDir.file_comment) lpad 16);
end func;
const func end_of_central_directory: get_end_of_central_directory (inout file: inFile) is func
result
var end_of_central_directory: endOfCentDir is end_of_central_directory.value;
local
var string: stri is "";
var integer: file_comment_length is 0;
begin
stri := gets(inFile, ZIP_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE);
if length(stri) = ZIP_END_OF_CENTRAL_DIRECTORY_FIXED_SIZE and
stri[.. 4] = ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE then
endOfCentDir.signature := ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE;
endOfCentDir.number_of_this_disk := bytes2Int(stri[ 5 fixLen 2], UNSIGNED, LE);
endOfCentDir.disk_number_with_start_of_central_directory := bytes2Int(stri[ 7 fixLen 2], UNSIGNED, LE);
endOfCentDir.entries_in_central_directory_on_this_disk := bytes2Int(stri[ 9 fixLen 2], UNSIGNED, LE);
endOfCentDir.entries_in_central_directory := bytes2Int(stri[11 fixLen 2], UNSIGNED, LE);
endOfCentDir.size_of_central_directory := bytes2Int(stri[13 fixLen 4], UNSIGNED, LE);
endOfCentDir.offset_of_start_of_central_directory := bytes2Int(stri[17 fixLen 4], UNSIGNED, LE);
file_comment_length := bytes2Int(stri[21 fixLen 2], UNSIGNED, LE);
endOfCentDir.file_comment := gets(inFile, file_comment_length);
end if;
end func;
const func string: str (in end_of_central_directory: endOfCentDir) is func
result
var string: stri is "";
begin
stri := ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE &
bytes( endOfCentDir.number_of_this_disk, UNSIGNED, LE, 2) &
bytes( endOfCentDir.disk_number_with_start_of_central_directory, UNSIGNED, LE, 2) &
bytes( endOfCentDir.entries_in_central_directory_on_this_disk, UNSIGNED, LE, 2) &
bytes( endOfCentDir.entries_in_central_directory, UNSIGNED, LE, 2) &
bytes( endOfCentDir.size_of_central_directory, UNSIGNED, LE, 4) &
bytes( endOfCentDir.offset_of_start_of_central_directory, UNSIGNED, LE, 4) &
bytes(length(endOfCentDir.file_comment), UNSIGNED, LE, 2) &
endOfCentDir.file_comment;
end func;
const func end_of_central_directory: readEndOfCentralDir (inout file: inFile,
inout integer: endOfCentralDirPos) is func
result
var end_of_central_directory: endOfCentralDir is end_of_central_directory.value;
local
const integer: MIN_RECORD_LEN is 22;
const integer: MAX_RECORD_LEN is MIN_RECORD_LEN + 2**16 - 1;
var zip64_end_of_central_dir_locator: endOfCentDirLocator is zip64_end_of_central_dir_locator.value;
var zip64_end_of_central_directory: endOfCentralDir64 is zip64_end_of_central_directory.value;
begin
endOfCentralDirPos := rposOfMagic(inFile, ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE,
MIN_RECORD_LEN, MAX_RECORD_LEN);
if endOfCentralDirPos <> 0 then
if endOfCentralDirPos > ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE then
seek(inFile, endOfCentralDirPos - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE);
endOfCentDirLocator := get_zip64_end_of_central_dir_locator(inFile);
if endOfCentDirLocator.signature = ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE then
seek(inFile, succ(endOfCentDirLocator.offset_of_zip64_end_of_central_directory));
endOfCentralDir64 := get_zip64_end_of_central_directory(inFile);
end if;
end if;
seek(inFile, endOfCentralDirPos);
endOfCentralDir := get_end_of_central_directory(inFile);
if endOfCentDirLocator.signature = ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE then
if endOfCentralDir64.signature = ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE then
if endOfCentralDir.number_of_this_disk = 16#ffff then
endOfCentralDir.number_of_this_disk := endOfCentralDir64.number_of_this_disk;
end if;
if endOfCentralDir.disk_number_with_start_of_central_directory = 16#ffff then
endOfCentralDir.disk_number_with_start_of_central_directory :=
endOfCentralDir64.disk_number_with_start_of_central_directory;
end if;
if endOfCentralDir.entries_in_central_directory_on_this_disk = 16#ffff then
endOfCentralDir.entries_in_central_directory_on_this_disk :=
endOfCentralDir64.entries_in_central_directory_on_this_disk;
end if;
if endOfCentralDir.entries_in_central_directory = 16#ffff then
endOfCentralDir.entries_in_central_directory := endOfCentralDir64.entries_in_central_directory;
end if;
if endOfCentralDir.size_of_central_directory = 16#ffffffff then
endOfCentralDir.size_of_central_directory := endOfCentralDir64.size_of_central_directory;
end if;
if endOfCentralDir.offset_of_start_of_central_directory = 16#ffffffff then
endOfCentralDir.offset_of_start_of_central_directory :=
endOfCentralDir64.offset_of_start_of_central_directory;
end if;
end if;
end if;
if tell(inFile) <> succ(length(inFile)) then
endOfCentralDir := end_of_central_directory.value;
end if;
end if;
end func;
const type: central_file_header is new struct
var string: signature is "";
var integer: version_made_by is 0;
var integer: version_needed_to_extract is 0;
var bin32: general_purpose_bit_flag is bin32(0);
var integer: compression_method is 0;
var integer: last_mod_file_time is 0;
var integer: last_mod_file_date is DOS_EPOCH;
var bin32: crc_32 is bin32(0);
var integer: compressed_size is 0;
var integer: uncompressed_size is 0;
var integer: disk_number_start is 0;
var integer: internal_file_attributes is 0;
var integer: external_file_attributes is 0;
var integer: relative_offset_of_local_header is 0;
var string: file_name is "";
var string: extra_field is "";
var string: file_comment is "";
var string: filePath is "";
var zipExtraFieldType: extraFieldMap is zipExtraFieldType.value;
end struct;
const proc: write (in central_file_header: header) is func
begin
writeln("signature: " rpad 45 <& literal(header.signature) lpad 16);
writeln("version_made_by: " rpad 45 <& (header.version_made_by radix 16 lpad0 4) lpad 16);
writeln("version_needed_to_extract: " rpad 45 <& header.version_needed_to_extract lpad 16);
writeln("general_purpose_bit_flag: " rpad 45 <& header.general_purpose_bit_flag radix 2 lpad0 16);
writeln("compression_method: " rpad 45 <& header.compression_method lpad 16);
writeln("last_mod_file_time: " rpad 45 <& header.last_mod_file_time lpad 16);
writeln("last_mod_file_date: " rpad 45 <& header.last_mod_file_date lpad 16);
writeln("crc_32: " rpad 45 <& header.crc_32 lpad 16);
writeln("compressed_size: " rpad 45 <& header.compressed_size lpad 16);
writeln("uncompressed_size: " rpad 45 <& header.uncompressed_size lpad 16);
writeln("file_name_length: " rpad 45 <& length(header.file_name) lpad 16);
writeln("extra_field_length: " rpad 45 <& length(header.extra_field) lpad 16);
writeln("file_comment_length: " rpad 45 <& length(header.file_comment) lpad 16);
writeln("disk_number_start: " rpad 45 <& header.disk_number_start lpad 16);
writeln("internal_file_attributes: " rpad 45 <& header.internal_file_attributes lpad 16);
writeln("external_file_attributes: " rpad 45 <& header.external_file_attributes radix 16 lpad 16);
writeln("relative_offset_of_local_header: " rpad 45 <& header.relative_offset_of_local_header lpad 16);
writeln("file_name: " rpad 45 <& literal(header.file_name) lpad 16);
writeln("extra_field: " rpad 45 <& literal(header.extra_field) lpad 16);
writeExtraField(header.extra_field);
writeln("file_comment: " rpad 45 <& literal(header.file_comment) lpad 16);
writeln("filePath: " rpad 45 <& header.filePath);
end func;
const proc: considerZip64ExtraField (inout central_file_header: header,
in string: zip64ExtraField) is func
local
var integer: pos is 1;
begin
if header.uncompressed_size = 16#ffffffff and length(zip64ExtraField) >= pos + 7 then
header.uncompressed_size := bytes2Int(zip64ExtraField[pos fixLen 8], UNSIGNED, LE);
pos +:= 8;
end if;
if header.compressed_size = 16#ffffffff and length(zip64ExtraField) >= pos + 7 then
header.compressed_size := bytes2Int(zip64ExtraField[pos fixLen 8], UNSIGNED, LE);
pos +:= 8;
end if;
if header.relative_offset_of_local_header = 16#ffffffff and length(zip64ExtraField) >= pos + 7 then
header.relative_offset_of_local_header := bytes2Int(zip64ExtraField[pos fixLen 8], UNSIGNED, LE);
pos +:= 8;
end if;
if header.disk_number_start = 16#ffff and length(zip64ExtraField) >= pos + 3 then
header.disk_number_start := bytes2Int(zip64ExtraField[pos fixLen 4], UNSIGNED, LE);
pos +:= 4;
end if;
end func;
const func central_file_header: get_central_header (inout file: inFile) is func
result
var central_file_header: header is central_file_header.value;
local
var string: stri is "";
var integer: file_name_length is 0;
var integer: extra_field_length is 0;
var integer: file_comment_length is 0;
begin
stri := gets(inFile, ZIP_CENTRAL_HEADER_FIXED_SIZE);
if length(stri) = ZIP_CENTRAL_HEADER_FIXED_SIZE and
stri[.. 4] = ZIP_CENTRAL_HEADER_SIGNATURE then
header.signature := ZIP_CENTRAL_HEADER_SIGNATURE;
header.version_made_by := bytes2Int(stri[ 5 fixLen 2], UNSIGNED, LE);
header.version_needed_to_extract := bytes2Int(stri[ 7 fixLen 2], UNSIGNED, LE);
header.general_purpose_bit_flag := bin32(bytes2Int(stri[ 9 fixLen 2], UNSIGNED, LE));
header.compression_method := bytes2Int(stri[11 fixLen 2], UNSIGNED, LE);
header.last_mod_file_time := bytes2Int(stri[13 fixLen 2], UNSIGNED, LE);
header.last_mod_file_date := bytes2Int(stri[15 fixLen 2], UNSIGNED, LE);
header.crc_32 := bin32(bytes2Int(stri[17 fixLen 4], UNSIGNED, LE));
header.compressed_size := bytes2Int(stri[21 fixLen 4], UNSIGNED, LE);
header.uncompressed_size := bytes2Int(stri[25 fixLen 4], UNSIGNED, LE);
file_name_length := bytes2Int(stri[29 fixLen 2], UNSIGNED, LE);
extra_field_length := bytes2Int(stri[31 fixLen 2], UNSIGNED, LE);
file_comment_length := bytes2Int(stri[33 fixLen 2], UNSIGNED, LE);
header.disk_number_start := bytes2Int(stri[35 fixLen 2], UNSIGNED, LE);
header.internal_file_attributes := bytes2Int(stri[37 fixLen 2], UNSIGNED, LE);
header.external_file_attributes := bytes2Int(stri[39 fixLen 4], UNSIGNED, LE);
header.relative_offset_of_local_header := bytes2Int(stri[43 fixLen 4], UNSIGNED, LE);
header.file_name := gets(inFile, file_name_length);
header.extra_field := gets(inFile, extra_field_length);
header.file_comment := gets(inFile, file_comment_length);
header.extraFieldMap := getExtraFieldMap(header.extra_field);
if header.general_purpose_bit_flag & ZIP_FILE_NAME_IS_UTF8 <> bin32(0) then
block
header.filePath:= fromUtf8(header.file_name);
exception
catch RANGE_ERROR:
header.filePath := header.file_name;
end block;
else
header.filePath := header.file_name;
end if;
if header.filePath <> "/" and endsWith(header.filePath, "/") then
header.filePath := header.filePath[.. pred(length(header.filePath))];
end if;
if 16#0001 in header.extraFieldMap then
considerZip64ExtraField(header, header.extraFieldMap[16#0001]);
end if;
end if;
end func;
const func string: str (in central_file_header: header) is func
result
var string: stri is "";
begin
stri := ZIP_CENTRAL_HEADER_SIGNATURE &
bytes( header.version_made_by, UNSIGNED, LE, 2) &
bytes( header.version_needed_to_extract, UNSIGNED, LE, 2) &
bytes( ord(header.general_purpose_bit_flag), UNSIGNED, LE, 2) &
bytes( header.compression_method, UNSIGNED, LE, 2) &
bytes( header.last_mod_file_time, UNSIGNED, LE, 2) &
bytes( header.last_mod_file_date, UNSIGNED, LE, 2) &
bytes( ord(header.crc_32), UNSIGNED, LE, 4) &
bytes( header.compressed_size, UNSIGNED, LE, 4) &
bytes( header.uncompressed_size, UNSIGNED, LE, 4) &
bytes(length(header.file_name), UNSIGNED, LE, 2) &
bytes(length(header.extra_field), UNSIGNED, LE, 2) &
bytes(length(header.file_comment), UNSIGNED, LE, 2) &
bytes( header.disk_number_start, UNSIGNED, LE, 2) &
bytes( header.internal_file_attributes, UNSIGNED, LE, 2) &
bytes( header.external_file_attributes, UNSIGNED, LE, 4) &
bytes( header.relative_offset_of_local_header, UNSIGNED, LE, 4) &
header.file_name &
header.extra_field &
header.file_comment;
end func;
const proc: writeHead (inout file: outFile, in central_file_header: header) is func
begin
write(outFile, str(header));
end func;
const func string: getCentralHeaderFilePath (inout file: inFile) is func
result
var string: filePath is "";
local
var string: stri is "";
var bin32: general_purpose_bit_flag is bin32(0);
var integer: file_name_length is 0;
var integer: extra_field_length is 0;
var integer: file_comment_length is 0;
begin
stri := gets(inFile, ZIP_CENTRAL_HEADER_FIXED_SIZE);
if length(stri) = ZIP_CENTRAL_HEADER_FIXED_SIZE and
stri[.. 4] = ZIP_CENTRAL_HEADER_SIGNATURE then
general_purpose_bit_flag := bin32(bytes2Int(stri[ 9 fixLen 2], UNSIGNED, LE));
file_name_length := bytes2Int(stri[29 fixLen 2], UNSIGNED, LE);
extra_field_length := bytes2Int(stri[31 fixLen 2], UNSIGNED, LE);
file_comment_length := bytes2Int(stri[33 fixLen 2], UNSIGNED, LE);
filePath := gets(inFile, file_name_length);
ignore(gets(inFile, extra_field_length + file_comment_length));
if general_purpose_bit_flag & ZIP_FILE_NAME_IS_UTF8 <> bin32(0) then
block
filePath := fromUtf8(filePath);
exception
catch RANGE_ERROR: noop;
end block;
end if;
if filePath <> "/" and endsWith(filePath, "/") then
filePath := filePath[.. pred(length(filePath))];
end if;
end if;
end func;
const func local_file_header: toLocalHeader (in central_file_header: header) is func
result
var local_file_header: localHeader is local_file_header.value;
begin
localHeader.signature := ZIP_LOCAL_HEADER_SIGNATURE;
localHeader.version_needed_to_extract := header.version_needed_to_extract;
localHeader.general_purpose_bit_flag := header.general_purpose_bit_flag;
localHeader.compression_method := header.compression_method;
localHeader.last_mod_file_time := header.last_mod_file_time;
localHeader.last_mod_file_date := header.last_mod_file_date;
localHeader.crc_32 := header.crc_32;
localHeader.compressed_size := header.compressed_size;
localHeader.uncompressed_size := header.uncompressed_size;
localHeader.file_name := header.file_name;
localHeader.extra_field := header.extra_field;
end func;
const proc: updateLocalHeader (inout local_file_header: localHeader,
in central_file_header: header) is func
begin
localHeader.compression_method := header.compression_method;
localHeader.last_mod_file_time := header.last_mod_file_time;
localHeader.last_mod_file_date := header.last_mod_file_date;
localHeader.crc_32 := header.crc_32;
localHeader.compressed_size := header.compressed_size;
localHeader.uncompressed_size := header.uncompressed_size;
end func;
const proc: initLastModFileTime (inout central_file_header: header,
in time: modificationTime) is func
local
var integer: timestamp is 0;
var string: unixExtraField is "";
begin
timestamp := timestamp1970(modificationTime);
unixExtraField :=
bytes(timestamp, UNSIGNED, LE, 4) mult 2 &
bytes( 0, UNSIGNED, LE, 2) mult 2;
header.extraFieldMap @:= [16#000d] unixExtraField;
header.extra_field := extraFieldFromMap(header.extraFieldMap);
header.last_mod_file_time := (modificationTime.hour << 11) +
(modificationTime.minute << 5) +
(modificationTime.second >> 1);
header.last_mod_file_date := ((modificationTime.year - 1980) << 9) +
(modificationTime.month << 5) +
modificationTime.day;
end func;
const proc: assignLastModFileTime (inout local_file_header: localHeader,
in time: modificationTime) is func
local
var integer: timestamp is 0;
begin
if 16#5455 in localHeader.extraFieldMap then
timestamp := timestamp1970(modificationTime);
localHeader.extraFieldMap[16#5455] @:= [2] bytes(timestamp, UNSIGNED, LE, 4);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#000d in localHeader.extraFieldMap then
timestamp := timestamp1970(modificationTime);
localHeader.extraFieldMap[16#000d] @:= [1] bytes(timestamp, UNSIGNED, LE, 4) mult 2;
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#000a in localHeader.extraFieldMap then
timestamp := timestamp1601(modificationTime);
localHeader.extraFieldMap[16#000a] @:= [9] bytes(timestamp, UNSIGNED, LE, 8);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
end if;
localHeader.last_mod_file_time := (modificationTime.hour << 11) +
(modificationTime.minute << 5) +
(modificationTime.second >> 1);
localHeader.last_mod_file_date := ((modificationTime.year - 1980) << 9) +
(modificationTime.month << 5) +
modificationTime.day;
end func;
const proc: assignLastModFileTime (inout central_file_header: header,
in time: modificationTime) is func
local
var integer: timestamp is 0;
begin
if 16#5455 in header.extraFieldMap then
timestamp := timestamp1970(modificationTime);
header.extraFieldMap[16#5455] @:= [2] bytes(timestamp, UNSIGNED, LE, 4);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#000d in header.extraFieldMap then
timestamp := timestamp1970(modificationTime);
header.extraFieldMap[16#000d] @:= [1] bytes(timestamp, UNSIGNED, LE, 4) mult 2;
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#000a in header.extraFieldMap then
timestamp := timestamp1601(modificationTime);
header.extraFieldMap[16#000a] @:= [9] bytes(timestamp, UNSIGNED, LE, 8);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
end if;
header.last_mod_file_time := (modificationTime.hour << 11) +
(modificationTime.minute << 5) +
(modificationTime.second >> 1);
header.last_mod_file_date := ((modificationTime.year - 1980) << 9) +
(modificationTime.month << 5) +
modificationTime.day;
end func;
const proc: assignUserId (inout local_file_header: localHeader,
in integer: uid) is func
local
var integer: size is 0;
begin
if 16#000d in localHeader.extraFieldMap then
localHeader.extraFieldMap[16#000d] @:= [9] bytes(uid, UNSIGNED, LE, 2);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#756e in localHeader.extraFieldMap then
localHeader.extraFieldMap[16#756e] @:= [11] bytes(uid, UNSIGNED, LE, 2);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#7875 in localHeader.extraFieldMap then
size := ord(localHeader.extraFieldMap[16#7875][2]);
localHeader.extraFieldMap[16#7875] @:= [3] bytes(uid, UNSIGNED, LE, size);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
end if;
end func;
const proc: assignUserId (inout central_file_header: header,
in integer: uid) is func
local
var integer: size is 0;
begin
if 16#000d in header.extraFieldMap then
header.extraFieldMap[16#000d] @:= [9] bytes(uid, UNSIGNED, LE, 2);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#756e in header.extraFieldMap then
header.extraFieldMap[16#756e] @:= [11] bytes(uid, UNSIGNED, LE, 2);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#7875 in header.extraFieldMap then
size := ord(header.extraFieldMap[16#7875][2]);
header.extraFieldMap[16#7875] @:= [3] bytes(uid, UNSIGNED, LE, size);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
end if;
end func;
const proc: assignGroupId (inout local_file_header: localHeader,
in integer: gid) is func
local
var integer: pos is 0;
var integer: size is 0;
begin
if 16#000d in localHeader.extraFieldMap then
localHeader.extraFieldMap[16#000d] @:= [11] bytes(gid, UNSIGNED, LE, 2);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#756e in localHeader.extraFieldMap then
localHeader.extraFieldMap[16#756e] @:= [13] bytes(gid, UNSIGNED, LE, 2);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
elsif 16#7875 in localHeader.extraFieldMap then
pos := 3 + ord(localHeader.extraFieldMap[16#7875][2]);
size := ord(localHeader.extraFieldMap[16#7875][pos]);
localHeader.extraFieldMap[16#7875] @:= [succ(pos)] bytes(gid, UNSIGNED, LE, size);
localHeader.extra_field := extraFieldFromMap(localHeader.extraFieldMap);
end if;
end func;
const proc: assignGroupId (inout central_file_header: header,
in integer: gid) is func
local
var integer: pos is 0;
var integer: size is 0;
begin
if 16#000d in header.extraFieldMap then
header.extraFieldMap[16#000d] @:= [11] bytes(gid, UNSIGNED, LE, 2);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#756e in header.extraFieldMap then
header.extraFieldMap[16#756e] @:= [13] bytes(gid, UNSIGNED, LE, 2);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
elsif 16#7875 in header.extraFieldMap then
pos := 3 + ord(header.extraFieldMap[16#7875][2]);
size := ord(header.extraFieldMap[16#7875][pos]);
header.extraFieldMap[16#7875] @:= [succ(pos)] bytes(gid, UNSIGNED, LE, size);
header.extra_field := extraFieldFromMap(header.extraFieldMap);
end if;
end func;
const type: zipCatalogType is hash [string] central_file_header;
const type: zipArchive is sub emptyFileSys struct
var file: zipFile is STD_NULL;
var archiveRegisterType: register is archiveRegisterType.value;
var zipCatalogType: catalog is zipCatalogType.value;
var end_of_central_directory: endOfCentralDir is end_of_central_directory.value;
var integer: endOfCentralDirPos is 1;
var integer: startOfCentralDirPos is 1;
end struct;
const func fileSys: openZip (inout file: zipFile) is func
result
var fileSys: newFileSys is fileSys.value;
local
var end_of_central_directory: endOfCentralDir is end_of_central_directory.value;
var integer: endOfCentralDirPos is 0;
var integer: centralHeaderPos is 0;
var string: filePath is "";
var zipArchive: zip is zipArchive.value;
begin
if length(zipFile) = 0 then
zip.zipFile := zipFile;
zip.endOfCentralDir.signature := ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE;
newFileSys := toInterface(zip);
else
endOfCentralDir := readEndOfCentralDir(zipFile, endOfCentralDirPos);
if endOfCentralDir.signature = ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE then
zip.zipFile := zipFile;
zip.endOfCentralDir := endOfCentralDir;
zip.endOfCentralDirPos := endOfCentralDirPos;
zip.startOfCentralDirPos := succ(endOfCentralDir.offset_of_start_of_central_directory);
centralHeaderPos := zip.startOfCentralDirPos;
seek(zip.zipFile, centralHeaderPos);
filePath := getCentralHeaderFilePath(zip.zipFile);
while filePath <> "" do
zip.register @:= [filePath] centralHeaderPos;
centralHeaderPos := tell(zip.zipFile);
filePath := getCentralHeaderFilePath(zip.zipFile);
end while;
newFileSys := toInterface(zip);
end if;
end if;
end func;
const func fileSys: openZip (in string: zipFileName) is func
result
var fileSys: zip is fileSys.value;
local
var file: zipFile is STD_NULL;
begin
zipFile := open(zipFileName, "r");
zip := openZip(zipFile);
end func;
const proc: close (inout zipArchive: zip) is func
begin
zip := zipArchive.value;
end func;
const func central_file_header: addToCatalog (inout zipArchive: zip, in string: filePath) is func
result
var central_file_header: header is central_file_header.value;
begin
seek(zip.zipFile, zip.register[filePath]);
header := get_central_header(zip.zipFile);
zip.catalog @:= [filePath] header;
end func;
const func central_file_header: addImplicitDir (inout zipArchive: zip,
in string: dirPath) is func
result
var central_file_header: header is central_file_header.value;
begin
header.file_name := dirPath & "/";
header.filePath := dirPath;
header.relative_offset_of_local_header := -1;
zip.catalog @:= [dirPath] header;
end func;
const func boolean: isRegularFile (in central_file_header: header) is func
result
var boolean: isRegularFile is FALSE;
begin
if header.relative_offset_of_local_header <> -1 then
if header.version_made_by >> 8 = ZIP_HOST_SYSTEM_UNIX then
isRegularFile := bin32(header.external_file_attributes >> 16) &
MODE_FILE_TYPE_MASK = MODE_FILE_REGULAR;
else
isRegularFile := not endsWith(header.file_name, "/");
end if;
end if;
end func;
const func boolean: isSymlink (in central_file_header: header) is
return header.version_made_by >> 8 = ZIP_HOST_SYSTEM_UNIX and
bin32(header.external_file_attributes >> 16) &
MODE_FILE_TYPE_MASK = MODE_FILE_SYMLINK;
const func string: followSymlink (inout zipArchive: zip, in var string: filePath,
inout central_file_header: header) is func
result
var string: missingPath is "";
local
var integer: symlinkCount is MAX_SYMLINK_CHAIN_LENGTH;
var boolean: isSymlink is TRUE;
var local_file_header: localHeader is local_file_header.value;
var string: targetPath is "";
begin
repeat
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
elsif implicitDir(zip.register, filePath) then
header := addImplicitDir(zip, filePath);
else
missingPath := filePath;
isSymlink := FALSE;
end if;
if missingPath = "" then
if isSymlink(header) then
decr(symlinkCount);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
if localHeader.compression_method = 0 then
targetPath := gets(zip.zipFile, localHeader.compressed_size);
filePath := symlinkDestination(filePath, targetPath);
else
raise FILE_ERROR;
end if;
else
isSymlink := FALSE;
end if;
end if;
until not isSymlink or symlinkCount < 0;
if isSymlink then
raise FILE_ERROR;
end if;
end func;
const func central_file_header: followSymlink (inout zipArchive: zip, in var string: filePath) is func
result
var central_file_header: header is central_file_header.value;
local
var string: missingPath is "";
begin
missingPath := followSymlink(zip, filePath, header);
if missingPath <> "" then
raise FILE_ERROR;
end if;
end func;
const proc: fixRegisterAndCatalog (inout zipArchive: zip, in integer: insertPos,
in integer: numChars) is func
local
var integer: headerPos is 1;
var string: filePath is "";
begin
for key filePath range zip.register do
if zip.register[filePath] >= insertPos then
zip.register[filePath] +:= numChars;
end if;
end for;
for key filePath range zip.catalog do
if succ(zip.catalog[filePath].relative_offset_of_local_header) >= insertPos then
zip.catalog[filePath].relative_offset_of_local_header +:= numChars;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, zip.catalog[filePath]);
end if;
end for;
if zip.startOfCentralDirPos >= insertPos then
zip.startOfCentralDirPos +:= numChars;
end if;
if zip.endOfCentralDirPos >= insertPos then
zip.endOfCentralDirPos +:= numChars;
end if;
end func;
const func array string: readDir (inout zipArchive: zip, in var string: dirPath) is
return readDir(zip.register, dirPath);
const func array string: readDir (inout zipArchive: zip, RECURSIVE) is
return sort(keys(zip.register));
const func fileType: fileType (inout zipArchive: zip, in var string: filePath) is func
result
var fileType: aFileType is FILE_UNKNOWN;
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var integer: symlinkCount is MAX_SYMLINK_CHAIN_LENGTH;
var boolean: isSymlink is FALSE;
var string: targetPath is "";
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
aFileType := FILE_DIR;
else
repeat
isSymlink := FALSE;
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
elsif implicitDir(zip.register, filePath) then
header := addImplicitDir(zip, filePath);
else
aFileType := FILE_ABSENT;
end if;
if aFileType = FILE_UNKNOWN then
case header.version_made_by >> 8 of
when {ZIP_HOST_SYSTEM_UNIX}:
case bin32(header.external_file_attributes >> 16) & MODE_FILE_TYPE_MASK of
when {MODE_FILE_REGULAR}: aFileType := FILE_REGULAR;
when {MODE_FILE_DIR}: aFileType := FILE_DIR;
when {MODE_FILE_CHAR}: aFileType := FILE_CHAR;
when {MODE_FILE_BLOCK}: aFileType := FILE_BLOCK;
when {MODE_FILE_FIFO}: aFileType := FILE_FIFO;
when {MODE_FILE_SOCKET}: aFileType := FILE_SOCKET;
when {MODE_FILE_SYMLINK}:
isSymlink := TRUE;
decr(symlinkCount);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
if localHeader.compression_method = 0 then
targetPath := gets(zip.zipFile, localHeader.compressed_size);
filePath := symlinkDestination(filePath, targetPath);
else
raise FILE_ERROR;
end if;
otherwise: aFileType := FILE_UNKNOWN;
end case;
otherwise:
if endsWith(header.file_name, "/") then
aFileType := FILE_DIR;
else
aFileType := FILE_REGULAR;
end if;
end case;
end if;
until not isSymlink or symlinkCount < 0;
if isSymlink then
aFileType := FILE_SYMLINK;
end if;
end if;
end func;
const func fileType: fileTypeSL (inout zipArchive: zip, in string: filePath) is func
result
var fileType: aFileType is FILE_UNKNOWN;
local
var central_file_header: header is central_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
elsif implicitDir(zip.register, filePath) then
header := addImplicitDir(zip, filePath);
else
aFileType := FILE_ABSENT;
end if;
if aFileType = FILE_UNKNOWN then
case header.version_made_by >> 8 of
when {ZIP_HOST_SYSTEM_UNIX}:
case bin32(header.external_file_attributes >> 16) & MODE_FILE_TYPE_MASK of
when {MODE_FILE_REGULAR}: aFileType := FILE_REGULAR;
when {MODE_FILE_DIR}: aFileType := FILE_DIR;
when {MODE_FILE_CHAR}: aFileType := FILE_CHAR;
when {MODE_FILE_BLOCK}: aFileType := FILE_BLOCK;
when {MODE_FILE_FIFO}: aFileType := FILE_FIFO;
when {MODE_FILE_SOCKET}: aFileType := FILE_SOCKET;
when {MODE_FILE_SYMLINK}: aFileType := FILE_SYMLINK;
otherwise: aFileType := FILE_UNKNOWN;
end case;
otherwise:
if endsWith(header.file_name, "/") then
aFileType := FILE_DIR;
else
aFileType := FILE_REGULAR;
end if;
end case;
end if;
end if;
end func;
const func fileMode: getFileMode (inout zipArchive: zip, in string: filePath) is func
result
var fileMode: mode is fileMode.value;
local
const bin32: FAT_READ_ONLY is bin32(16#01);
const bin32: FAT_HIDDEN is bin32(16#02);
const bin32: FAT_SYSTEM is bin32(16#04);
const bin32: FAT_VOLUME_LABEL is bin32(16#08);
const bin32: FAT_DIRECTORY is bin32(16#10);
const bin32: FAT_ARCHIVE is bin32(16#20);
const bin32: FAT_DEVICE is bin32(16#40);
var central_file_header: header is central_file_header.value;
var string: extension is "";
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
case header.version_made_by >> 8 of
when {ZIP_HOST_SYSTEM_MS_DOS}:
mode := {READ_USER, READ_GROUP, READ_OTHER};
if bin32(header.external_file_attributes) & FAT_READ_ONLY = bin32(0) then
mode |:= {WRITE_USER, WRITE_GROUP, WRITE_OTHER};
end if;
if bin32(header.external_file_attributes) & FAT_DIRECTORY <> bin32(0) then
mode |:= {EXEC_USER, EXEC_GROUP, EXEC_OTHER};
end if;
if length(header.file_name) >= 5 then
extension := lower(header.file_name[length(header.file_name) - 3 ..]);
if extension in {".bat", ".cmd", ".com", ".exe"} then
mode |:= {EXEC_USER, EXEC_GROUP, EXEC_OTHER};
end if;
end if;
when {ZIP_HOST_SYSTEM_UNIX}:
mode := fileMode((header.external_file_attributes >> 16) mod 8#1000);
otherwise:
mode := {READ_USER, READ_GROUP, READ_OTHER,
WRITE_USER, WRITE_GROUP, WRITE_OTHER};
if endsWith(header.file_name, "/") then
mode |:= {EXEC_USER, EXEC_GROUP, EXEC_OTHER};
end if;
end case;
end if;
end func;
const proc: setFileMode (inout zipArchive: zip, in string: filePath,
in fileMode: mode) is func
local
var central_file_header: header is central_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
header.external_file_attributes :=
header.external_file_attributes mod 16#10000 +
(((header.external_file_attributes >> 25 << 9) + integer(mode)) << 16);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
end if;
end func;
const func integer: fileSize (inout zipArchive: zip, in string: filePath) is func
result
var integer: size is 0;
local
var central_file_header: header is central_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
size := followSymlink(zip, filePath).uncompressed_size;
end if;
end func;
const func time: getMTime (inout zipArchive: zip, in string: filePath) is func
result
var time: modificationTime is time.value;
local
var central_file_header: header is central_file_header.value;
var integer: timestamp is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
if 16#5455 in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#5455][2 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#5855 in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#5855][5 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#000d in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#000d][5 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#000a in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#000a][9 fixLen 8], UNSIGNED, LE);
modificationTime := timestamp1601ToTime(timestamp);
else
modificationTime.year := (header.last_mod_file_date >> 9) + 1980;
modificationTime.month := (header.last_mod_file_date >> 5) mod 16;
modificationTime.day := header.last_mod_file_date mod 32;
modificationTime.hour := header.last_mod_file_time >> 11;
modificationTime.minute := (header.last_mod_file_time >> 5) mod 64;
modificationTime.second := (header.last_mod_file_time mod 32) * 2;
modificationTime := setLocalTZ(modificationTime);
end if;
end if;
end func;
const proc: setMTime (inout zipArchive: zip, in string: filePath,
in time: modificationTime) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
assignLastModFileTime(header, modificationTime);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignLastModFileTime(localHeader, modificationTime);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
end if;
end func;
const func string: getOwner (inout zipArchive: zip, in string: filePath) is func
result
var string: owner is "";
local
var central_file_header: header is central_file_header.value;
var integer: size is 0;
var integer: uid is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
if 16#000d in header.extraFieldMap then
uid := bytes2Int(header.extraFieldMap[16#000d][9 fixLen 2], UNSIGNED, LE);
owner := str(uid);
elsif 16#756e in header.extraFieldMap then
uid := bytes2Int(header.extraFieldMap[16#756e][11 fixLen 2], UNSIGNED, LE);
owner := str(uid);
elsif 16#7875 in header.extraFieldMap then
size := ord(header.extraFieldMap[16#7875][2]);
uid := bytes2Int(header.extraFieldMap[16#7875][3 fixLen size], UNSIGNED, LE);
owner := str(uid);
end if;
end if;
end func;
const proc: setOwner (inout zipArchive: zip, in string: filePath,
in string: owner) is func
local
var integer: uid is 0;
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if isDigitString(owner) then
uid := integer(owner);
elsif owner <> "root" then
raise RANGE_ERROR;
end if;
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
assignUserId(header, uid);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignUserId(localHeader, uid);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
end if;
end func;
const func string: getGroup (inout zipArchive: zip, in string: filePath) is func
result
var string: group is "";
local
var central_file_header: header is central_file_header.value;
var integer: pos is 0;
var integer: size is 0;
var integer: gid is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
if 16#000d in header.extraFieldMap then
gid := bytes2Int(header.extraFieldMap[16#000d][11 fixLen 2], UNSIGNED, LE);
group := str(gid);
elsif 16#756e in header.extraFieldMap then
gid := bytes2Int(header.extraFieldMap[16#756e][13 fixLen 2], UNSIGNED, LE);
group := str(gid);
elsif 16#7875 in header.extraFieldMap then
pos := 3 + ord(header.extraFieldMap[16#7875][2]);
size := ord(header.extraFieldMap[16#7875][pos]);
gid := bytes2Int(header.extraFieldMap[16#7875][succ(pos) fixLen size], UNSIGNED, LE);
group := str(gid);
end if;
end if;
end func;
const proc: setGroup (inout zipArchive: zip, in string: filePath,
in string: group) is func
local
var integer: gid is 0;
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if isDigitString(group) then
gid := integer(group);
elsif group <> "root" then
raise RANGE_ERROR;
end if;
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
assignGroupId(header, gid);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignGroupId(localHeader, gid);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
end if;
end func;
const func fileMode: getFileMode (inout zipArchive: zip, in string: filePath, SYMLINK) is func
result
var fileMode: mode is fileMode.value;
local
var central_file_header: header is central_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
mode := fileMode((header.external_file_attributes >> 16) mod 8#1000);
else
raise FILE_ERROR;
end if;
end if;
end func;
const func time: getMTime (inout zipArchive: zip, in string: filePath, SYMLINK) is func
result
var time: modificationTime is time.value;
local
var central_file_header: header is central_file_header.value;
var integer: timestamp is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
if 16#5455 in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#5455][2 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#5855 in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#5855][5 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#000d in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#000d][5 fixLen 4], UNSIGNED, LE);
modificationTime := timestamp1970ToTime(timestamp);
elsif 16#000a in header.extraFieldMap then
timestamp := bytes2Int(header.extraFieldMap[16#000a][9 fixLen 8], UNSIGNED, LE);
modificationTime := timestamp1601ToTime(timestamp);
else
modificationTime.year := (header.last_mod_file_date >> 9) + 1980;
modificationTime.month := (header.last_mod_file_date >> 5) mod 16;
modificationTime.day := header.last_mod_file_date mod 32;
modificationTime.hour := header.last_mod_file_time >> 11;
modificationTime.minute := (header.last_mod_file_time >> 5) mod 64;
modificationTime.second := (header.last_mod_file_time mod 32) * 2;
modificationTime := setLocalTZ(modificationTime);
end if;
else
raise FILE_ERROR;
end if;
end if;
end func;
const proc: setMTime (inout zipArchive: zip, in string: filePath,
in time: modificationTime, SYMLINK) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
assignLastModFileTime(header, modificationTime);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignLastModFileTime(localHeader, modificationTime);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
else
raise FILE_ERROR;
end if;
end if;
end func;
const func string: getOwner (inout zipArchive: zip, in string: filePath, SYMLINK) is func
result
var string: owner is "";
local
var central_file_header: header is central_file_header.value;
var integer: size is 0;
var integer: uid is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
if 16#000d in header.extraFieldMap then
uid := bytes2Int(header.extraFieldMap[16#000d][9 fixLen 2], UNSIGNED, LE);
owner := str(uid);
elsif 16#756e in header.extraFieldMap then
uid := bytes2Int(header.extraFieldMap[16#756e][11 fixLen 2], UNSIGNED, LE);
owner := str(uid);
elsif 16#7875 in header.extraFieldMap then
size := ord(header.extraFieldMap[16#7875][2]);
uid := bytes2Int(header.extraFieldMap[16#7875][3 fixLen size], UNSIGNED, LE);
owner := str(uid);
end if;
else
raise FILE_ERROR;
end if;
end if;
end func;
const proc: setOwner (inout zipArchive: zip, in string: filePath,
in string: owner, SYMLINK) is func
local
var integer: uid is 0;
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if isDigitString(owner) then
uid := integer(owner);
elsif owner <> "root" then
raise RANGE_ERROR;
end if;
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
assignUserId(header, uid);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignUserId(localHeader, uid);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
else
raise FILE_ERROR;
end if;
end if;
end func;
const func string: getGroup (inout zipArchive: zip, in string: filePath, SYMLINK) is func
result
var string: group is "";
local
var central_file_header: header is central_file_header.value;
var integer: pos is 0;
var integer: size is 0;
var integer: gid is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
if 16#000d in header.extraFieldMap then
gid := bytes2Int(header.extraFieldMap[16#000d][11 fixLen 2], UNSIGNED, LE);
group := str(gid);
elsif 16#756e in header.extraFieldMap then
gid := bytes2Int(header.extraFieldMap[16#756e][13 fixLen 2], UNSIGNED, LE);
group := str(gid);
elsif 16#7875 in header.extraFieldMap then
pos := 3 + ord(header.extraFieldMap[16#7875][2]);
size := ord(header.extraFieldMap[16#7875][pos]);
gid := bytes2Int(header.extraFieldMap[16#7875][succ(pos) fixLen size], UNSIGNED, LE);
group := str(gid);
end if;
else
raise FILE_ERROR;
end if;
end if;
end func;
const proc: setGroup (inout zipArchive: zip, in string: filePath,
in string: group, SYMLINK) is func
local
var integer: gid is 0;
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
begin
if isDigitString(group) then
gid := integer(group);
elsif group <> "root" then
raise RANGE_ERROR;
end if;
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath = "" then
raise FILE_ERROR;
else
if filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
assignGroupId(header, gid);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
assignGroupId(localHeader, gid);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
else
raise FILE_ERROR;
end if;
end if;
end func;
const func string: readLink (inout zipArchive: zip, in string: filePath) is func
result
var string: linkPath is "";
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var string: linkPath8 is "";
var bin32: crc_32 is bin32(0);
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
else
raise FILE_ERROR;
end if;
if isSymlink(header) then
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
if localHeader.compression_method = 0 then
linkPath8 := gets(zip.zipFile, localHeader.compressed_size);
else
raise FILE_ERROR;
end if;
crc_32 := crc32(linkPath8);
if localHeader.crc_32 <> crc_32 or
localHeader.uncompressed_size <> length(linkPath8) or
header.crc_32 <> crc_32 or
header.uncompressed_size <> length(linkPath8) then
raise FILE_ERROR;
end if;
block
linkPath := fromUtf8(linkPath8);
exception
catch RANGE_ERROR:
linkPath := linkPath8;
end block;
else
raise FILE_ERROR;
end if;
end func;
const proc: makeLink (inout zipArchive: zip, in string: symlinkPath,
in string: targetPath) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var string: symlinkPath8 is "";
var string: targetPath8 is "";
var integer: roomForNewFile is 0;
begin
if symlinkPath <> "/" and endsWith(symlinkPath, "/") then
raise RANGE_ERROR;
elsif symlinkPath = "" or symlinkPath in zip.catalog or
symlinkPath in zip.register or implicitDir(zip.register, symlinkPath) then
raise FILE_ERROR;
else
symlinkPath8 := toUtf8(symlinkPath);
targetPath8 := toUtf8(targetPath);
header.signature := ZIP_CENTRAL_HEADER_SIGNATURE;
header.version_made_by := (ZIP_HOST_SYSTEM_UNIX << 8) + 16#1e;
header.version_needed_to_extract := 10;
if symlinkPath8 <> symlinkPath then
header.general_purpose_bit_flag := ZIP_FILE_NAME_IS_UTF8;
else
header.general_purpose_bit_flag := bin32(0);
end if;
header.compression_method := 0;
header.crc_32 := crc32(targetPath8);
header.compressed_size := length(targetPath8);
header.uncompressed_size := length(targetPath8);
header.disk_number_start := 0;
header.internal_file_attributes := 0;
header.external_file_attributes := (ord(MODE_FILE_SYMLINK) + 8#777) << 16;
header.file_name := symlinkPath8;
initLastModFileTime(header, time(NOW));
header.file_comment := "";
header.relative_offset_of_local_header := pred(zip.startOfCentralDirPos);
roomForNewFile := ZIP_LOCAL_HEADER_FIXED_SIZE + length(header.file_name) +
length(header.extra_field) + header.compressed_size;
insertArea(zip.zipFile, zip.startOfCentralDirPos, roomForNewFile);
fixRegisterAndCatalog(zip, zip.startOfCentralDirPos, roomForNewFile);
localHeader := toLocalHeader(header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
write(zip.zipFile, targetPath8);
seek(zip.zipFile, zip.endOfCentralDirPos);
zip.register @:= [symlinkPath] zip.endOfCentralDirPos;
writeHead(zip.zipFile, header);
zip.catalog @:= [symlinkPath] header;
zip.endOfCentralDirPos := tell(zip.zipFile);
incr(zip.endOfCentralDir.entries_in_central_directory_on_this_disk);
incr(zip.endOfCentralDir.entries_in_central_directory);
zip.endOfCentralDir.size_of_central_directory := zip.endOfCentralDirPos - zip.startOfCentralDirPos;
zip.endOfCentralDir.offset_of_start_of_central_directory := pred(zip.startOfCentralDirPos);
write(zip.zipFile, str(zip.endOfCentralDir));
end if;
end func;
const func string: getFile (inout zipArchive: zip, in string: filePath) is func
result
var string: content is "";
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var integer: dataDescriptorSize is 0;
var integer: signaturePos is 0;
var string: stri is "";
var bin32: crc_32 is bin32(0);
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
else
header := followSymlink(zip, filePath);
if isRegularFile(header) then
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
localHeader := get_local_header(zip.zipFile);
if localHeader.compression_method = 0 then
content := gets(zip.zipFile, localHeader.compressed_size);
elsif localHeader.compression_method = 8 then
if localHeader.general_purpose_bit_flag & ZIP_HAS_DATA_DESCRIPTOR <> bin32(0) then
content := inflate(zip.zipFile);
dataDescriptorSize := 16#0001 in header.extraFieldMap ? 20 : 12;
stri := gets(zip.zipFile, 4);
if stri = ZIP_DATA_DESCRIPTOR_SIGNATURE then
stri := gets(zip.zipFile, dataDescriptorSize);
else
stri &:= gets(zip.zipFile, dataDescriptorSize - 4);
if length(stri) = dataDescriptorSize then
signaturePos := pos(stri, ZIP_DATA_DESCRIPTOR_SIGNATURE);
if signaturePos <> 0 then
stri := stri[signaturePos + 4 .. ] &
gets(zip.zipFile, signaturePos + 3);
end if;
end if;
end if;
if length(stri) = dataDescriptorSize then
if dataDescriptorSize = 12 then
localHeader.crc_32 := bin32(bytes2Int(stri[1 fixLen 4], UNSIGNED, LE));
localHeader.compressed_size := bytes2Int(stri[5 fixLen 4], UNSIGNED, LE);
localHeader.uncompressed_size := bytes2Int(stri[9 fixLen 4], UNSIGNED, LE);
else
localHeader.crc_32 := bin32(bytes2Int(stri[1 fixLen 4], UNSIGNED, LE));
localHeader.compressed_size := bytes2Int(stri[5 fixLen 8], UNSIGNED, LE);
localHeader.uncompressed_size := bytes2Int(stri[9 fixLen 8], UNSIGNED, LE);
end if;
else
raise RANGE_ERROR;
end if;
else
content := gets(zip.zipFile, localHeader.compressed_size);
content := inflate(content);
end if;
else
raise FILE_ERROR;
end if;
crc_32 := crc32(content);
if localHeader.crc_32 <> crc_32 or
localHeader.uncompressed_size <> length(content) or
header.crc_32 <> crc_32 or
header.uncompressed_size <> length(content) then
raise FILE_ERROR;
end if;
else
raise FILE_ERROR;
end if;
end if;
end func;
const proc: putFile (inout zipArchive: zip, in string: filePath,
in string: data) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var boolean: fileExists is TRUE;
var time: modificationTime is time.value;
var integer: oldSize is 0;
var integer: newSize is 0;
var integer: localHeaderPos is 0;
var string: filePath8 is "";
var string: compressed is "";
var integer: roomForNewFile is 0;
begin
if filePath = "" or filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
elsif implicitDir(zip.register, filePath) then
raise FILE_ERROR;
else
fileExists := FALSE;
end if;
compressed := deflate(data);
oldSize := header.compressed_size;
header.crc_32 := crc32(data);
header.uncompressed_size := length(data);
if length(compressed) >= length(data) then
header.compression_method := 0;
header.compressed_size := length(data);
compressed := data;
else
header.compression_method := 8;
header.compressed_size := length(compressed);
end if;
if fileExists then
if endsWith(header.file_name, "/") then
raise FILE_ERROR;
else
modificationTime := time(NOW);
assignLastModFileTime(header, modificationTime);
localHeaderPos := succ(header.relative_offset_of_local_header);
seek(zip.zipFile, localHeaderPos);
localHeader := get_local_header(zip.zipFile);
newSize := header.compressed_size;
if newSize > oldSize then
insertArea(zip.zipFile, localHeaderPos, newSize - oldSize);
fixRegisterAndCatalog(zip, localHeaderPos, newSize - oldSize);
elsif newSize < oldSize then
deleteArea(zip.zipFile, localHeaderPos, oldSize - newSize);
fixRegisterAndCatalog(zip, localHeaderPos + (oldSize - newSize),
newSize - oldSize);
end if;
updateLocalHeader(localHeader, header);
assignLastModFileTime(localHeader, modificationTime);
seek(zip.zipFile, localHeaderPos);
writeHead(zip.zipFile, localHeader);
write(zip.zipFile, compressed);
zip.catalog @:= [filePath] header;
seek(zip.zipFile, zip.register[filePath]);
writeHead(zip.zipFile, header);
zip.endOfCentralDir.offset_of_start_of_central_directory := pred(zip.startOfCentralDirPos);
seek(zip.zipFile, zip.endOfCentralDirPos);
write(zip.zipFile, str(zip.endOfCentralDir));
flush(zip.zipFile);
end if;
else
filePath8 := toUtf8(filePath);
header.signature := ZIP_CENTRAL_HEADER_SIGNATURE;
header.version_made_by := (ZIP_HOST_SYSTEM_UNIX << 8) + 16#1e;
header.version_needed_to_extract := 10;
if filePath8 <> filePath then
header.general_purpose_bit_flag := ZIP_FILE_NAME_IS_UTF8;
else
header.general_purpose_bit_flag := bin32(0);
end if;
header.disk_number_start := 0;
header.internal_file_attributes := 0;
header.external_file_attributes := (ord(MODE_FILE_REGULAR) + 8#664) << 16;
header.file_name := filePath8;
initLastModFileTime(header, time(NOW));
header.file_comment := "";
header.relative_offset_of_local_header := pred(zip.startOfCentralDirPos);
roomForNewFile := ZIP_LOCAL_HEADER_FIXED_SIZE + length(header.file_name) +
length(header.extra_field) + header.compressed_size;
insertArea(zip.zipFile, zip.startOfCentralDirPos, roomForNewFile);
fixRegisterAndCatalog(zip, zip.startOfCentralDirPos, roomForNewFile);
localHeader := toLocalHeader(header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
write(zip.zipFile, compressed);
seek(zip.zipFile, zip.endOfCentralDirPos);
zip.register @:= [filePath] zip.endOfCentralDirPos;
writeHead(zip.zipFile, header);
zip.catalog @:= [filePath] header;
zip.endOfCentralDirPos := tell(zip.zipFile);
incr(zip.endOfCentralDir.entries_in_central_directory_on_this_disk);
incr(zip.endOfCentralDir.entries_in_central_directory);
zip.endOfCentralDir.size_of_central_directory := zip.endOfCentralDirPos - zip.startOfCentralDirPos;
zip.endOfCentralDir.offset_of_start_of_central_directory := pred(zip.startOfCentralDirPos);
write(zip.zipFile, str(zip.endOfCentralDir));
end if;
end func;
const proc: makeDir (inout zipArchive: zip, in string: dirPath) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var boolean: fileExists is TRUE;
var integer: relative_offset_of_local_header is 0;
var string: dirPath8 is "";
var integer: roomForNewFile is 0;
begin
if dirPath = "" or dirPath <> "/" and endsWith(dirPath, "/") then
raise RANGE_ERROR;
elsif dirPath in zip.catalog then
relative_offset_of_local_header := zip.catalog[dirPath].relative_offset_of_local_header;
elsif dirPath in zip.register then
relative_offset_of_local_header := addToCatalog(zip, dirPath).relative_offset_of_local_header;
elsif implicitDir(zip.register, dirPath) then
relative_offset_of_local_header := addImplicitDir(zip, dirPath).relative_offset_of_local_header;
else
fileExists := FALSE;
end if;
if fileExists and relative_offset_of_local_header <> -1 then
raise FILE_ERROR;
else
dirPath8 := toUtf8(dirPath);
header.signature := ZIP_CENTRAL_HEADER_SIGNATURE;
header.version_made_by := (ZIP_HOST_SYSTEM_UNIX << 8) + 16#1e;
header.version_needed_to_extract := 10;
if dirPath8 <> dirPath then
header.general_purpose_bit_flag := ZIP_FILE_NAME_IS_UTF8;
else
header.general_purpose_bit_flag := bin32(0);
end if;
header.compression_method := 0;
header.compressed_size := 0;
header.uncompressed_size := 0;
header.disk_number_start := 0;
header.internal_file_attributes := 0;
header.external_file_attributes := (ord(MODE_FILE_DIR) + 8#775) << 16;
header.file_name := dirPath8 & "/";
initLastModFileTime(header, time(NOW));
header.file_comment := "";
header.relative_offset_of_local_header := pred(zip.startOfCentralDirPos);
roomForNewFile := ZIP_LOCAL_HEADER_FIXED_SIZE + length(header.file_name) +
length(header.extra_field);
insertArea(zip.zipFile, zip.startOfCentralDirPos, roomForNewFile);
fixRegisterAndCatalog(zip, zip.startOfCentralDirPos, roomForNewFile);
localHeader := toLocalHeader(header);
seek(zip.zipFile, succ(header.relative_offset_of_local_header));
writeHead(zip.zipFile, localHeader);
seek(zip.zipFile, zip.endOfCentralDirPos);
zip.register @:= [dirPath] zip.endOfCentralDirPos;
writeHead(zip.zipFile, header);
zip.catalog @:= [dirPath] header;
zip.endOfCentralDirPos := tell(zip.zipFile);
incr(zip.endOfCentralDir.entries_in_central_directory_on_this_disk);
incr(zip.endOfCentralDir.entries_in_central_directory);
zip.endOfCentralDir.size_of_central_directory := zip.endOfCentralDirPos - zip.startOfCentralDirPos;
zip.endOfCentralDir.offset_of_start_of_central_directory := pred(zip.startOfCentralDirPos);
write(zip.zipFile, str(zip.endOfCentralDir));
end if;
end func;
const proc: removeFile (inout zipArchive: zip, in string: filePath) is func
local
var central_file_header: header is central_file_header.value;
var local_file_header: localHeader is local_file_header.value;
var boolean: fileExists is TRUE;
var integer: posOfHeaderToBeRemoved is 0;
var integer: numCharsToBeRemoved is 0;
begin
if filePath <> "/" and endsWith(filePath, "/") then
raise RANGE_ERROR;
elsif filePath in zip.catalog then
header := zip.catalog[filePath];
elsif filePath in zip.register then
header := addToCatalog(zip, filePath);
elsif implicitDir(zip.register, filePath) then
header := addImplicitDir(zip, filePath);
else
fileExists := FALSE;
end if;
if fileExists and
(not endsWith(header.file_name, "/") or
isEmptyDir(zip.register, filePath)) then
posOfHeaderToBeRemoved := succ(header.relative_offset_of_local_header);
numCharsToBeRemoved := ZIP_LOCAL_HEADER_FIXED_SIZE + length(header.file_name) +
length(header.extra_field) + header.compressed_size;
deleteArea(zip.zipFile, posOfHeaderToBeRemoved, numCharsToBeRemoved);
fixRegisterAndCatalog(zip, posOfHeaderToBeRemoved + numCharsToBeRemoved,
-numCharsToBeRemoved);
posOfHeaderToBeRemoved := zip.register[filePath];
numCharsToBeRemoved := ZIP_CENTRAL_HEADER_FIXED_SIZE + length(header.file_name) +
length(header.extra_field) + length(header.file_comment);
deleteArea(zip.zipFile, posOfHeaderToBeRemoved, numCharsToBeRemoved);
excl(zip.register, filePath);
excl(zip.catalog, filePath);
fixRegisterAndCatalog(zip, posOfHeaderToBeRemoved + numCharsToBeRemoved,
-numCharsToBeRemoved);
decr(zip.endOfCentralDir.entries_in_central_directory_on_this_disk);
decr(zip.endOfCentralDir.entries_in_central_directory);
zip.endOfCentralDir.size_of_central_directory := zip.endOfCentralDirPos - zip.startOfCentralDirPos;
zip.endOfCentralDir.offset_of_start_of_central_directory := pred(zip.startOfCentralDirPos);
seek(zip.zipFile, zip.endOfCentralDirPos);
write(zip.zipFile, str(zip.endOfCentralDir));
flush(zip.zipFile);
else
raise FILE_ERROR;
end if;
end func;
const func string: getZipContent (in string: zipFilePath, in string: filePath) is func
result
var string: content is "";
local
var fileSys: zip is fileSys.value;
begin
zip := openZip(zipFilePath);
if zip <> fileSys.value then
content := getFile(zip, filePath);
close(zip);
end if;
end func;
const proc: for (inout string: filePath) range (inout zipArchive: zip) do
(in proc: statements)
end for is func
begin
for key filePath range zip.register do
statements;
end for;
end func;