Browse Source

--- Merging r22138 into '.':
A packages/paszlib/tests
A packages/paszlib/tests/tconend.pp
A packages/paszlib/tests/tczipper.pp
--- Merging r22284 into '.':
U packages/paszlib/src/zipper.pp

# revisions: 22138,22284
r22138 | michael | 2012-08-20 00:08:32 +0200 (Mon, 20 Aug 2012) | 1 line
Changed paths:
A /trunk/packages/paszlib/tests
A /trunk/packages/paszlib/tests/tconend.pp
A /trunk/packages/paszlib/tests/tczipper.pp

* 2 test cases from Reinier Olislagers
r22284 | nickysn | 2012-09-02 18:53:59 +0200 (Sun, 02 Sep 2012) | 1 line
Changed paths:
M /trunk/packages/paszlib/src/zipper.pp

+ TUnZipper: added support for zip file comments

git-svn-id: branches/fixes_2_6@22544 -

marco 13 years ago
parent
commit
08bdc44dd2
4 changed files with 363 additions and 13 deletions
  1. 2 0
      .gitattributes
  2. 65 13
      packages/paszlib/src/zipper.pp
  3. 152 0
      packages/paszlib/tests/tconend.pp
  4. 144 0
      packages/paszlib/tests/tczipper.pp

+ 2 - 0
.gitattributes

@@ -5673,6 +5673,8 @@ packages/paszlib/src/zipper.pp svneol=native#text/plain
 packages/paszlib/src/ziputils.pas svneol=native#text/plain
 packages/paszlib/src/ziputils.pas svneol=native#text/plain
 packages/paszlib/src/zstream.pp svneol=native#text/plain
 packages/paszlib/src/zstream.pp svneol=native#text/plain
 packages/paszlib/src/zuncompr.pas svneol=native#text/plain
 packages/paszlib/src/zuncompr.pas svneol=native#text/plain
+packages/paszlib/tests/tconend.pp svneol=native#text/plain
+packages/paszlib/tests/tczipper.pp svneol=native#text/plain
 packages/pcap/Makefile svneol=native#text/plain
 packages/pcap/Makefile svneol=native#text/plain
 packages/pcap/Makefile.fpc svneol=native#text/plain
 packages/pcap/Makefile.fpc svneol=native#text/plain
 packages/pcap/fpmake.pp svneol=native#text/plain
 packages/pcap/fpmake.pp svneol=native#text/plain

+ 65 - 13
packages/paszlib/src/zipper.pp

@@ -396,6 +396,7 @@ Type
     FBufSize    : LongWord;
     FBufSize    : LongWord;
     FFileName   :  String;         { Name of resulting Zip file                 }
     FFileName   :  String;         { Name of resulting Zip file                 }
     FOutputPath : String;
     FOutputPath : String;
+    FFileComment: String;
     FEntries    : TFullZipFileEntries;
     FEntries    : TFullZipFileEntries;
     FFiles      : TStrings;
     FFiles      : TStrings;
     FZipStream  : TStream;     { I/O file variables                         }
     FZipStream  : TStream;     { I/O file variables                         }
@@ -441,6 +442,7 @@ Type
     Property OnEndFile : TOnEndOfFileEvent Read FOnEndOfFile Write FOnEndOfFile;
     Property OnEndFile : TOnEndOfFileEvent Read FOnEndOfFile Write FOnEndOfFile;
     Property FileName : String Read FFileName Write SetFileName;
     Property FileName : String Read FFileName Write SetFileName;
     Property OutputPath : String Read FOutputPath Write SetOutputPath;
     Property OutputPath : String Read FOutputPath Write SetOutputPath;
+    Property FileComment: String Read FFileComment;
     Property Files : TStrings Read FFiles;
     Property Files : TStrings Read FFiles;
     Property Entries : TFullZipFileEntries Read FEntries;
     Property Entries : TFullZipFileEntries Read FEntries;
   end;
   end;
