Browse Source

* Patch from Reinier Olislagers to let filenames conform to standard / (bug id 26468)

git-svn-id: trunk@28198 -
michael 11 years ago
parent
commit
c281c4d036

+ 1 - 1
packages/paszlib/examples/miniunz.pas

@@ -9,7 +9,7 @@ program MiniUnz;
         -x like -e, but extract without path information
         -x like -e, but extract without path information
         -o overwrite an existing file without warning
         -o overwrite an existing file without warning
 
 
-  Pascal tranlastion
+  Pascal translation
   Copyright (C) 2000 by Jacques Nomssi Nzali
   Copyright (C) 2000 by Jacques Nomssi Nzali
   For conditions of distribution and use, see copyright notice in readme.txt
   For conditions of distribution and use, see copyright notice in readme.txt
 }{$ifdef WIN32}
 }{$ifdef WIN32}

+ 59 - 15
packages/paszlib/src/zipper.pp

@@ -1,7 +1,7 @@
 {
 {
     $Id: header,v 1.3 2013/05/26 06:33:45 michael Exp $
     $Id: header,v 1.3 2013/05/26 06:33:45 michael Exp $
     This file is part of the Free Component Library (FCL)
     This file is part of the Free Component Library (FCL)
-    Copyright (c) 1999-2013 by the Free Pascal development team
+    Copyright (c) 1999-2014 by the Free Pascal development team
 
 
     See the file COPYING.FPC, included in this distribution,
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
     for details about the copyright.
@@ -90,7 +90,7 @@ Type
     Starting_Disk_Num    :  Word;
     Starting_Disk_Num    :  Word;
     Internal_Attributes  :  Word;
     Internal_Attributes  :  Word;
     External_Attributes  :  LongWord;
     External_Attributes  :  LongWord;
-    Local_Header_Offset  :  LongWord; //todo: use zip64 and set to 0xFFFFFFFF if needed
+    Local_Header_Offset  :  LongWord; // if zip64: 0xFFFFFFFF
   End;
   End;
 
 
   End_of_Central_Dir_Type =  Packed Record //End of central directory record
   End_of_Central_Dir_Type =  Packed Record //End of central directory record
@@ -306,10 +306,11 @@ Type
 
 
   TZipFileEntry = Class(TCollectionItem)
   TZipFileEntry = Class(TCollectionItem)
   private
   private
-    FArchiveFileName: String;
+    FArchiveFileName: String; //Name of the file as it appears in the zip file list
     FAttributes: LongInt;
     FAttributes: LongInt;
     FDateTime: TDateTime;
     FDateTime: TDateTime;
-    FDiskFileName: String;
+    FDiskFileName: String; {Name of the file on disk (i.e. uncompressed. Can be empty if based on a stream.);
+    uses local OS/filesystem directory separators}
     FHeaderPos: int64;
     FHeaderPos: int64;
     FNeedsZip64: Boolean; //flags whether filesize is big enough so we need a zip64 entry
     FNeedsZip64: Boolean; //flags whether filesize is big enough so we need a zip64 entry
     FOS: Byte;
     FOS: Byte;
@@ -317,6 +318,8 @@ Type
     FStream: TStream;
     FStream: TStream;
     FCompressionLevel: TCompressionlevel;
     FCompressionLevel: TCompressionlevel;
     function GetArchiveFileName: String;
     function GetArchiveFileName: String;
+    procedure SetArchiveFileName(Const AValue: String);
+    procedure SetDiskFileName(Const AValue: String);
   Protected
   Protected
     // For multi-disk support, a disk number property could be added here.
     // For multi-disk support, a disk number property could be added here.
     Property HdrPos : int64 Read FHeaderPos Write FheaderPos;
     Property HdrPos : int64 Read FHeaderPos Write FheaderPos;
@@ -328,8 +331,8 @@ Type
     Procedure Assign(Source : TPersistent); override;
     Procedure Assign(Source : TPersistent); override;
     Property Stream : TStream Read FStream Write FStream;
     Property Stream : TStream Read FStream Write FStream;
   Published
   Published
-    Property ArchiveFileName : String Read GetArchiveFileName Write FArchiveFileName;
-    Property DiskFileName : String Read FDiskFileName Write FDiskFileName;
+    Property ArchiveFileName : String Read GetArchiveFileName Write SetArchiveFileName;
+    Property DiskFileName : String Read FDiskFileName Write SetDiskFileName;
     Property Size : Int64 Read FSize Write FSize;
     Property Size : Int64 Read FSize Write FSize;
     Property DateTime : TDateTime Read FDateTime Write FDateTime;
     Property DateTime : TDateTime Read FDateTime Write FDateTime;
     property OS: Byte read FOS write FOS;
     property OS: Byte read FOS write FOS;
@@ -393,10 +396,14 @@ Type
     Constructor Create;
     Constructor Create;
     Destructor Destroy;override;
     Destructor Destroy;override;
     Procedure ZipAllFiles; virtual;
     Procedure ZipAllFiles; virtual;
+    // Saves zip to file and changes FileName
     Procedure SaveToFile(AFileName: string);
     Procedure SaveToFile(AFileName: string);
+    // Saves zip to stream
     Procedure SaveToStream(AStream: TStream);
     Procedure SaveToStream(AStream: TStream);
+    // Zips specified files into a zip with name AFileName
     Procedure ZipFiles(AFileName : String; FileList : TStrings);
     Procedure ZipFiles(AFileName : String; FileList : TStrings);
     Procedure ZipFiles(FileList : TStrings);
     Procedure ZipFiles(FileList : TStrings);
+    // Zips specified entries into a zip with name AFileName
     Procedure ZipFiles(AFileName : String; Entries : TZipFileEntries);
     Procedure ZipFiles(AFileName : String; Entries : TZipFileEntries);
     Procedure ZipFiles(Entries : TZipFileEntries);
     Procedure ZipFiles(Entries : TZipFileEntries);
     Procedure Clear;
     Procedure Clear;
@@ -1513,7 +1520,7 @@ Var
   ACount    : QWord; //entry counter
   ACount    : QWord; //entry counter
   ZFileName : string; //archive filename
   ZFileName : string; //archive filename
   IsZip64   : boolean; //local header=zip64 format?
   IsZip64   : boolean; //local header=zip64 format?
-  MinReqdVersion: word; //minimum
+  MinReqdVersion: word; //minimum needed to extract
   ExtInfoHeader : Extensible_Data_Field_Header_Type;
   ExtInfoHeader : Extensible_Data_Field_Header_Type;
   Zip64ECD  : Zip64_End_of_Central_Dir_type;
   Zip64ECD  : Zip64_End_of_Central_Dir_type;
   Zip64ECDL : Zip64_End_of_Central_Dir_Locator_type;
   Zip64ECDL : Zip64_End_of_Central_Dir_Locator_type;
@@ -1779,6 +1786,7 @@ procedure TZipper.SaveToFile(AFileName: string);
 var
 var
   lStream: TFileStream;
   lStream: TFileStream;
 begin
 begin
+  FFileName:=AFileName;
   lStream:=TFileStream.Create(FFileName,fmCreate);
   lStream:=TFileStream.Create(FFileName,fmCreate);
   try
   try
     SaveToStream(lStream);
     SaveToStream(lStream);
@@ -1956,13 +1964,22 @@ Begin
     as directory separator. We don't want that behavior
     as directory separator. We don't want that behavior
     here, since 'abc\' is a valid file name under Unix.
     here, since 'abc\' is a valid file name under Unix.
 	
 	
-    (mantis 15836) On the other hand, many archives on
-    Windows have '/' as pathseparator, even Windows
-    generated .odt files. So we disable this for Windows.
+    The zip standard appnote.txt says zip files must have '/' as path
+    separator, even on Windows: 4.4.17.1:
+    "The path stored MUST not contain a drive or device letter, or a leading
+    slash. All slashes MUST be forward slashes '/' as opposed to backwards
+    slashes '\'" See also mantis issue #15836
+    However, old versions of FPC on Windows (and possibly other utilities)
+    created incorrect zip files with \ separator, so accept these as well as
+    they're not valid in Windows file names anyway.
   }
   }
   OldDirectorySeparators:=AllowDirectorySeparators;
   OldDirectorySeparators:=AllowDirectorySeparators;
