Back to... Zip-Ada

Source file : find_zip.adb


------------------------------------------------------------------------------
--  File:            Find_Zip.adb
--  Description:     Search a text string in files packed in a zip archive.
--  Date/version:    3-Feb-2009
--  Author:          Gautier de Montmollin
------------------------------------------------------------------------------

with Ada.Command_Line;                  use Ada.Command_Line;
with Ada.Text_IO;                       use Ada.Text_IO;
with Ada.Integer_Text_IO;               use Ada.Integer_Text_IO;
with Ada.Characters.Handling;           use Ada.Characters.Handling;
with Ada.Streams;

with Zip;
with UnZip.Streams;                     use UnZip.Streams;

procedure Find_Zip is

  max: constant:= 2**10; -- 1024
  str: String(1..max);  -- str(1..stl) = string to search
  stl: Natural; -- string length
  l: Character; -- last character of the search string

  z: Zip.Zip_info;

  ignore_case: constant Boolean:= True;

  procedure Search_1_file_using_output_stream( name: String ) is
    occ: Natural:= 0;
    -- Define a circular buffer
    siz: constant:= max;
    type Buffer_range is mod siz;
    buf: array(Buffer_range) of Character:= (others => ' ');
    bup: Buffer_range:= 0;
    --  We define a local, ad-hoc stream type.
    --
    type Search_stream is new Ada.Streams.Root_Stream_Type with null record;
    --
    overriding procedure Read
      (Stream : in out Search_stream;
       Item   :    out Ada.Streams.Stream_Element_Array;
       Last   :    out Ada.Streams.Stream_Element_Offset);
    overriding procedure Write
     (Stream : in out Search_stream;
      Item   : in     Ada.Streams.Stream_Element_Array);

    --  Implementation of Read & Write:

    overriding procedure Read
      (Stream : in out Search_stream;
       Item   :    out Ada.Streams.Stream_Element_Array;
       Last   :    out Ada.Streams.Stream_Element_Offset) is null;  --  Not used.

    overriding procedure Write
     (Stream : in out Search_stream;
      Item   : in     Ada.Streams.Stream_Element_Array)
    is
      pragma Unreferenced (Stream);
      c: Character;
      i: Buffer_range:= 0;
      j: Natural;
    begin
      for sei in Item'Range loop
        c:= Character'Val(Item(sei));
        if ignore_case then
          c:= To_Upper(c);
        end if;
        if c = l then -- last character do match, search further...
          i:= bup;
          j:= stl;
          match: loop
            i:= i-1;  --  this loops modulo max: 3, 2, 1, 0, max-1, max-2, ...
            j:= j-1;
            if j = 0 then -- we survived the whole search string
              occ:= occ+1;
              exit match;
            end if;
            exit match when str(j) /= buf(i);
          end loop match;
        end if;
        buf(bup):= c;
        bup:= bup+1;
      end loop;
    end Write;

    sst: Search_stream;

  begin
    Extract(
      Destination      => sst,
      Archive_Info     => z,
      Name             => name,
      Ignore_Directory => False
    );
    if occ > 0 then
      Put(occ, 5);
      Put_Line(" in [" & To_Lower(name) & "] (outward stream method)");
    end if;
  end Search_1_file_using_output_stream;

  --  Old variant using an input stream (memory footprint is uncompressed
  --  size plus fixed amounts: can be large!)

  procedure Search_1_file_using_input_stream( name: String ) is
    f: Zipped_File_Type;
    s: Stream_Access;
    c: Character;
    occ: Natural:= 0;
    -- Define a circular buffer
    siz: constant:= max;
    type Buffer_range is mod siz;
    buf: array(Buffer_range) of Character:= (others => ' ');
    i, bup: Buffer_range:= 0;
    j: Natural;
  begin
    Open( f, z, name );
    s:= Stream(f);
    while not End_Of_File(f) loop
      Character'Read(s,c);
      if ignore_case then
        c:= To_Upper(c);
      end if;
      if c = l then -- last character do match, search further...
        i:= bup;
        j:= stl;
        match: loop
          i:= i-1;  --  this loops modulo max: 3, 2, 1, 0, max-1, max-2, ...
          j:= j-1;
          if j = 0 then -- we survived the whole search string
            occ:= occ+1;
            exit match;
          end if;
          exit match when str(j) /= buf(i);
        end loop match;
      end if;
      buf(bup):= c;
      bup:= bup+1;
    end loop;
    Close(f);
    if occ > 0 then
      Put(occ, 5);
      Put_Line(" in [" & To_Lower(name) & "] (inward stream method)");
    end if;
  end Search_1_file_using_input_stream;
  pragma Unreferenced (Search_1_file_using_input_stream);

  procedure Search_all_files is new Zip.Traverse( Search_1_file_using_output_stream );

  function Try_with_zip(name: String) return String is
  begin
    if Zip.Exists(name) then
      return name;
    else
      return name & ".zip";
      -- Maybe the file doesn't exist, but we tried our best...
    end if;
  end Try_with_zip;

begin
  if Argument_Count < 2 then
    Put_Line("Find_Zip * Search a text string in files packed in a zip archive.");
    Put_Line("Demo for the Zip-Ada library, by G. de Montmollin");
    Put_Line("Library version " & Zip.version & " dated " & Zip.reference );
    Put_Line("URL: " & Zip.web);
    New_Line;
    Put_Line("Usage: find_zip archive[.zip] [""]text[""]");
    return;
  end if;
  declare
    n: constant String:= Try_with_zip(Argument(1));
  begin
    Zip.Load( z, n );
  exception
    when Zip.Zip_file_open_error =>
      Put( "Can't open archive [" & n & ']' ); raise;
    when UnZip.Wrong_password      =>
      Put( "Archive has a password" ); raise;
  end;
  declare
    s: String:= Argument(2);
  begin
    Put_Line("Searching string [" & s & "]");
    if ignore_case then
      s:= To_Upper(s);
    end if;
    stl:= s'Length;
    if stl > str'Length then
      raise Constraint_Error;
    end if;
    str(1..stl):= s;
    l:= str(stl);
  end;
  Search_all_files(z);
end Find_Zip;

Zip-Ada: Ada library for zip archive files (.zip). Ada programming.
Some news about Zip-Ada and other related Ada projects on Gautier's blog.