@@ -1693,31 +1695,81 @@ Begin
     end;
     end;
 End;
 End;
 
 
+procedure FindEndHeader(AZip: TStream; out AEndHdr: End_of_Central_Dir_Type; out AEndHdrPos: Int64; out AZipFileComment: string);
+var
+  Buf: PByte;
+  BufSize: Integer;
+  I: Integer;
+begin
+  AZipFileComment := '';
+  AEndHdrPos := AZip.Size - SizeOf(AEndHdr);
+  if AEndHdrPos < 0 then
+  begin
+    AEndHdrPos := -1;
+    FillChar(AEndHdr, SizeOf(AEndHdr), 0);
+    exit;
+  end;
+  AZip.Seek(AEndHdrPos, soFromBeginning);
+  AZip.ReadBuffer(AEndHdr, SizeOf(AEndHdr));
+  {$IFDEF FPC_BIG_ENDIAN}
+  AEndHdr := SwapECD(AEndHdr);
+  {$ENDIF}
+  if (AEndHdr.Signature = END_OF_CENTRAL_DIR_SIGNATURE) and
+     (AEndHdr.ZipFile_Comment_Length = 0) then
+    exit;
+
+  // scan the last (64k + something) bytes for the END_OF_CENTRAL_DIR_SIGNATURE
+  // (zip file comments are 64k max)
+  BufSize := 65536 + SizeOf(AEndHdr) + 128;
+  if AZip.Size < BufSize then
+    BufSize := AZip.Size;
+
+  Buf := GetMem(BufSize);
+  try
+    AZip.Seek(AZip.Size - BufSize, soFromBeginning);
+    AZip.ReadBuffer(Buf^, BufSize);
+
+    for I := BufSize - SizeOf(AEndHdr) downto 0 do
+    begin
+      if (Buf[I] or (Buf[I + 1] shl 8) or (Buf[I + 2] shl 16) or (Buf[I + 3] shl 24)) = END_OF_CENTRAL_DIR_SIGNATURE then
+      begin
+        Move(Buf[I], AEndHdr, SizeOf(AEndHdr));
+        {$IFDEF FPC_BIG_ENDIAN}
+        AEndHdr := SwapECD(AEndHdr);
+        {$ENDIF}
+        if (AEndHdr.Signature = END_OF_CENTRAL_DIR_SIGNATURE) and
+           (I + SizeOf(AEndHdr) + AEndHdr.ZipFile_Comment_Length = BufSize) then
+        begin
+          AEndHdrPos := AZip.Size - BufSize + I;
+          AZip.Seek(AEndHdrPos + SizeOf(AEndHdr), soFromBeginning);
+          SetLength(AZipFileComment, AEndHdr.ZipFile_Comment_Length);
+          AZip.ReadBuffer(AZipFileComment[1], Length(AZipFileComment));
+          exit;
+        end;
+      end;
+    end;
+
+    AEndHdrPos := -1;
+    FillChar(AEndHdr, SizeOf(AEndHdr), 0);
+  finally
+    FreeMem(Buf);
+  end;
+end;
 
 
 Procedure TUnZipper.ReadZipDirectory;
 Procedure TUnZipper.ReadZipDirectory;
 
 
 Var
 Var
   i : LongInt;
   i : LongInt;
   EndHdrPos,
   EndHdrPos,
-  CenDirPos : LongInt;
+  CenDirPos : Int64;
   NewNode   : TFullZipFileEntry;
   NewNode   : TFullZipFileEntry;
   D : TDateTime;
   D : TDateTime;
   S : String;
   S : String;
 Begin
 Begin
-  EndHdrPos:=FZipStream.Size-SizeOf(EndHdr);
+  FindEndHeader(FZipStream, EndHdr, EndHdrPos, FFileComment);
   if EndHdrPos < 0 then
   if EndHdrPos < 0 then
     raise EZipError.CreateFmt(SErrCorruptZIP,[FileName]);
     raise EZipError.CreateFmt(SErrCorruptZIP,[FileName]);