-  {$ifndef Windows}
-  AllowDirectorySeparators:=[DirectorySeparator];
+  {$ifdef Windows}
+  // Explicitly allow / and \ regardless of what Windows supports
+  AllowDirectorySeparators:=['\','/'];
+  {$else}
+  // Follow the standard: only allow / regardless of actual separator on OS
+  AllowDirectorySeparators:=['/'];
   {$endif}
   {$endif}
   Path:=ExtractFilePath(OutFileName);
   Path:=ExtractFilePath(OutFileName);
   OutStream:=Nil;
   OutStream:=Nil;
@@ -2365,7 +2382,9 @@ Var
   end;
   end;
 Begin
 Begin
   ReadZipHeader(Item, ZMethod);
   ReadZipHeader(Item, ZMethod);
-  OutputFileName:=Item.DiskFileName;
+  // Normalize output filename to conventions of target platform.
+  // Zip file always has / path separators
+  OutputFileName:=StringReplace(Item.DiskFileName,'/',DirectorySeparator,[rfReplaceAll]);
 
 
   IsCustomStream := Assigned(FOnCreateStream);
   IsCustomStream := Assigned(FOnCreateStream);
 
 
@@ -2377,7 +2396,8 @@ Begin
 {$IFNDEF UNIX}
 {$IFNDEF UNIX}
   if IsLink and Not IsCustomStream then
   if IsLink and Not IsCustomStream then
   begin
   begin
-    {$warning TODO: Implement symbolic link creation for non-unix}
+    {$warning TODO: Implement symbolic link creation for non-unix, e.g.
+    Windows NTFS}
     IsLink := False;
     IsLink := False;
   end;
   end;
 {$ENDIF}
 {$ENDIF}
@@ -2618,7 +2638,7 @@ end;
 
 
 function TZipFileEntry.IsDirectory: Boolean;
 function TZipFileEntry.IsDirectory: Boolean;
 begin
 begin
-  Result := (DiskFileName <> '') and (DiskFileName[Length(DiskFileName)] in ['/', '\']);
+  Result := (DiskFileName <> '') and (DiskFileName[Length(DiskFileName)] = DirectorySeparator);
   if Attributes <> 0 then
   if Attributes <> 0 then
   begin
   begin
     case OS of
     case OS of
@@ -2640,6 +2660,30 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TZipFileEntry.SetArchiveFileName(const AValue: String);
+var
+  Separator: char;
+begin
+  if FArchiveFileName=AValue then Exit;
+  // Zip standard: filenames inside the zip archive have / path separator
+  if DirectorySeparator='/' then
+    FArchiveFileName:=AValue
+  else
+    FArchiveFileName:=StringReplace(AValue, DirectorySeparator, '/', [rfReplaceAll]);
+end;
+
+procedure TZipFileEntry.SetDiskFileName(const AValue: String);
+begin
+  if FDiskFileName=AValue then Exit;
+  // Zip file uses / as directory separator on all platforms
+  // so convert to separator used on current OS
+  if DirectorySeparator='/' then
+    FDiskFileName:=AValue
+  else
+    FDiskFileName:=StringReplace(AValue,'/',DirectorySeparator,[rfReplaceAll]);
+end;
+
+
 procedure TZipFileEntry.Assign(Source: TPersistent);
 procedure TZipFileEntry.Assign(Source: TPersistent);
 
 
 Var
 Var

+ 205 - 15
packages/paszlib/tests/tczipper.pp

@@ -1,7 +1,7 @@
 program tczipper;
 program tczipper;
 {
 {
     This file is part of the Free Pascal packages.
     This file is part of the Free Pascal packages.
-    Copyright (c) 2012-2013 by the Free Pascal Development Team
+    Copyright (c) 2012-2014 by the Free Pascal Development Team
     Created by Reinier Olislagers
     Created by Reinier Olislagers
 
 
     Tests zip/unzip functionality provided by the FPC zipper.pp unit.
     Tests zip/unzip functionality provided by the FPC zipper.pp unit.
@@ -17,7 +17,9 @@ program tczipper;
 //Define this if you want to inspect the generated zips etc
 //Define this if you want to inspect the generated zips etc
 {$define KEEPTESTFILES}
 {$define KEEPTESTFILES}
 
 
-uses SysUtils, classes, zipper, unzip, zdeflate, zinflate, zip, md5, zstream, nullstream;
+uses
+  SysUtils, classes,
+  zipper, unzip, zdeflate, zinflate, zip, md5, zstream, nullstream;
 
 
 type
 type
 
 
@@ -45,7 +47,7 @@ type
 
 
 procedure TCallBackHandler.EndOfFile(Sender: TObject; const Ratio: double);
 procedure TCallBackHandler.EndOfFile(Sender: TObject; const Ratio: double);
 begin
 begin
-  writeln('End of file handler hit; ratio: '+floattostr(ratio));
+  writeln('End of file handler hit; compression ratio: '+floattostr(ratio));
   if (FPerformChecks) and (Ratio<0) then
   if (FPerformChecks) and (Ratio<0) then
   begin
   begin
     writeln('Found compression ratio '+floattostr(Ratio)+', which should never be lower than 0.');
     writeln('Found compression ratio '+floattostr(Ratio)+', which should never be lower than 0.');
@@ -120,9 +122,6 @@ var
   UnZipper: TUnZipper;
   UnZipper: TUnZipper;
 begin
 begin
   result:=true;
   result:=true;
-  UncompressedFile1:=SysUtils.GetTempFileName('', 'UNC');
-  UncompressedFile2:=SysUtils.GetTempFileName('', 'UNC');
-  CompressedFile:=SysUtils.GetTempFileName('', 'CC');
 
 
   FileContents:=TStringList.Create;
   FileContents:=TStringList.Create;
   OurZipper:=TZipper.Create;
   OurZipper:=TZipper.Create;
@@ -132,16 +131,22 @@ begin
     // Set up uncompressed files
     // Set up uncompressed files
     FileContents.Add('This is an uncompressed file.');
     FileContents.Add('This is an uncompressed file.');
     FileContents.Add('And another line.');
     FileContents.Add('And another line.');
+    UncompressedFile1:=SysUtils.GetTempFileName('', 'UN1');
     FileContents.SaveToFile(UncompressedFile1);
     FileContents.SaveToFile(UncompressedFile1);
     FileContents.Clear;
     FileContents.Clear;
     FileContents.Add('Have you looked into using fpcup today?');
     FileContents.Add('Have you looked into using fpcup today?');
     FileContents.Add('It works nicely with fpc and goes well with a fruity red wine, too.');
     FileContents.Add('It works nicely with fpc and goes well with a fruity red wine, too.');
+    // Second GetTempFileName call needs to be done after saving first file because
+    // GetTempFileName checks for existing file names and may give the *same* file name
+    // if called before
+    UncompressedFile2:=SysUtils.GetTempFileName('', 'UN2');
     FileContents.SaveToFile(UncompressedFile2);
     FileContents.SaveToFile(UncompressedFile2);
     // Remember their content, so we can compare later.
     // Remember their content, so we can compare later.
     UncompressedFile1Hash:=MD5Print(MD5File(UncompressedFile1, MDDefBufSize));
     UncompressedFile1Hash:=MD5Print(MD5File(UncompressedFile1, MDDefBufSize));
     UncompressedFile2Hash:=MD5Print(MD5File(UncompressedFile2, MDDefBufSize));
     UncompressedFile2Hash:=MD5Print(MD5File(UncompressedFile2, MDDefBufSize));
 
 
     // Test zip functionality.
     // Test zip functionality.
+    CompressedFile:=SysUtils.GetTempFileName('', 'CC');
     OurZipper.FileName:=CompressedFile;
     OurZipper.FileName:=CompressedFile;
     // Add the files only with their filenames, we don't want to create
     // Add the files only with their filenames, we don't want to create
     // subdirectories:
     // subdirectories:
@@ -153,7 +158,7 @@ begin
     if not FileExists(CompressedFile) then
     if not FileExists(CompressedFile) then
     begin
     begin
       writeln('Zip file was not created.');
       writeln('Zip file was not created.');
-      halt(5);
+      exit(false);
     end;
     end;
 
 
     // Delete original files
     // Delete original files
@@ -505,6 +510,50 @@ begin
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
+function SaveToFileTest: boolean;
+var
+  NewFileName: string;
+  OldFileName: string;
+  z: TZipper;
+  zfe: TZipFileEntry;
+  s: string = 'abcd';
+  DefaultStream: TStringStream;
+begin
+  result:=true;
+  OldFileName:=SysUtils.GetTempFileName('', 'OLD');
+  NewFileName:=SysUtils.GetTempFileName('', 'NEW');
+  z:=TZipper.Create;
+  z.FileName:=OldFileName;
+  try
+    DefaultStream:=TStringStream.Create(s);
+    zfe:=z.Entries.AddFileEntry(DefaultStream, 'Compressed');
+    z.ZipAllFiles; //saves to OldFileName
+    DeleteFile(NewFileName); //delete if present
+    z.SaveToFile(NewFileName); //should save to newfilename
+    if not(FileExists(NewFileName)) then
+    begin
+      writeln('Failure: file '+NewFileName+' does not exist.');
+      result:=false;
+    end
+    else
+    begin
+      result:=true;
+    end;
+  finally
+    DefaultStream.Free;
+    z.Free;
+  end;
+
+  {$IFNDEF KEEPTESTFILES}
+  try
+    DeleteFile(DestFile);
+  except
+    // ignore mess
+  end;
+  {$ENDIF}
+end;
+
+
 
 
 function TestLargeFileName: boolean;
 function TestLargeFileName: boolean;
 // Zips/unzips 259-character filename
 // Zips/unzips 259-character filename
@@ -557,17 +606,77 @@ begin
   {$ENDIF}
   {$ENDIF}
 end;
 end;
 
 
+function TestWindowsPath: boolean;
+// Zips filename in a subdirectory with a \ used as separator
+// Zip standard requires using /
+// On Linux, \ should be seen as a regular part of the filename
+var
+  FileWithBackslash: string;
+  DestFile: string;
+  s: string = 'a';
+  DefaultStream: TStringStream;
+  UnZipper: TUnZipper;
+  Zipper: TZipper;
+begin
+  result:=true;
+  FileWithBackslash:='test\afile.txt'; //on Windows, zip should handle this and internally replace \ with /
+  // On *nix, this should just be a long file
+  DestFile:=SysUtils.GetTempFileName('', 'TW');
+  Zipper:=TZipper.Create;
+  Zipper.FileName:=DestFile;
+  try
+    DefaultStream:=TStringStream.Create(s);
+    Zipper.Entries.AddFileEntry(DefaultStream, FileWithBackslash);
+    Zipper.ZipAllFiles;
+  finally
+    DefaultStream.Free;
+    Zipper.Free;
+  end;
+
+  UnZipper:=TUnZipper.Create;
+  try
+    UnZipper.FileName:=DestFile;
+    Unzipper.Examine;
+    {$ifdef mswindows}
+    if (pos('\',Unzipper.Entries[0].ArchiveFileName)>0) then
+    begin
+      result:=false;
+      writeln('Failed: found \ in archive filename; expected /:');
+      writeln('*'+Unzipper.Entries[0].ArchiveFileName+'*');
+      exit;
+    end;
+    {$else}
+    if (pos('\',Unzipper.Entries[0].ArchiveFileName)<=0) then
+    begin
+      result:=false;
+      writeln('Failed: did not find / in archive filename:');
+      writeln('*'+Unzipper.Entries[0].ArchiveFileName+'*');
+      exit;
+    end;
+    {$endif}
+  finally
+    Unzipper.Free;
+  end;
+
+  {$IFNDEF KEEPTESTFILES}
+  try
+    DeleteFile(DestFile);
+  except
+    // ignore mess
+  end;
+  {$ENDIF}
+end;
+
+
 function TestLargeZip64: boolean;
 function TestLargeZip64: boolean;
 // Tests single zip file with large uncompressed content
 // Tests single zip file with large uncompressed content
 // which forces it to zip64 format
 // which forces it to zip64 format
 var
 var
   ArchiveFile: string;
   ArchiveFile: string;
-  Buffer: PChar;
   DestFile: string;
   DestFile: string;
   ContentStream: TNullStream; //empty contents
   ContentStream: TNullStream; //empty contents
   UnZipper: TUnZipper;
   UnZipper: TUnZipper;
   Zipper: TZipper;
   Zipper: TZipper;
-  i: int64;
 begin
 begin
   result:=true;
   result:=true;
   DestFile:=SysUtils.GetTempFileName('', 'LZ');
   DestFile:=SysUtils.GetTempFileName('', 'LZ');
@@ -638,27 +747,98 @@ begin
     end;
     end;
 
 
     writeln('CompareCompressDecompress started');
     writeln('CompareCompressDecompress started');
-    if not(CompareCompressDecompress) then code:=code+2; //1 already taken by callback handler
+    try
+      if not(CompareCompressDecompress) then code:=code+2; //1 already taken by callback handler
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+2;
+      end;
+    end;
     writeln('CompareCompressDecompress finished');
     writeln('CompareCompressDecompress finished');
     writeln('');
     writeln('');
+
     writeln('CompressSmallStreams started');
     writeln('CompressSmallStreams started');
-    if not(CompressSmallStreams) then code:=code+4;
+    try
+      if not(CompressSmallStreams) then code:=code+4;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+4;
+      end;
+    end;
     writeln('CompressSmallStreams finished');
     writeln('CompressSmallStreams finished');
     writeln('');
     writeln('');
+
     writeln('TestZipEntries(2) started');
     writeln('TestZipEntries(2) started');
-    if not(TestZipEntries(2)) then code:=code+8;
+    try
+      if not(TestZipEntries(2)) then code:=code+8;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+8;
+      end;
+    end;
     writeln('TestZipEntries(2) finished');
     writeln('TestZipEntries(2) finished');
     writeln('');
     writeln('');
+
     writeln('TestLargeFileName started');
     writeln('TestLargeFileName started');
-    if not(TestLargeFileName) then code:=code+16;
+    try
+      if not(TestLargeFileName) then code:=code+16;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+16;
+      end;
+    end;
     writeln('TestLargeFileName finished');
     writeln('TestLargeFileName finished');
     writeln('');
     writeln('');
+
+    writeln('TestWindowsPath started');
+    try
+      if not(TestWindowsPath) then code:=code+32;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+32;
+      end;
+    end;
+    writeln('TestWindowsPath finished');
+    writeln('');
+
     writeln('TestEmptyZipEntries(10) started');
     writeln('TestEmptyZipEntries(10) started');
     // Run testemptyzipentries with a small number to test the test itself... as
     // Run testemptyzipentries with a small number to test the test itself... as
     // well as zip structure generated with empty files.
     // well as zip structure generated with empty files.
-    if not(TestEmptyZipEntries(10)) then code:=code+32;
+    try
+      if not(TestEmptyZipEntries(10)) then code:=code+64;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+64;
+      end;
+    end;
     writeln('TestEmptyZipEntries(10) finished');
     writeln('TestEmptyZipEntries(10) finished');
     writeln('');
     writeln('');
+
+    writeln('SaveToFileTest started');
+    try
+      if not(SaveToFileTest) then code:=code+128;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+128;
+      end;
+    end;
+    writeln('SaveToFileTest finished');
+    writeln('');
+
     writeln('TestEmptyZipEntries(65537) started');
     writeln('TestEmptyZipEntries(65537) started');
     writeln('(note: this will take a long time)');
     writeln('(note: this will take a long time)');
     {Note: tested tools with this file:
     {Note: tested tools with this file:
@@ -666,9 +846,18 @@ begin
     - Ionic's DotNetZip library unzip.exe utility verison 1.9.1.8 works
     - Ionic's DotNetZip library unzip.exe utility verison 1.9.1.8 works
     - 7zip's 7za 9.22 beta works.
     - 7zip's 7za 9.22 beta works.
     }
     }
-    if not(TestEmptyZipEntries(65537)) then code:=code+32;
+    try
+      if not(TestEmptyZipEntries(65537)) then code:=code+256;
+    except
+      On E: Exception do
+      begin
+        writeln('Exception: '+E.Message);
+        code:=code+256;
+      end;
+    end;
     writeln('TestEmptyZipEntries(65537) finished');
     writeln('TestEmptyZipEntries(65537) finished');
     writeln('');
     writeln('');
+
     { This test will take a very long time as it tries to zip a 4Gb memory block.
     { This test will take a very long time as it tries to zip a 4Gb memory block.
     It is therefore commented out by default }
     It is therefore commented out by default }
     {
     {
@@ -684,6 +873,7 @@ begin
       writeln('Exception: ');
       writeln('Exception: ');
       writeln(E.Message);
       writeln(E.Message);
       writeln('');
       writeln('');
+      if code=0 then code:=maxint; //more or less random error code
     end;
     end;
   end;
   end;