Back to... Zip-Ada

Source file : zip_streams.adb



   1  --  Legal licensing note:
   2  
   3  --  Copyright (c) 2008 .. 2023 Gautier de Montmollin (maintainer)
   4  --  SWITZERLAND
   5  
   6  --  Permission is hereby granted, free of charge, to any person obtaining a copy
   7  --  of this software and associated documentation files (the "Software"), to deal
   8  --  in the Software without restriction, including without limitation the rights
   9  --  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10  --  copies of the Software, and to permit persons to whom the Software is
  11  --  furnished to do so, subject to the following conditions:
  12  
  13  --  The above copyright notice and this permission notice shall be included in
  14  --  all copies or substantial portions of the Software.
  15  
  16  --  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17  --  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18  --  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19  --  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20  --  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21  --  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22  --  THE SOFTWARE.
  23  
  24  --  NB: this is the MIT License, as found 21-Aug-2016 on the site
  25  --  http://www.opensource.org/licenses/mit-license.php
  26  
  27  ----------------
  28  --  Some changes
  29  --
  30  --  11-Nov-2009 (GdM): Unbounded_Stream.Write and .Set_Index are buffered
  31  --  18-Jan-2009 (GdM): Fixed Read(Stream, Item...) which read
  32  --                       only 1st element of Item
  33  
  34  package body Zip_Streams is
  35  
  36     use Ada.Strings.Unbounded;
  37  
  38     procedure Set_Name (S : in out Root_Zipstream_Type; Name : String) is
  39     begin
  40        S.Name := To_Unbounded_String (Name);
  41     end Set_Name;
  42  
  43     function Get_Name (S : in Root_Zipstream_Type) return String is
  44     begin
  45        return To_String (S.Name);
  46     end Get_Name;
  47  
  48     procedure Set_Time (S : in out Root_Zipstream_Type; Modification_Time : Time) is
  49     begin
  50        S.Modification_Time := Modification_Time;
  51     end Set_Time;
  52  
  53     function Get_Time (S : in Root_Zipstream_Type) return Time is
  54     begin
  55        return S.Modification_Time;
  56     end Get_Time;
  57  
  58     --  Ada.Calendar versions
  59  
  60     procedure Set_Time (S : in out Root_Zipstream_Type'Class;
  61                         Modification_Time : Ada.Calendar.Time) is
  62     begin
  63       Set_Time (S, Calendar.Convert (Modification_Time));
  64     end Set_Time;
  65  
  66     function Get_Time (S : in Root_Zipstream_Type'Class)
  67                        return Ada.Calendar.Time is
  68     begin
  69       return Calendar.Convert (Get_Time (S));
  70     end Get_Time;
  71  
  72     procedure Set_Unicode_Name_Flag (S     : out Root_Zipstream_Type;
  73                                      Value : in Boolean)
  74     is
  75     begin
  76       S.Is_Unicode_Name := Value;
  77     end Set_Unicode_Name_Flag;
  78  
  79     function Is_Unicode_Name (S : in Root_Zipstream_Type)
  80                               return Boolean
  81     is
  82     begin
  83       return S.Is_Unicode_Name;
  84     end Is_Unicode_Name;
  85  
  86     procedure Set_Read_Only_Flag (S     : out Root_Zipstream_Type;
  87                                   Value : in Boolean)
  88     is
  89     begin
  90       S.Is_Read_Only := Value;
  91     end Set_Read_Only_Flag;
  92  
  93     function Is_Read_Only (S : in Root_Zipstream_Type)
  94                            return Boolean
  95     is
  96     begin
  97       return S.Is_Read_Only;
  98     end Is_Read_Only;
  99  
 100     ---------------------------------------------------------------------
 101     -- Unbounded_Stream: stream based on an in-memory Unbounded_String --
 102     ---------------------------------------------------------------------
 103     procedure Get (Str : Memory_Zipstream; Unb : out Ada.Strings.Unbounded.Unbounded_String) is
 104     begin
 105        Unb := Str.Unb;
 106     end Get;
 107  
 108     procedure Set (Str : in out Memory_Zipstream; Unb : Ada.Strings.Unbounded.Unbounded_String) is
 109     begin
 110        Str.Unb := Null_Unbounded_String; -- clear the content of the stream
 111        Str.Unb := Unb;
 112        Str.Loc := 1;
 113     end Set;
 114  
 115     use Ada.Streams;
 116  
 117     overriding procedure Read
 118       (Stream : in out Memory_Zipstream;
 119        Item   :    out Ada.Streams.Stream_Element_Array;
 120        Last   :    out Ada.Streams.Stream_Element_Offset)
 121     is
 122     begin
 123        --  Item is read from the stream. If (and only if) the stream is
 124        --  exhausted, Last will be < Item'Last. In that case, T'Read will
 125        --  raise an End_Error exception.
 126        --
 127        --  Cf: RM 13.13.1(8), RM 13.13.1(11), RM 13.13.2(37) and
 128        --  explanations by Tucker Taft
 129        --
 130        Last := Item'First - 1;
 131        --  if Item is empty, the following loop is skipped; if Stream.Loc
 132        --  is already indexing out of Stream.Unb, that value is also appropriate
 133        for i in Item'Range loop
 134           Item (i) := Character'Pos (Element (Stream.Unb, Stream.Loc));
 135           Stream.Loc := Stream.Loc + 1;
 136           Last := i;
 137        end loop;
 138     exception
 139        when Ada.Strings.Index_Error =>
 140           null; -- what could be read has been read; T'Read will raise End_Error
 141     end Read;
 142  
 143     max_chunk_size : constant := 16 * 1024;
 144  
 145     overriding procedure Write
 146       (Stream : in out Memory_Zipstream;
 147        Item   :        Ada.Streams.Stream_Element_Array)
 148     is
 149       I : Stream_Element_Offset := Item'First;
 150       chunk_size : Integer;
 151       tmp : String (1 .. max_chunk_size);
 152     begin
 153       while I <= Item'Last loop
 154         chunk_size := Integer'Min (Integer (Item'Last - I + 1), max_chunk_size);
 155         if Stream.Loc > Length (Stream.Unb) then
 156           --  ...we are off the string's bounds, we need to extend it.
 157           for J in 1 .. chunk_size loop
 158             tmp (J) := Character'Val (Item (I));
 159             I := I + 1;
 160           end loop;
 161           Append (Stream.Unb, tmp (1 .. chunk_size));
 162         else
 163           --  ...we can work (at least for a part) within the string's bounds.
 164           chunk_size := Integer'Min (chunk_size, Length (Stream.Unb) - Stream.Loc + 1);
 165           for J in 0 .. chunk_size - 1 loop
 166             Replace_Element (Stream.Unb, Stream.Loc + J, Character'Val (Item (I)));
 167             --  GNAT 2008's Replace_Slice does something very general
 168             --  even in the trivial case where one can make:
 169             --  Source.Reference(Low..High):= By;
 170             --  -> still faster with elem by elem replacement
 171             --  Anyway, this place is not critical for zipping: only the
 172             --  local header before compressed data is rewritten after
 173             --  compression. So usually, we are off bounds.
 174             I := I + 1;
 175           end loop;
 176         end if;
 177         Stream.Loc := Stream.Loc + chunk_size;
 178       end loop;
 179     end Write;
 180  
 181     overriding procedure Set_Index (S : in out Memory_Zipstream; To : ZS_Index_Type) is
 182       I, chunk_size : ZS_Size_Type;
 183     begin
 184       if To > ZS_Size_Type (Length (S.Unb)) then
 185         --  ...we are off the string's bounds, we need to extend it.
 186         I := ZS_Size_Type (Length (S.Unb)) + 1;
 187         while I <= To loop
 188           chunk_size := ZS_Size_Type'Min (To - I + 1, ZS_Size_Type (max_chunk_size));
 189           Append (S.Unb, (1 .. Integer (chunk_size) => ASCII.NUL));
 190           I := I + chunk_size;
 191         end loop;
 192       end if;
 193       S.Loc := Integer (To);
 194     end Set_Index;
 195  
 196     overriding function Size (S : in Memory_Zipstream) return ZS_Size_Type is
 197     begin
 198        return ZS_Size_Type (Length (S.Unb));
 199     end Size;
 200  
 201     overriding function Index (S : in Memory_Zipstream) return ZS_Index_Type is
 202     begin
 203        return ZS_Index_Type (S.Loc);
 204     end Index;
 205  
 206     overriding function End_Of_Stream (S : in Memory_Zipstream) return Boolean is
 207     begin
 208        if Size (S) < Index (S) then
 209           return True;
 210        else
 211           return False;
 212        end if;
 213     end End_Of_Stream;
 214  
 215     --------------------------------------------
 216     -- File_Zipstream: stream based on a file --
 217     --------------------------------------------
 218     procedure Open (Str : in out File_Zipstream; Mode : File_Mode) is
 219     begin
 220        Ada.Streams.Stream_IO.Open (
 221          Str.File,
 222          Ada.Streams.Stream_IO.File_Mode (Mode),
 223          To_String (Str.Name),
 224          Form => To_String (Form_For_IO_Open_and_Create)
 225        );
 226        --  NB: we could have here a call to Set_Time using
 227        --  Ada.Directories.Modification_Time if the latter
 228        --  was able to accept the Form as above for Open
 229        --  (this is needed for Unicode names).
 230     end Open;
 231  
 232     procedure Create (Str : in out File_Zipstream; Mode : File_Mode) is
 233     begin
 234        Ada.Streams.Stream_IO.Create (
 235          Str.File,
 236          Ada.Streams.Stream_IO.File_Mode (Mode),
 237          To_String (Str.Name),
 238          Form => To_String (Form_For_IO_Open_and_Create)
 239        );
 240        --  NB: we could have here a call to Set_Time using
 241        --  Ada.Directories.Modification_Time if the latter
 242        --  was able to accept the Form as above for Create
 243        --  (this is needed for Unicode names).
 244     end Create;
 245  
 246     procedure Close (Str : in out File_Zipstream) is
 247     begin
 248        Ada.Streams.Stream_IO.Close (Str.File);
 249     end Close;
 250  
 251     function Is_Open (Str : in File_Zipstream) return Boolean is
 252     begin
 253        return Ada.Streams.Stream_IO.Is_Open (Str.File);
 254     end Is_Open;
 255  
 256     overriding procedure Read
 257       (Stream : in out File_Zipstream;
 258        Item   : out Stream_Element_Array;
 259        Last   : out Stream_Element_Offset)
 260     is
 261     begin
 262        Ada.Streams.Stream_IO.Read (Stream.File, Item, Last);
 263     end Read;
 264  
 265     overriding procedure Write
 266       (Stream : in out File_Zipstream;
 267        Item   : Stream_Element_Array) is
 268     begin
 269        Ada.Streams.Stream_IO.Write (Stream.File, Item);
 270     end Write;
 271  
 272     overriding procedure Set_Index (S : in out File_Zipstream; To : ZS_Index_Type) is
 273     begin
 274        Ada.Streams.Stream_IO.Set_Index (
 275          S.File,
 276          Ada.Streams.Stream_IO.Positive_Count (To)
 277        );
 278     end Set_Index;
 279  
 280     overriding function Size (S : in File_Zipstream) return ZS_Size_Type is
 281     begin
 282        return ZS_Size_Type (Ada.Streams.Stream_IO.Size (S.File));
 283     end Size;
 284  
 285     overriding function Index (S : in File_Zipstream) return ZS_Index_Type is
 286     begin
 287        return ZS_Index_Type (Ada.Streams.Stream_IO.Index (S.File));
 288     end Index;
 289  
 290     overriding function End_Of_Stream (S : in File_Zipstream) return Boolean is
 291     begin
 292        return Ada.Streams.Stream_IO.End_Of_File (S.File);
 293     end End_Of_Stream;
 294  
 295     package body Calendar is
 296  
 297        -----------------------------------------------
 298        -- Time = DOS Time. Valid through Year 2107. --
 299        -----------------------------------------------
 300  
 301        procedure Split
 302          (Date       : Time;
 303           To_Year    : out Ada.Calendar.Year_Number;
 304           To_Month   : out Ada.Calendar.Month_Number;
 305           To_Day     : out Ada.Calendar.Day_Number;
 306           To_Seconds : out Ada.Calendar.Day_Duration)
 307        is
 308           d_date : constant Integer := Integer (Date  /  65536);
 309           d_time : constant Integer := Integer (Date and 65535);
 310           x            : Integer;
 311           hours        : Integer;
 312           minutes      : Integer;
 313           seconds_only : Integer;
 314        begin
 315           To_Year := 1980 + d_date / 512;
 316           x := (d_date / 32) mod 16;
 317           if x not in Ada.Calendar.Month_Number then  --  that is 0, or in 13..15
 318             raise Time_Error;
 319           end if;
 320           To_Month := x;
 321           x := d_date mod 32;
 322           if x not in Ada.Calendar.Day_Number then  --  that is 0
 323             raise Time_Error;
 324           end if;
 325           To_Day := x;
 326           hours   := d_time / 2048;
 327           minutes := (d_time / 32) mod 64;
 328           seconds_only := 2 * (d_time mod 32);
 329           if hours not in 0 .. 23 or
 330             minutes not in 0 .. 59 or
 331             seconds_only not in 0 .. 59
 332           then
 333             raise Time_Error;
 334           end if;
 335           To_Seconds := Ada.Calendar.Day_Duration (hours * 3600 + minutes * 60 + seconds_only);
 336        end Split;
 337        --
 338        function Time_Of
 339          (From_Year    : Ada.Calendar.Year_Number;
 340           From_Month   : Ada.Calendar.Month_Number;
 341           From_Day     : Ada.Calendar.Day_Number;
 342           From_Seconds : Ada.Calendar.Day_Duration := 0.0) return Time
 343        is
 344           year_2          : Integer := From_Year;
 345           hours           : Unsigned_32;
 346           minutes         : Unsigned_32;
 347           seconds_only    : Unsigned_32;
 348           seconds_day     : Unsigned_32;
 349           result : Unsigned_32;
 350        begin
 351           if year_2 < 1980 then  --  avoid invalid DOS date
 352             year_2 := 1980;
 353           end if;
 354           seconds_day := Unsigned_32 (From_Seconds);
 355           hours := seconds_day / 3600;
 356           minutes :=  (seconds_day / 60) mod 60;
 357           seconds_only := seconds_day mod 60;
 358           result :=
 359             --  MSDN formula for encoding:
 360               Unsigned_32 ((year_2 - 1980) * 512 + From_Month * 32 + From_Day) * 65536  --  Date
 361             +
 362               hours * 2048 + minutes * 32 + seconds_only / 2;  --  Time
 363           return Time (result);
 364        end Time_Of;
 365  
 366        function ">"  (Left, Right : Time) return Boolean is
 367        begin
 368          return Unsigned_32 (Left) > Unsigned_32 (Right);
 369        end ">";
 370  
 371        function Convert (Date : in Ada.Calendar.Time) return Time is
 372           year_temp       : Ada.Calendar.Year_Number;
 373           month_temp      : Ada.Calendar.Month_Number;
 374           day_temp        : Ada.Calendar.Day_Number;
 375           seconds_day_dur : Ada.Calendar.Day_Duration;
 376        begin
 377           Ada.Calendar.Split (Date, year_temp, month_temp, day_temp, seconds_day_dur);
 378           return Time_Of (year_temp, month_temp, day_temp, seconds_day_dur);
 379        end Convert;
 380  
 381        function Convert (Date : in Time) return Ada.Calendar.Time is
 382           year_temp       : Ada.Calendar.Year_Number;
 383           month_temp      : Ada.Calendar.Month_Number;
 384           day_temp        : Ada.Calendar.Day_Number;
 385           seconds_day_dur : Ada.Calendar.Day_Duration;
 386        begin
 387           Split (Date, year_temp, month_temp, day_temp, seconds_day_dur);
 388           return Ada.Calendar.Time_Of (year_temp, month_temp, day_temp, seconds_day_dur);
 389        end Convert;
 390  
 391        function Convert (Date : in DOS_Time) return Time is
 392        begin
 393           return Time (Date);      --  currently a trivial conversion
 394        end Convert;
 395  
 396        function Convert (Date : in Time) return DOS_Time is
 397        begin
 398           return DOS_Time (Date);  --  currently a trivial conversion
 399        end Convert;
 400  
 401     end Calendar;
 402  
 403  end Zip_Streams;

Web view of Ada source code generated by GNATHTML, project: ALI_Parse version 1.0.
Zip-Ada: Ada library for zip archive files (.zip). Ada programming.
Some news about Zip-Ada and other Ada projects on Gautier's blog.