-  FZipStream.Seek(EndHdrPos,soFromBeginning);
-  FZipStream.ReadBuffer(EndHdr, SizeOf(EndHdr));
-{$IFDEF FPC_BIG_ENDIAN}
-  EndHdr := SwapECD(EndHdr);
-{$ENDIF}
-  With EndHdr do
-    begin
-    if Signature <> END_OF_CENTRAL_DIR_SIGNATURE then
-      raise EZipError.CreateFmt(SErrCorruptZIP,[FileName]);
-    CenDirPos:=Start_Disk_Offset;
-    end;
+  CenDirPos := EndHdr.Start_Disk_Offset;
   FZipStream.Seek(CenDirPos,soFrombeginning);
   FZipStream.Seek(CenDirPos,soFrombeginning);
   FEntries.Clear;
   FEntries.Clear;
   for i:=0 to EndHdr.Entries_This_Disk-1 do
   for i:=0 to EndHdr.Entries_This_Disk-1 do

+ 152 - 0
packages/paszlib/tests/tconend.pp

@@ -0,0 +1,152 @@
+{
+    This file is part of the Free Pascal packages.
+    Copyright (c) 1999-2012 by the Free Pascal development team
+
+    Tests zip/unzip functionality provided by the FPC zipper.pp unit.
+    Created by Reinier Olislagers
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+program tconend;
+ 
+{$mode objfpc}{$h+}
+
+uses SysUtils, classes, zipper, md5;
+
+type
+  TCallBackHandler = class(TObject)
+  public
+    procedure EndOfFile(Sender:TObject; const Ratio:double);
+    procedure StartOfFile(Sender:TObject; const AFileName:string);
+  end;
+
+
+procedure TCallBackHandler.EndOfFile(Sender : TObject; Const Ratio : Double);
+begin
+  writeln('End of file handler hit; ratio: '+floattostr(ratio));
+  if (Ratio<0) then
+  begin
+    writeln('Found compression ratio '+floattostr(Ratio)+', which should never be lower than 0.');
+    halt(3);
+  end;
+end;
+
+procedure TCallBackHandler.StartOfFile(Sender : TObject; Const AFileName : String);
+begin
+  writeln('Start of file handler hit; filename: '+AFileName);
+  if AFileName='' then
+  begin
+    writeln('Archive filename should not be empty.');
+    halt(4);
+  end;
+end;
+
+var
+  code: cardinal;
+  CallBackHandler: TCallBackHandler;
+  CompressedFile: string;
+  FileContents: TStringList;
+  UncompressedFile1: string;
+  UncompressedFile1Hash: string;
+  UncompressedFile2: string;
+  UncompressedFile2Hash: string;
+  OurZipper: TZipper;
+  UnZipper: TUnZipper;
+begin
+  code := 0;
+  UncompressedFile1:=SysUtils.GetTempFileName('', 'UNC');
+  UncompressedFile2:=SysUtils.GetTempFileName('', 'UNC');
+  CompressedFile:=SysUtils.GetTempFileName('', 'ZP');
+
+  FileContents:=TStringList.Create;
+  OurZipper:=TZipper.Create;
+  UnZipper:=TUnZipper.Create;
+  CallBackHandler:=TCallBackHandler.Create;
+  try
+    // Set up uncompressed files
+    FileContents.Add('This is an uncompressed file.');
+    FileContents.Add('And another line.');
+    FileContents.SaveToFile(UncompressedFile1);
+    FileContents.Clear;
+    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.SaveToFile(UncompressedFile2);
+    // Remember their content, so we can compare later.
+    UncompressedFile1Hash:=MD5Print(MD5File(UncompressedFile1, MDDefBufSize));
+    UncompressedFile2Hash:=MD5Print(MD5File(UncompressedFile2, MDDefBufSize));
+
+    // Test zip functionality.
+    writeln('Beginning zip test');
+    OurZipper.FileName:=CompressedFile;
+    // Add the files only with their filenames, we don't want to create
+    // subdirectories:
+    OurZipper.Entries.AddFileEntry(UncompressedFile1,ExtractFileName(UncompressedFile1));
+    OurZipper.Entries.AddFileEntry(UncompressedFile2,ExtractFileName(UncompressedFile2));
+    OurZipper.OnStartFile:[email protected];
+    OurZipper.OnEndFile:[email protected];
+    OurZipper.ZipAllFiles;
+    if not FileExists(CompressedFile) then
+    begin
+      writeln('Zip file was not created.');
+      halt(5);
+    end;
+    writeln('Zip test done');
+
+    // Delete original files
+    DeleteFile(UncompressedFile1);
+    DeleteFile(UncompressedFile2);
+
+    // Now unzip
+    writeln('Beginning unzip test');
+    Unzipper.FileName:=CompressedFile;
+    Unzipper.OutputPath:=ExtractFilePath(UncompressedFile1);
+    UnZipper.OnStartFile:[email protected];
+    UnZipper.OnEndFile:[email protected];
+    Unzipper.Examine;
+    Unzipper.UnZipAllFiles;
+
+    // Now we should have the uncompressed files again
+    if (not FileExists(UncompressedFile1)) or
+      (not FileExists(UncompressedFile2)) then
+    begin
+      writeln('Unzip failed: could not find decompressed files.');
+      halt(6);
+    end;
+
+    // Compare hashes
+    if
+      (UncompressedFile1Hash<>MD5Print(MD5File(UncompressedFile1, MDDefBufSize)))
+      or
+      (UncompressedFile2Hash<>MD5Print(MD5File(UncompressedFile2, MDDefBufSize)))
+    then
+    begin
+      writeln('Unzip failed: uncompressed files are not the same as the originals.');
+      halt(7);
+    end;
+    writeln('Unzip test done');
+
+    if code = 0 then
+      writeln('Basic zip/unzip tests passed')
+    else
+      writeln('Basic zip/unzip test failed: ', code);
+  finally
+    FileContents.Free;
+    CallBackHandler.Free;
+    OurZipper.Free;
+    UnZipper.Free;
+    try
+      if FileExists(CompressedFile) then DeleteFile(CompressedFile);
+      if FileExists(UncompressedFile1) then DeleteFile(UncompressedFile1);
+      if FileExists(UncompressedFile2) then DeleteFile(UncompressedFile2);
+    finally
+      // Ignore errors; operating system should clean out temp files
+    end; 
+  end;
+  Halt(code);
+end.

+ 144 - 0
packages/paszlib/tests/tczipper.pp

@@ -0,0 +1,144 @@
+program tczipper;
+{
+    This file is part of the Free Pascal packages.
+    Copyright (c) 1999-2012 by the Free Pascal development team
+
+    Tests zip/unzip functionality provided by the FPC zipper.pp unit.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$mode objfpc}{$h+}
+
+uses SysUtils, classes, zipper, md5;
+
+type
+  TCallBackHandler = class(TObject)
+  public
+    procedure EndOfFile(Sender:TObject; const Ratio:double);
+    procedure StartOfFile(Sender:TObject; const AFileName:string);
+  end;
+
+
+procedure TCallBackHandler.EndOfFile(Sender : TObject; Const Ratio : Double);
+begin
+  if (Ratio<0) then
+  begin
+    writeln('Found compression ratio '+floattostr(Ratio)+', which should never be lower than 0.');
+    halt(3);
+  end;
+end;
+
+procedure TCallBackHandler.StartOfFile(Sender : TObject; Const AFileName : String);
+begin
+  if AFileName='' then
+  begin
+    writeln('Archive filename should not be empty.');
+    halt(4);
+  end;
+end;
+
+var
+  code: cardinal;
+  CallBackHandler: TCallBackHandler;
+  CompressedFile: string;
+  FileContents: TStringList;
+  UncompressedFile1: string;
+  UncompressedFile1Hash: string;
+  UncompressedFile2: string;
+  UncompressedFile2Hash: string;
+  OurZipper: TZipper;
+  UnZipper: TUnZipper;
+begin
+  code := 0;
+  UncompressedFile1:=SysUtils.GetTempFileName('', 'UNC');
+  UncompressedFile2:=SysUtils.GetTempFileName('', 'UNC');
+  CompressedFile:=SysUtils.GetTempFileName('', 'ZP');
+
+  FileContents:=TStringList.Create;
+  OurZipper:=TZipper.Create;
+  UnZipper:=TUnZipper.Create;
+  CallBackHandler:=TCallBackHandler.Create;
+  try
+    // Set up uncompressed files
+    FileContents.Add('This is an uncompressed file.');
+    FileContents.Add('And another line.');
+    FileContents.SaveToFile(UncompressedFile1);
+    FileContents.Clear;
+    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.SaveToFile(UncompressedFile2);
+    // Remember their content, so we can compare later.
+    UncompressedFile1Hash:=MD5Print(MD5File(UncompressedFile1, MDDefBufSize));
+    UncompressedFile2Hash:=MD5Print(MD5File(UncompressedFile2, MDDefBufSize));
+
+    // Test zip functionality.
+    OurZipper.FileName:=CompressedFile;
+    // Add the files only with their filenames, we don't want to create
+    // subdirectories:
+    OurZipper.Entries.AddFileEntry(UncompressedFile1,ExtractFileName(UncompressedFile1));
+    OurZipper.Entries.AddFileEntry(UncompressedFile2,ExtractFileName(UncompressedFile2));
+    OurZipper.OnStartFile:[email protected];
+    OurZipper.OnEndFile:[email protected];
+    OurZipper.ZipAllFiles;
+    if not FileExists(CompressedFile) then
+    begin
+      writeln('Zip file was not created.');
+      halt(5);
+    end;
+
+    // Delete original files
+    DeleteFile(UncompressedFile1);
+    DeleteFile(UncompressedFile2);
+
+    // Now unzip
+    Unzipper.FileName:=CompressedFile;
+    Unzipper.OutputPath:=ExtractFilePath(UncompressedFile1);
+    UnZipper.OnStartFile:[email protected];
+    UnZipper.OnEndFile:[email protected];
+    Unzipper.Examine;
+    Unzipper.UnZipAllFiles;
+
+    // Now we should have the uncompressed files again
+    if (not FileExists(UncompressedFile1)) or
+      (not FileExists(UncompressedFile2)) then
+    begin
+      writeln('Unzip failed: could not find decompressed files.');
+      halt(6);
+    end;
+
+    // Compare hashes
+    if
+      (UncompressedFile1Hash<>MD5Print(MD5File(UncompressedFile1, MDDefBufSize)))
+      or
+      (UncompressedFile2Hash<>MD5Print(MD5File(UncompressedFile2, MDDefBufSize)))
+    then
+    begin
+      writeln('Unzip failed: uncompressed files are not the same as the originals.');
+      halt(7);
+    end;
+
+    if code = 0 then
+      writeln('Basic zip/unzip tests passed')
+    else
+      writeln('Basic zip/unzip test failed: ', code);
+  finally
+    FileContents.Free;
+    CallBackHandler.Free;
+    OurZipper.Free;
+    UnZipper.Free;
+    try
+      if FileExists(CompressedFile) then DeleteFile(CompressedFile);
+      if FileExists(UncompressedFile1) then DeleteFile(UncompressedFile1);
+      if FileExists(UncompressedFile2) then DeleteFile(UncompressedFile2);
+    finally
+      // Ignore errors; operating system should clean out temp files
+    end; 
+  end;
+  Halt(code);
+end.