Back to... Zip-Ada

Source file : zip-create.adb


with Ada.Unchecked_Deallocation;
with Ada.Text_IO;
with Interfaces;                        use Interfaces;

package body Zip.Create is

   procedure Create(Info        : out Zip_Create_info;
                    Z_Stream    : in Zipstream_Class;
                    Name        : String;
                    Compress    : Zip.Compress.Compression_Method:= Zip.Compress.Shrink) is
   begin
      Info.Stream := Z_Stream;
      Info.Compress := Compress;
      if Name /= "" then
         SetName (Info.Stream, Name);
      end if;
      --
      -- If we have a real file (ZipFile_Stream or descendent), create the file too:
      --
      if Z_Stream.all in ZipFile_Stream'Class then
        Zip_Streams.Create (ZipFile_Stream(Z_Stream.all), Ada.Streams.Stream_IO.Out_File);
      end if;
   end Create;

   procedure Dispose is new
     Ada.Unchecked_Deallocation (Dir_entries, Pdir_entries);

   procedure Resize (A    : in out Pdir_entries;
                     Size : Integer) is
      Hlp : constant Pdir_entries := new Dir_entries (1 .. Size);
   begin
      if A = null then
         A := Hlp;
      else
         Hlp (1 .. Integer'Min (Size, A'Length)) :=
           A (1 .. Integer'Min (Size, A'Length));
         Dispose (A);
         A := Hlp;
      end if;
   end Resize;

   procedure Add_Stream (Info           : in out Zip_Create_info;
                         Stream         : Zipstream_Class)
   is
     Compressed_Size: Zip.File_size_type; -- dummy
     Final_Method   : Natural;            -- dummy
   begin
     Add_Stream(Info, Stream, null, Compressed_Size, Final_Method);
   end Add_Stream;

   procedure Add_Stream (Info           : in out Zip_Create_info;
                         Stream         : Zipstream_Class;
                         Feedback       : in     Feedback_proc;
                         Compressed_Size:    out Zip.File_size_type;
                         Final_Method   :    out Natural)
   is
      Last       : Integer := 1;
      mem1, mem2 : Integer := 1;
   begin
      if Info.Contains /= null then
         Last := Info.Contains'Length + 1;
      end if;
      Resize (Info.Contains, Last);
      --  Administration
      Info.Contains (Last).head.made_by_version      := 23; -- version 2.30
      Info.Contains (Last).head.comment_length       := 0;
      Info.Contains (Last).head.disk_number_start    := 0;
      Info.Contains (Last).head.internal_attributes  := 0; -- 0:binary; 1:text
      Info.Contains (Last).head.external_attributes               := 0;
      Info.Contains (Last).head.short_info.needed_extract_version :=
        10; -- Value put by Zip/PKZip
      Info.Contains (Last).head.short_info.bit_flag               := 0;
      Info.Contains (Last).head.short_info.file_timedate          :=
        GetTime (Stream);
      Info.Contains (Last).head.short_info.dd.uncompressed_size   :=
        Unsigned_32 (Size (Stream));
      Info.Contains (Last).head.short_info.filename_length        :=
        GetName (Stream)'Length;
      Info.Contains (Last).head.short_info.extra_field_length     := 0;
      Info.Contains (Last).name := To_Unbounded_String (GetName (Stream));

      mem1 := Index (Info.Stream);
      Info.Contains (Last).head.local_header_offset := Unsigned_32 (mem1) - 1;
      --  Write the local header with incomplete informations
      Zip.Headers.Write (Info.Stream, Info.Contains (Last).head.short_info);

      String'Write(Info.Stream, To_String (Info.Contains (Last).name));

      --  Write compressed file
      Zip.Compress.Compress_data
        (input            => Stream,
         output           => Info.Stream,
         input_size_known => True,
         input_size       =>
           Info.Contains (Last).head.short_info.dd.uncompressed_size,
         method           => Info.Compress,
         feedback         => Feedback,
         CRC              => Info.Contains (Last).head.short_info.dd.crc_32,
         output_size      => Info.Contains (Last).head.short_info.dd.compressed_size,
         zip_type         => Info.Contains (Last).head.short_info.zip_type
      );
      mem2 := Index (Info.Stream);
      --  Go back to the local header to rewrite it
      --  with complete informations
      Set_Index (Info.Stream, mem1);
      Zip.Headers.Write (Info.Stream, Info.Contains (Last).head.short_info);
      --  Return to momentaneous end of file
      Set_Index (Info.Stream, mem2);
      --
      Compressed_Size:= Info.Contains (Last).head.short_info.dd.compressed_size;
      Final_Method   := Natural(Info.Contains (Last).head.short_info.zip_type);
   end Add_Stream;

   procedure Add_File (Info              : in out Zip_Create_info;
                       Name              : String;
                       Name_in_archive   : String:= "";
                       -- default: add the file in the archive
                       -- under the same name
                       Delete_file_after : Boolean:= False
                       -- practical to delete temporary file after
                       -- adding
   )
   is
      temp_zip_stream     : aliased ZipFile_Stream;
      acc_temp_zip_stream : constant Zipstream_Class := temp_zip_stream'Unchecked_Access;
      use Ada.Text_IO;
      fd: File_Type;
   begin
     -- Read the file
     SetName(acc_temp_zip_stream, Name);
     Open(temp_zip_stream, Ada.Streams.Stream_IO.In_File);
     -- Eventually we set a new name for archiving:
     if Name_in_archive /= "" then
        SetName(acc_temp_zip_stream, Name_in_archive);
     end if;
     -- Stuff into the .zip archive:
     Add_Stream (Info, acc_temp_zip_stream);
     Close(temp_zip_stream);
     if Delete_file_after then
        Open(fd, In_File, Name);
        Delete(fd);
     end if;
   end Add_File;

   procedure Add_String (Info            : in out Zip_Create_info;
                         Contents        : String;
                         Name_in_archive : String
   )
   is
   begin
     Add_String(Info, To_Unbounded_String(Contents), Name_in_archive);
   end Add_String;

   procedure Add_String (Info            : in out Zip_Create_info;
                         Contents        : Unbounded_String;
                         Name_in_archive : String
   )
   is
     temp_zip_stream     : aliased Unbounded_Stream;
     acc_temp_zip_stream : constant Zipstream_Class := temp_zip_stream'Unchecked_Access;
   begin
     Set(temp_zip_stream, Contents);
     SetName(acc_temp_zip_stream, Name_in_archive);
     Add_Stream (Info, acc_temp_zip_stream);
   end Add_String;

   procedure Finish (Info : in out Zip_Create_info) is
      ed : Zip.Headers.End_of_Central_Dir;
   begin
      --
      --  2/ Almost done - write Central Directory:
      --
      ed.central_dir_offset := Unsigned_32 (Index (Info.Stream)) - 1;
      ed.total_entries := 0;
      ed.central_dir_size := 0;
      ed.main_comment_length := 0;
      for e in Info.Contains'Range loop
         ed.total_entries := ed.total_entries + 1;
         Zip.Headers.Write (Info.Stream, Info.Contains (e).head);
         String'Write(Info.Stream, To_String (Info.Contains (e).name));
         ed.central_dir_size :=
           ed.central_dir_size +
             Zip.Headers.central_header_length +
               Unsigned_32 (Info.Contains (e).head.short_info.filename_length);
      end loop;
      Dispose (Info.Contains);
      ed.disknum := 0;
      ed.disknum_with_start := 0;
      ed.disk_total_entries := ed.total_entries;
      Zip.Headers.Write (Info.Stream, ed);
      --
      -- If we have a real file (ZipFile_Stream or descendent), close the file too:
      --
      if Info.Stream.all in ZipFile_Stream'Class then
        Zip_Streams.Close (ZipFile_Stream(Info.Stream.all));
      end if;
   end Finish;

end Zip.Create;

Zip-Ada: Ada library for zip archive files (.zip). Ada programming.