DataMuseum.dk

Presents historical artifacts from the history of:

Rational R1000/400

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Rational R1000/400

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦4a3d9607a⟧ Ada Source

    Length: 113664 (0x1bc00)
    Types: Ada Source
    Notes: 03_class, FILE, Long Ada Source, R1k_Segment, e3_tag, package body Test_Io, seg_00563f

Derivation

└─⟦8527c1e9b⟧ Bits:30000544 8mm tape, Rational 1000, Arrival backup of disks in PAM's R1000
    └─ ⟦5a81ac88f⟧ »Space Info Vol 1« 
        └─⟦this⟧ 

E3 Source Code



--/ if R1000 then
with Directory_Tools;
--/ elsif TeleGen2 and then Unix then
--// with Error_Messages;                    -- Unix error codes
--// with File_Ops;                          -- Unix file operations
--// with Integer_Text_Io;
--// with Process_Control;                   -- Unix process controls
--// with System_Calls;                      -- Unix system calls
--// with Unix_Types;                        -- Unix system types
--// use Unix_Types;
--/ end if;

with Text_Io;  
with Unchecked_Deallocation;

with Xlbt_Arithmetic;  
use Xlbt_Arithmetic;  
with Xlbt_String;  
use Xlbt_String;

package body Test_Io is
------------------------------------------------------------------------------
-- X Library Testing
--
-- Test_Io - Basic I/O facilities for the tests.
------------------------------------------------------------------------------
-- Copyright 1990 - 1990 by Rational, Santa Clara, California.
--
--                  All Rights Reserved.
--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright notice(s) appear in all copies and that
-- both that copyright notice(s) and this permission notice appear in
-- supporting documentation, and that the name of Rational not be used in
-- advertising or publicity pertaining to distribution of the software
-- without specific, written prior permission.
--
-- Rational disclaims all warranties with regard to this software, including
-- all implied warranties of merchantability and fitness, in no event shall
-- Rational be liable for any special, indirect or consequential damages or
-- any damages whatsoever resulting from loss of use, data or profits, whether
-- in an action of contract, negligence or other tortious action, arising out
-- of or in connection with the use or performance of this software.
------------------------------------------------------------------------------

--/ if R1000 then
    package Integer_Text_Io is new Text_Io.Integer_Io (Integer);
--/ end if;

------------------------------------------------------------------------------
-- File_List - List of all open output files.
------------------------------------------------------------------------------
    type File_List_Rec;  
    type File_List is access File_List_Rec;

    type File_List_Rec is  
        record  
            Next      : File_List;  
            File      : Text_Io.File_Type;  
            Duplicate : Boolean;  
        end record;

--/ if Enable_Deallocation then
    pragma Enable_Deallocation (File_List);
--/ end if;

    procedure Free_File_List is new Unchecked_Deallocation (File_List_Rec,  
                                                            File_List);

    Current : File_List := null;

------------------------------------------------------------------------------
-- File_Line - Line of a file we are diff'ing.
------------------------------------------------------------------------------

    type String_Pointer is access String;

--/ if Enable_Deallocation then
    pragma Enable_Deallocation (String_Pointer);
--/ end if;

    procedure Free_String_Pointer is  
       new Unchecked_Deallocation (String, String_Pointer);

    type File_Line_Rec is  
        record  
            Line  : String_Pointer;  
            Hash  : S_Long;  
            Eop   : Boolean;  
            Blank : Boolean;  
        end record;

    type File_Line is access File_Line_Rec;

--/ if Enable_Deallocation then
    pragma Enable_Deallocation (File_Line);
--/ end if;

    type File_Line_Array is array (S_Natural range <>) of File_Line;  
    type File_Line_List  is access File_Line_Array;

--/ if Enable_Deallocation then
    pragma Enable_Deallocation (File_Line_List);
--/ end if;

    procedure Free_File_Line       is new Unchecked_Deallocation (File_Line_Rec,  
                                                                  File_Line);  
    procedure Free_File_Line_Array is  
       new Unchecked_Deallocation (File_Line_Array,  
                                   File_Line_List);

------------------------------------------------------------------------------
-- Wildcards
------------------------------------------------------------------------------

    type File_Name_Rec;  
    type File_Name is access File_Name_Rec;

    type File_Name_Rec is  
        record  
            Next : File_Name;  
            Name : String_Pointer;  
        end record;

--/ if Enable_Deallocation then
    pragma Enable_Deallocation (File_Name);
--/ end if;

    procedure Free_File_Name_Rec is new Unchecked_Deallocation (File_Name_Rec,  
                                                                File_Name);

    Name_List : File_Name;

--\x0c
    function To_String (Str : X_String) return String is  
        Stro : String (1 .. Str'Length) := Xlbt_String.To_String (Str);  
    begin  
        for I in Stro'Range loop  
            if Character'Pos (Stro (I)) not in 32 .. 127 then  
                return Stro (Stro'First .. I - 1) &  
                          '\' & Natural'Image (Character'Pos (Stro (I))) & '\' &  
                          To_String (Str (Str'First + S_Natural (I) ..  
                                             Str'Last));  
            end if;  
        end loop;  
        return Stro;  
    end To_String;

--\x0c
    procedure Free_File_Name (Name : in out File_Name) is
------------------------------------------------------------------------------
--  List    - Specfies the list to deallocatd
--
-- We deallocate all elements of the list and then the list itself.
------------------------------------------------------------------------------
    begin

        Free_String_Pointer (Name.Name);  
        Free_File_Name_Rec (Name);

    end Free_File_Name;

--\x0c
    procedure Free_File_Line_List (List : in out File_Line_List) is
------------------------------------------------------------------------------
--  List    - Specfies the list to deallocatd
--
-- We deallocate all elements of the list and then the list itself.
------------------------------------------------------------------------------
    begin

        for I in List'Range loop  
            if List (I) /= null then  
                Free_String_Pointer (List (I).Line);  
                Free_File_Line (List (I));  
            end if;  
        end loop;  
        Free_File_Line_Array (List);

    end Free_File_Line_List;

--\x0c
    procedure Put_Aux (Msg : String) is
------------------------------------------------------------------------------
--  Msg - Specifies the ::: error message to put out.
--
-- Put out "::: " and Msg to all active outputs regardless of Duplicate flag.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, "::: ");  
            Text_Io.Put_Line (File.File, Msg);  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, "::: ");  
        Text_Io.Put_Line (Text_Io.Standard_Output, Msg);

    end Put_Aux;

--\x0c
    procedure Put_Error (Msg : String) is
------------------------------------------------------------------------------
--  Msg - Specifies the *** error message to put out.
--
-- Put out "*** " and Msg to all active outputs regardless of Duplicate flag.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, "*** ");  
            Text_Io.Put_Line (File.File, Msg);  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, "*** ");  
        Text_Io.Put_Line (Text_Io.Standard_Output, Msg);

    end Put_Error;

--\x0c
    procedure Put_Exception (Msg : String) is
------------------------------------------------------------------------------
--  Msg - Specifies the %%% error message to put out.
--
-- Put out "%%% " and Msg to all active outputs regardless of Duplicate flag.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, "*** ");  
            Text_Io.Put_Line (File.File, Msg);  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, "*** ");  
        Text_Io.Put_Line (Text_Io.Standard_Output, Msg);

    end Put_Exception;

--\x0c
    procedure Push_Output (File      : String;  
                           Duplicate : Boolean := True) is
------------------------------------------------------------------------------
--  File        - Specifies the name of the new output file
--  Duplicate   - Specifies TRUE if output should go to all stacked outputs
--
-- Opens the file for writing and sets it so that it is used for future output.
--
-- Duplicate => FALSE will block normal output from duplication in "outer"
-- log files.  If another Push is done with Duplicate => TRUE then output
-- will go to that file and this one but no further because this one has
-- Duplicate => FALSE which blocks further output.
--
-- Put_Error and Put_Exception ignore the Duplicate flag.
------------------------------------------------------------------------------
        New_File : File_List := new File_List_Rec;  
    begin

----Create/Open the new output file.

        begin  
            Text_Io.Create (New_File.File, Text_Io.Out_File, File);  
            New_File.Duplicate := Duplicate;  
        exception  
            when others =>  
                begin  
                    Text_Io.Open (New_File.File, Text_Io.Out_File, File);  
                exception  
                    when others =>  
                        Free_File_List (New_File);  
                        Put_Error ("Failed to open output file [" &  
                                   File & "].");  
                        raise;  
                end;  
        end;

----Link the new file onto our list of files and make it the current output.

        New_File.Next := Current;  
        Current       := New_File;  
        Text_Io.Set_Output (New_File.File);

    end Push_Output;

--\x0c
    procedure Pop_Output is
------------------------------------------------------------------------------
-- Closes the current output file and reverts to the previous output file.
-- Raises Constraint_Error if there was no previous file.
------------------------------------------------------------------------------
        Old_File : File_List;  
    begin

----Make sure that we have an old file.

        if Current = null then  
            raise Constraint_Error;  
        end if;

----Close the old file.

        Text_Io.New_Line (Current.File);  
        Text_Io.Put_Line (Current.File, "End of log.");  
        Text_Io.Close (Current.File);

----Remove the old file from the list and free the storage.

        Old_File := Current;  
        Current  := Current.Next;  
        Free_File_List (Old_File);

----Set the current output file.

        if Current /= null then  
            Text_Io.Set_Output (Current.File);  
        else  
            Text_Io.Set_Output (Text_Io.Standard_Output);  
        end if;

    end Pop_Output;

--\x0c
    procedure Section (Name : String) is
------------------------------------------------------------------------------
--  Name    - Specifies the name of a "section" in the log file
--
-- alled within tests so that we can log "where we are" while running a test.
-- Some tests will have many "parts" and each part will start by identifying
-- itself to the log.  This helps when initially tracking down bugs that show
-- up when running the regression tests.
------------------------------------------------------------------------------
    begin

        New_Line;  
        Put ("=== Test Section => ");  
        Put_Line (Name);  
        New_Line;

    end Section;

--\x0c
    procedure New_Line (Spacing : Positive := 1) is
------------------------------------------------------------------------------
--  Spacing - Specifies the number of new-lines to produce
--
-- Called to go to a new line in the log file.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.New_Line (File.File, Text_Io.Positive_Count (Spacing));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.New_Line (Text_Io.Standard_Output,  
                          Text_Io.Positive_Count (Spacing));

    end New_Line;

--\x0c
    procedure Set_Col (To : Positive) is
------------------------------------------------------------------------------
--  To  - Specifies the column to go to, 1..N
--
-- Used to put out column information in the log file.  Helps when you want
-- various things to line up in the log.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Set_Col (File.File, Text_Io.Positive_Count (To));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Set_Col (Text_Io.Standard_Output, Text_Io.Positive_Count (To));

    end Set_Col;

--\x0c
    procedure Put (Ch : Character) is
------------------------------------------------------------------------------
--  Ch  - Specifies the character to put out
--
-- Used to put one character into the log.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, Ch);  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, Ch);

    end Put;

--\x0c
    procedure Put (Str : String) is
------------------------------------------------------------------------------
--  Str - Specifies the string to put out
--
-- Used to put a string into the log.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, Str);  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, Str);

    end Put;

--\x0c
    procedure Put_Line (Str : String) is
------------------------------------------------------------------------------
--  Str - Specifies the string to put out
--
-- Used to put a string into the log and ends it in a new-line.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put_Line (File.File, Str);  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Put_Line (Text_Io.Standard_Output, Str);

    end Put_Line;

--\x0c
    procedure Putx (Str : X_String) is
------------------------------------------------------------------------------
--  Str - Specifies the string to put out
--
-- Used to put a string into the log.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put (File.File, To_String (Str));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Put (Text_Io.Standard_Output, To_String (Str));

    end Putx;

--\x0c
    procedure Putx_Line (Str : X_String) is
------------------------------------------------------------------------------
--  Str - Specifies the string to put out
--
-- Used to put a string into the log and ends it in a new-line.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Text_Io.Put_Line (File.File, To_String (Str));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Text_Io.Put_Line (Text_Io.Standard_Output, To_String (Str));

    end Putx_Line;

--\x0c
    procedure Put (Int   : Integer;  
                   Width : Natural  := 1;  
                   Base  : Positive := 10) is
------------------------------------------------------------------------------
--  Int     - Specifies the integer value
--  Width   - Specifies the width of the output
--  Base    - Specifies the base of the output
--
-- Used to put an integer into the log; base 10.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Integer_Text_Io.Put (File.File, Int, Text_Io.Field (Width),  
                                 Text_Io.Number_Base (Base));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Integer_Text_Io.Put (Text_Io.Standard_Output, Int,  
                             Text_Io.Field (Width), Text_Io.Number_Base (Base));

    end Put;

--\x0c
    procedure Hex (Int   : Integer;  
                   Width : Natural  := 1;  
                   Base  : Positive := 16) is
------------------------------------------------------------------------------
--  Int     - Specifies the integer value
--  Width   - Specifies the width of the output
--  Base    - Specifies the base of the output
--
-- Used to put an integer into the log; base 16.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            Integer_Text_Io.Put (File.File, Int, Text_Io.Field (Width),  
                                 Text_Io.Number_Base (Base));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        Integer_Text_Io.Put (Text_Io.Standard_Output, Int,  
                             Text_Io.Field (Width), Text_Io.Number_Base (Base));

    end Hex;

--\x0c
    procedure Put (Ul    : S_Long;  
                   Width : Natural  := 1;  
                   Base  : Positive := 10) is
------------------------------------------------------------------------------
--  Int     - Specifies the S_Long value
--  Width   - Specifies the width of the output
--  Base    - Specifies the base of the output
--
-- Used to put an S_Long into the log; base 10.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            S_Long_Io.Put (File.File, Ul, Text_Io.Field (Width),  
                           Text_Io.Number_Base (Base));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        S_Long_Io.Put (Text_Io.Standard_Output, Ul, Text_Io.Field (Width),  
                       Text_Io.Number_Base (Base));

    end Put;

--\x0c
    procedure Hex (Ul    : S_Long;  
                   Width : Natural  := 1;  
                   Base  : Positive := 16) is
------------------------------------------------------------------------------
--  Int     - Specifies the S_Long value
--  Width   - Specifies the width of the output
--  Base    - Specifies the base of the output
--
-- Used to put an S_Long into the log; base 16.
------------------------------------------------------------------------------
        File : File_List := Current;  
    begin

        while File /= null loop  
            S_Long_Io.Put (File.File, Ul, Text_Io.Field (Width),  
                           Text_Io.Number_Base (Base));  
            if not File.Duplicate then  
                return;  
            end if;  
            File := File.Next;  
        end loop;  
        S_Long_Io.Put (Text_Io.Standard_Output, Ul, Text_Io.Field (Width),  
                       Text_Io.Number_Base (Base));

    end Hex;

--\x0c
    function Append_File_Extension (File      : String;  
                                    Extension : String) return String is
------------------------------------------------------------------------------
--  File        - Specifies the basic name of the file, eg. "foobar".
--  Extension   - Specifies the extension to give to the file, eg. "log".
--
-- Adds the Extension to the File with the appropriate system-dependent
-- character(s) in between.  Eg. "foobar.log" or "foobar_log"
------------------------------------------------------------------------------
    begin

--/ if R1000 then

        return File & '_' & Extension;

--/ elsif Unix then
--//
--//         return File & '.' & Extension;
--//
--/ else
--//
--//   return need_something_here;
--//
--/ end if;

    end Append_File_Extension;

--\x0c
    function File_Context (File : String) return String is
------------------------------------------------------------------------------
--  File    - Specifies a file name
--
-- Returns the path portion of the name.  Returns something like "$" or "."
-- for files that have no explicit path.
------------------------------------------------------------------------------
    begin

--/ if R1000 then

        declare  
            Prefix : constant String := Directory_Tools.Naming.Prefix (File);  
        begin  
            if Prefix = "" then  
                return "$";  
            else  
                return Directory_Tools.Naming.Prefix (File);  
            end if;  
        end;

--/ elsif Unix then
--//
--//         for I in reverse File'Range loop
--//             if File (I) = '/' then
--//                 return File (File'First .. I - 1);
--//             end if;
--//         end loop;
--//         return ".";
--//
--/ else
--//
--//         Need_Something_Here;
--//
--/ end if;

    end File_Context;

--\x0c
    --/ if R1000 then

    procedure File_Delete (File : String) is
------------------------------------------------------------------------------
--  File    - Specifies the file to delete
--
-- Called to delete a particular file.
------------------------------------------------------------------------------
        Handle : Directory_Tools.Object.Handle;  
        Status : Directory_Tools.Object.Error_Code;  
    begin

----First we locate the object.

        Handle := Directory_Tools.Naming.Resolution (File);  
        if Directory_Tools.Object.Is_Bad (Handle) then  
            Put_Error ("Could not find [" & File & "] to delete it.");  
            Put_Error ("Error: " & Directory_Tools.Object.Message (Handle));  
            raise Test_Io_Error;  
        end if;

----Second we delete the object.

        Directory_Tools.Any_Object.Delete (Handle, Status);  
        if Directory_Tools.Object.Is_Bad (Status) then  
            Put_Error ("Could not delete [" & File & "].");  
            Put_Error ("Error: " & Directory_Tools.Object.Message (Handle));  
            raise Test_Io_Error;  
        end if;

    end File_Delete;

--/ end if; -- R1000

--\x0c
    --/ if TeleGen2 and then Unix then
--//
--//     procedure File_Delete (File : String) is
--// ------------------------------------------------------------------------------
--// --  File    - Specifies the file to delete
--// --
--// -- Called to delete a particular file.
--// ------------------------------------------------------------------------------
--//     begin
--//
--//         File_Ops.Unlink (File);
--//
--//     end File_Delete;
--//
--/ end if; -- TeleGen2 and then Unix

--\x0c
    function File_Exists (File : String) return Boolean is
------------------------------------------------------------------------------
--  File    - Specifies the name of the file to check for
--
-- Called to check to see whether or not a file exists and is readable.
------------------------------------------------------------------------------
        Old_File : Text_Io.File_Type;  
    begin

        begin  
            Text_Io.Open (Old_File, Text_Io.In_File, File);  
            Text_Io.Close (Old_File);  
            return True;  
        exception  
            when others =>  
                return False;  
        end;

    end File_Exists;

--\x0c
    procedure Read_Line (File  :     Text_Io.File_Type;  
                         Line  : out String;  
                         Linei : out Natural;  
                         Eop   : out Boolean;  
                         Eof   : out Boolean) is
------------------------------------------------------------------------------
--  File    - Specifies the file to read
--  Line    - Receives the next line of input
--  Linei   - Receives the length of the next line
--  Eop     - Receives TRUE if this line is the last line of a page
--  Eof     - Receives TRUE if we just encountered EOF and Line is empty
--
-- Called to read the next line of input from a file.
------------------------------------------------------------------------------
    begin

        Text_Io.Get_Line (File, Line, Linei);  
        Eop := Text_Io.End_Of_Page (File);  
        Eof := False;

    exception

        when Text_Io.End_Error =>  
            Linei := 0;  
            Eop   := False;  
            Eof   := True;

    end Read_Line;

--\x0c
    function Equal_Uc (Str1 : String;  
                       Str2 : String) return Boolean is
------------------------------------------------------------------------------
--  Str1    - Specifies the first string
--  Str2    - Specifies the second string
--
-- Called to compare two strings without regard to the case of the strings.
-- Returns TRUE if they are equal.
------------------------------------------------------------------------------
        C1 : Character;  
        C2 : Character;  
    begin

        if Str1'Length /= Str2'Length then  
            return False;  
        end if;  
        for I in reverse Natural range 0 .. Str1'Length - 1 loop  
            C1 := Str1 (Str1'First + I);  
            C2 := Str2 (Str2'First + I);  
            if C1 /= C2 then  
                if C1 in 'a' .. 'z' then  
                    if C2 in 'A' .. 'Z' then
                        ----C1 and C2 are both letters.
                        if Character'Pos (C1) /=  
                           Character'Pos (C2) - (Character'Pos ('A') -  
                                                 Character'Pos ('a')) then  
                            return False;  
                        end if;  
                    else
                        ----C1 is a letter but C2 is not.
                        return False;  
                    end if;  
                elsif C1 in 'A' .. 'Z' then  
                    if C2 in 'a' .. 'z' then
                        ----C1 and C2 are both letters.
                        if Character'Pos (C1) /=  
                           Character'Pos (C2) - (Character'Pos ('a') -  
                                                 Character'Pos ('A')) then  
                            return False;  
                        end if;  
                    else
                        ----C1 is a letter but C2 is not.
                        return False;  
                    end if;  
                else
                    ----C1 is not a letter and C1 /= C2.
                    return False;  
                end if;  
            end if;  
        end loop;  
        return True;

    end Equal_Uc;

--\x0c
    function Is_Blank (Str : String) return Boolean is
------------------------------------------------------------------------------
--  Str     - Specifies the string to check
--
-- Returns TRUE if Str is empty or contains only ' ' and/or Ascii.Ht.
------------------------------------------------------------------------------
        C : Character;  
    begin

        for I in Str'Range loop  
            C := Str (I);  
            if C /= ' ' and then C /= Ascii.Ht then  
                return False;  
            end if;  
        end loop;  
        return True;

    end Is_Blank;

--\x0c
    function Files_Equal (File1              : String;  
                          File2              : String;  
                          Ignore_Case        : Boolean;  
                          Ignore_Blank_Lines : Boolean) return Boolean is
------------------------------------------------------------------------------
--  File1   - Specifies the name of the first file
--  File2   - Specifies the name of the second file
--
-- We do a "fast" comparison of the two files.  Our only concern is 'equality'.
-- call the File_Difference routine for a diff output.
------------------------------------------------------------------------------
        F1     : Text_Io.File_Type;  
        F2     : Text_Io.File_Type;  
        Result : Boolean := False;

        Line1  : String (1 .. 1024);  
        Line2  : String (1 .. 1024);  
        Line1i : Natural;  
        Line2i : Natural;  
        Eop1   : Boolean;  
        Eop2   : Boolean;  
        Eof1   : Boolean := False;  
        Eof2   : Boolean := False;

    begin

----Open the files for reading.

        begin  
            Text_Io.Open (F1, Text_Io.In_File, File1);  
        exception  
            when others =>  
                Put_Error ("Could not open file to compare [" & File1 & "}.");  
                return False;  
        end;  
        begin  
            Text_Io.Open (F2, Text_Io.In_File, File2);  
        exception  
            when others =>  
                Put_Error ("Could not open file to compare [" & File2 & "}.");  
                Text_Io.Close (F1);  
                return False;  
        end;

----Loop reading lines until we get to something that is different.

        if Ignore_Blank_Lines then

----We are ignoring blank lines and case differences.

            if Ignore_Case then  
                loop  
                    loop  
                        Read_Line (F1, Line1, Line1i, Eop1, Eof1);  
                        if Eof1 or else not Is_Blank (Line1 (1 .. Line1i)) then  
                            exit;  
                        end if;  
                    end loop;  
                    loop  
                        Read_Line (F2, Line2, Line2i, Eop2, Eof2);  
                        if Eof2 or else not Is_Blank (Line2 (1 .. Line2i)) then  
                            exit;  
                        end if;  
                    end loop;  
                    if (Line1 (1 .. Line1i) /= Line2 (1 .. Line2i) and then  
                        not Equal_Uc (Line1 (1 .. Line1i),  
                                      Line2 (1 .. Line2i))) or else  
                       Eop1 /= Eop2 or else  
                       Eof1 /= Eof2 then  
                        Result := False;  
                        exit;  
                    elsif Eof1 then  
                        Result := True;  
                        exit;  
                    end if;  
                end loop;

----We are ignoring blank lines but not case differences.

            else  
                loop  
                    loop  
                        Read_Line (F1, Line1, Line1i, Eop1, Eof1);  
                        if Eof1 or else not Is_Blank (Line1 (1 .. Line1i)) then  
                            exit;  
                        end if;  
                    end loop;  
                    loop  
                        Read_Line (F2, Line2, Line2i, Eop2, Eof2);  
                        if Eof2 or else not Is_Blank (Line2 (1 .. Line2i)) then  
                            exit;  
                        end if;  
                    end loop;  
                    if Line1 (1 .. Line1i) /= Line2 (1 .. Line2i) or else  
                       Eop1 /= Eop2 or else  
                       Eof1 /= Eof2 then  
                        Result := False;  
                        exit;  
                    elsif Eof1 then  
                        Result := True;  
                        exit;  
                    end if;  
                end loop;  
            end if;

----We are not ignoring blank lines.

        else

----We are not ignoring blank lines but we are ignoring case differences.

            if Ignore_Case then  
                loop  
                    Read_Line (F1, Line1, Line1i, Eop1, Eof1);  
                    Read_Line (F2, Line2, Line2i, Eop2, Eof2);  
                    if (Line1 (1 .. Line1i) /= Line2 (1 .. Line2i) and then  
                        not Equal_Uc (Line1 (1 .. Line1i),  
                                      Line2 (1 .. Line2i))) or else  
                       Eop1 /= Eop2 or else  
                       Eof1 /= Eof2 then  
                        Result := False;  
                        exit;  
                    elsif Eof1 then  
                        Result := True;  
                        exit;  
                    end if;  
                end loop;

----We are not ignoring blank lines or case differences.

            else  
                loop  
                    Read_Line (F1, Line1, Line1i, Eop1, Eof1);  
                    Read_Line (F2, Line2, Line2i, Eop2, Eof2);  
                    if Line1 (1 .. Line1i) /= Line2 (1 .. Line2i) or else  
                       Eop1 /= Eop2 or else  
                       Eof1 /= Eof2 then  
                        Result := False;  
                        exit;  
                    elsif Eof1 then  
                        Result := True;  
                        exit;  
                    end if;  
                end loop;  
            end if;  
        end if;

----Close the files and return our result.

        Text_Io.Close (F1);  
        Text_Io.Close (F2);  
        return Result;

    exception

        when others =>  
            Put_Line ("%%% Unexpected exception in Text_Io.Files_Equal?");  
            Text_Io.Close (F1);  
            Text_Io.Close (F2);  
            return False;

    end Files_Equal;

--\x0c
    function Hash (Str         : String;  
                   Ignore_Case : Boolean) return S_Long is
------------------------------------------------------------------------------
--  Str         - Specifies the string to hash
--  Ignore_Case - Specifies TRUE if we are ignoring case
--
-- Computes a hash function for a string.  Could overflow for very long strings
-- but lines are limited to 1024 characters so it won't in practice.
------------------------------------------------------------------------------
        H : S_Long  := 0;  
        I : Natural := Str'First;  
        C : Character;  
    begin

----Do it this way if we are not ignoring case.

        if not Ignore_Case then  
            loop  
                if I > Str'Last then  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I));  
                I := I + 1;  
                if I > Str'Last hen  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I)) * 2 ** 4;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I)) * 2 ** 8;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I)) * 2 ** 12;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I)) * 2 ** 16;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                H := H + Character'Pos (Str (I)) * 2 ** 20;  
                I := I + 1;  
            end loop;

----Do it this way if we are not ignoring case.
        else  
            loop  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C);  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C) * 2 ** 4;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C) * 2 ** 8;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C) * 2 ** 12;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C) * 2 ** 16;  
                I := I + 1;  
                if I > Str'Last then  
                    exit;  
                end if;  
                C := Str (I);  
                if C in 'a' .. 'z' then  
                    C := Character'Val (Character'Pos (C) -  
                                        (Character'Pos ('a') -  
                                         Character'Pos ('A')));  
                end if;  
                H := H + Character'Pos (C) * 2 ** 20;  
                I := I + 1;  
            end loop;  
        end if;  
        return H;

    end Hash;

--\x0c
    procedure Read_File (File        :        Text_Io.File_Type;  
                         Line_List   : in out File_Line_List;  
                         Line_Listi  : in out S_Natural;  
                         Ignore_Case :        Boolean) is
------------------------------------------------------------------------------
--  File        - Specifies the file to read.
--  Line_List   - Receives the list of lines read
--  Line_Listi  - Receives the number of lines in the list
--  Ignore_Case - Specifies TRUE if the hash is to ignore case
--
-- Called to read an entire file into memory.
------------------------------------------------------------------------------
        Line  : String (1 .. 1024);  
        Linei : Natural;  
        Eop   : Boolean;  
        Eof   : Boolean;  
    begin

----Initialize our output array.


        Line_List  := new File_Line_Array (1 .. 1000);  
        Line_Listi := 0;

----Read the file to the end.

        loop  
            Read_Line (File, Line, Linei, Eop, Eof);  
            if Eof then  
                return;  
            end if;  
            if Line_Listi = Line_List'Last then  
                declare  
                    Newl : File_Line_List :=  
                       new File_Line_Array (1 .. Line_List'Length * 2);  
                begin  
                    Newl (1 .. Line_List'Length) := Line_List.all;  
                    Free_File_Line_Array (Line_List);  
                    Line_List := Newl;  
                end;  
            end if;  
            Line_Listi             := Line_Listi + 1;  
            Line_List (Line_Listi) :=  
               new File_Line_Rec'(Line => new String'(Line (1 .. Linei)),  
                                  Hash => Hash (Line (1 .. Linei), Ignore_Case),  
                                  Eop => Eop,  
                                  Blank => Is_Blank (Line (1 .. Linei)));  
        end loop;

    end Read_File;

--\x0c
    procedure Regions_Differ (List1 : File_Line_Array;  
                              List2 : File_Line_Array) is
------------------------------------------------------------------------------
--  List1   - Specifies the lines in file 1 that don't match
--  List2   - Specifies the lines in file 2 that don't match
--
-- Called when we (may) have a region of lines in file1 that don't match some
-- region of lines in file2.  Report them to the log.
------------------------------------------------------------------------------
    begin

----If there are no lines from File1 then we either have no difference or else
--  we have an Insert.

        if List1'Length = 0 then  
            if List2'Length = 0 then  
                return;     -- Nothing to report.
            end if;  
            New_Line;  
            Put ("--- Insert ");  
            Put (List1'First);  
            Put (",");  
            Put (S_Long (List2'Length));  
            New_Line;  
            for I in List2'Range loop  
                Put ("> ");  
                Put_Line (List2 (I).Line.all);  
            end loop;  
            return;  
        end if;

----If there are no lines from File2 then we have a Delete.

        if List2'Length = 0 then  
            New_Line;  
            Put ("--- Delete ");  
            Put (List1'First);  
            Put (",");  
            Put (S_Long (List1'Length));  
            New_Line;  
            for I in List1'Range loop  
                Put ("< ");  
                Put_Line (List1 (I).Line.all);  
            end loop;  
            return;  
        end if;

----Otherwise we have a change.

        New_Line;  
        Put ("--- Change ");  
        Put (List1'First);  
        Put (",");  
        Put (S_Long (List1'Length));  
        Put (",");  
        Put (S_Long (List2'Length));  
        New_Line;  
        for I in List1'Range loop  
            Put ("< ");  
            Put_Line (List1 (I).Line.all);  
        end loop;  
        Put_Line ("--- ------");  
        for I in List2'Range loop  
            Put ("> ");  
            Put_Line (List2 (I).Line.all);  
        end loop;  
        return;

    end Regions_Differ;

--\x0c
    procedure Diff_Cb (List1 : File_Line_Array;  
                       List2 : File_Line_Array) is
------------------------------------------------------------------------------
--  List1               - Specifies the lines from file 1
--  List2               - Specifies the lines from file 2
--
-- Called to perform the main work of the Files_Diff routine.
--
-- Ignore_Case        => FALSE
-- Ignore_Blank_Lines => FALSE
------------------------------------------------------------------------------
        Line1       : File_Line;  
        Line2       : File_Line;  
        Line1i      : S_Natural := 0;  
        Line2i      : S_Natural := 0;  
        Last_Line1i : S_Natural := 0;  
        Last_Line2i : S_Natural := 0;  
        Tmp1        : S_Natural;  
        Tmp2        : S_Natural;  
    begin

----Check our assumptions.

        if List1'First /= List2'First then  
            raise Program_Error;        -- Our assumption fails.
        end if;

----Match lines starting at the top until we don't match any more.

        for I in List1'Range loop  
            if I > List2'Last then  
                exit;  
            end if;  
            Line1 := List1 (I);  
            Line2 := List2 (I);  
            if Line1.Hash /= Line2.Hash or else  
               Line1.Line.all /= Line2.Line.all then  
                exit;  
            end if;  
            Last_Line1i := I;  
            Last_Line2i := I;  
        end loop;

----Loop until we have gone over the entirety of both lists.

        Main_Diff_Loop:  
            loop

----Loop until we find a non-blank Line1 that has a matching Line2.

                Line1_Line2_Loop:  
                    loop

----Loop until we find the next non-blank Line1 of any flavor.

                        for I in Line1i + 1 .. List1'Last loop  
                            if not List1 (I).Blank then  
                                Line1  := List1 (I);  
                                Line1i := I;  
                                goto Have_Line1;  
                            end if;  
                        end loop;  
                        exit Main_Diff_Loop;    -- Can't find a next Line1.

----We get here when we have a Line1 and we need to find a matchine Line2.

                        <<Have_Line1>> null;  
                        for I in Line2i + 1 .. List2'Last loop  
                            Line2 := List2 (I);  
                            if not Line2.Blank then  
                                if Line1.Hash = Line2.Hash and then  
                                   Line1.Line.al = Line2.Line.all then  
                                    Line2i := I;  
                                    exit Line1_Line2_Loop;  
                                end if;  
                            end if;  
                        end loop;

----If we get here then there is no Line2 that matches Line1.  Loop and go
--  after the next non-blank Line1.

                    end loop Line1_Line2_Loop;

----Come here when we have Line1 = Line2 and we now want to expand the
--  region of match.  Loop backwards looking for a line that doesn't match.
--  Then report that region of difference.

                <<Have_A_Match>> null;  
                Tmp1 := Line1i;  
                Tmp2 := Line2i;  
                while Line1i - 1 > Last_Line1i and then  
                         Line2i - 1 > Last_Line2i loop  
                    Line1 := List1 (Line1i - 1);  
                    Line2 := List2 (Line2i - 1);  
                    if Line1.Hash /= Line2.Hash or else  
                       Line1.Line.all /= Line2.Line.all then  
                        exit;  
                    end if;  
                    Line1i := Line1i - 1;  
                    Line2i := Line2i - 1;  
                end loop;  
                Regions_Differ (List1 (Last_Line1i + 1 .. Line1i - 1),  
                                List2 (Last_Line2i + 1 .. Line2i - 1));

----Now loop forwards looking for a line that doesn't match.  That defines
--  our next starting point.

                Line1i := Tmp1;  
                Line2i := Tmp2;  
                while Line1i < List1'Last and then  
                         Line2i < List2'Last loop  
                    Line1 := List1 (Line1i + 1);  
                    Line2 := List2 (Line2i + 1);  
                    if Line1.Hash /= Line2.Hash or else  
                       Line1.Line.all /= Line2.Line.all then  
                        exit;  
                    end if;  
                    Line1i := Line1i + 1;  
                    Line2i := Line2i + 1;  
                end loop;  
                Last_Line1i := Line1i;  
                Last_Line2i := Line2i;

----Loop and go after the next matching region.

            end loop Main_Diff_Loop;

----Come here when we can't find a next-non-blank Line1.

        Regions_Differ (List1 (Last_Line1i + 1 .. List1'Last),  
                        List2 (Last_Line2i + 1 .. List2'Last));

    end Diff_Cb;

--\x0c
    procedure Diff_Ci (List1 : File_Line_Array;  
                       List2 : File_Line_Array) is
------------------------------------------------------------------------------
--  List1               - Specifies the lines from file 1
--  List2               - Specifies the lines from file 2
--
-- Called to perform the main work of the Files_Diff routine.
--
-- Ignore_Case        => FALSE
-- Ignore_Blank_Lines => TRUE
------------------------------------------------------------------------------
        Line1       : File_Line;  
        Line2       : File_Line;  
        Line1i      : S_Natural := 0;  
        Line2i      : S_Natural := 0;  
        Last_Line1i : S_Natural := 0;  
        Last_Line2i : S_Natural := 0;  
        Tmp1        : S_Natural;  
        Tmp2        : S_Natural;  
    begin

----Check our assumptions.

        if List1'First /= List2'First then  
            raise Program_Error;        -- Our assumption fails.
        end if;

----Match lines starting at the top until we don't match any more.

        Pre_Diff_Loop:  
            loop  
                loop  
                    if Last_Line1i = List1'Last then  
                        exit Pre_Diff_Loop;  
                    elsif not List1 (Last_Line1i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line1i := Last_Line1i + 1;  
                end loop;  
                Line1 := List1 (Last_Line1i + 1);  
                loop  
                    if Last_Line2i = List1'Last then  
                        exit Pre_Diff_Loop;  
                    elsif not List1 (Last_Line2i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line2i := Last_Line2i + 1;  
                end loop;  
                Line2 := List1 (Last_Line2i + 1);  
                if Line1.Hash /= Line2.Hash or else  
                   Line1.Line.all /= Line2.Line.all then  
                    exit;  
                end if;  
                Last_Line1i := Last_Line1i + 1;  
                Last_Line2i := Last_Line2i + 1;  
            end loop Pre_Diff_Loop;

----Loop until we have gone over the entirety of both lists.

        Main_Diff_Loop:  
            loop

----Loop until we find a non-blank Line1 that has a matching Line2.

                Line1_Line2_Loop:  
                    loop

----Loop until we find the next non-blank Line1 of any flavor.

                        for I in Line1i + 1 .. List1'Last loop  
                            if not List1 (I).Blank then  
                                Line1  := List1 (I);  
                                Line1i := I;  
                                goto Have_Line1;  
                            end if;  
                        end loop;  
                        exit Main_Diff_Loop;    -- Can't find a next Line1.

----We get here when we have a Line1 and we need to find a matchine Line2.

                        <<Have_Line1>> null;  
                        for I in Line2i + 1 .. List2'Last loop  
                            Line2 := List2 (I);  
                            if not Line2.Blank then  
                                if Line1.Hash = Line2.Hash and then  
                                   Line1.Line.all = Line2.Line.all then  
                                    Line2i := I;  
                                   exit Line1_Line2_Loop;  
                                end if;  
                            end if;  
                        end loop;

----If we get here then there is no Line2 that matches Line1.  Loop and go
--  after the next non-blank Line1.

                    end loop Line1_Line2_Loop;

----Come here when we have Line1 = Line2 and we now want to expand the
--  region of match.  Loop backwards looking for a line that doesn't match.
--  Then report that region of difference.

                <<Have_A_Match>> null;  
                Tmp1 := Line1i;  
                Tmp2 := Line2i;

----Skip backwards from the match looking for lines that aren't blank and which
--  don't match.

                Post_Diff_Loop1:  
                    loop  
                        loop  
                            if Line1i - 1 <= Last_Line1i then  
                                exit Post_Diff_Loop1;  
                            end if;  
                            Line1 := List1 (Line1i - 1);  
                            if not Line1.Blank then  
                                exit;  
                            end if;  
                            Line1i := Line1i - 1;  
                        end loop;  
                        loop  
                            if Line2i - 1 <= Last_Line2i then  
                                exit Post_Diff_Loop1;  
                            end if;  
                            Line2 := List2 (Line2i - 1);  
                            if not Line2.Blank then  
                                exit;  
                            end if;  
                            Line2i := Line2i - 1;  
                        end loop;  
                        if Line1.Hash /= Line2.Hash or else  
                           Line1.Line.all /= Line2.Line.all then  
                            exit Post_Diff_Loop1;  
                        end if;  
                        Line1i := Line1i - 1;  
                        Line2i := Line2i - 1;  
                    end loop Post_Diff_Loop1;

----Skip blanks at the start of the difference regions.

                while Last_Line1i + 1 < Line1i - 1 loop  
                    if not List1 (Last_Line1i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line1i := Last_Line1i + 1;  
                end loop;  
                while Last_Line2i + 1 < Line2i - 1 loop  
                    if not List1 (Last_Line2i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line2i := Last_Line2i + 1;  
                end loop;  
                Regions_Differ (List1 (Last_Line1i + 1 .. Line1i - 1),  
                                List2 (Last_Line2i + 1 .. Line2i - 1));

----Now loop forwards looking for a line that doesn't match.  That defines
--  our next starting point.

                Line1i := Tmp1;  
                Line2i := Tmp2;  
                Post_Diff_Loop2:  
                    loop  
                        loop  
                            if Line1i >= List1'Last then  
                                exit Post_Diff_Loop2;  
                            end if;  
                            Line1 := List1 (Line1i + 1);  
                            if not Line1.Blank then  
                                exit;  
                            end if;  
                            Line1i := Line1i + 1;  
                        end loop;  
                        loop  
                            if Line2i >= List2'Last then  
                                exit Post_Diff_Loop2;  
                            end if;  
                            Line2 := List2 (Line2i + 1);  
                            if not Line2.Blank then  
                                exit;  
                            end if;  
                            Line2i := Line2i + 1;  
                        end loop;  
                        if Line1.Hash /= Line2.Hash or else  
                           Line1.Line.all /= Line2.Line.all then  
                            exit Post_Diff_Loop2;  
                        end if;  
                        Line1i := Line1i + 1;  
                        Line2i := Line2i + 1;  
                    end loop Post_Diff_Loop2;  
                Last_Line1i := Line1i;  
                Last_Line2i := Line2i;

----Loop and go after the next matching region.

            end loop Main_Diff_Loop;

----Come here when we can't find a next-non-blank Line1.

        while Last_Line1i + 1 < List1'Last loop  
            if not List1 (Last_Line1i + 1).Blank then  
                exit;  
            end if;  
            Last_Line1i := Last_Line1i + 1;  
        end loop;  
        while Last_Line2i + 1 < List2'Last loop  
            if not List1 (Last_Line2i + 1).Blank then  
                exit;  
            end if;  
            Last_Line2i := Last_Line2i + 1;  
        end loop;  
        Regions_Differ (List1 (Last_Line1i + 1 .. List1'Last),  
                        List2 (Last_Line2i + 1 .. List2'Last));

    end Diff_Ci;

--\x0c
    procedure Diff_Ib (List1 : File_Line_Array;  
                       List2 : File_Line_Array) is
------------------------------------------------------------------------------
--  List1               - Specifies the lines from file 1
--  List2               - Specifies the lines from file 2
--
-- Called to perform the main work of the Files_Diff routine.
--
-- Ignore_Case        => TRUE
-- Ignore_Blank_Lines => FALSE
------------------------------------------------------------------------------
        Line1       : File_Line;  
        Line2       : File_Line;  
        Line1i      : S_Natural := 0;  
        Line2i      : S_Natural := 0;  
        Last_Line1i : S_Natural := 0;  
        Last_Line2i : S_Natural := 0;  
        Tmp1        : S_Natural;  
        Tmp2        : S_Natural;  
    begin

----Check our assumptions.

        if List1'First /= List2'First then  
            raise Program_Error;        -- Our assumption fails.
        end if;

----Match lines starting at the top until we don't match any more.

        for I in List1'Range loop  
            if I > List2'Last then  
                exit;  
            end if;  
            Line1 := List1 (I);  
            Line2 := List2 (I);  
            if Line1.Hash /= Line2.Hash or else  
               not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                exit;  
            end if;  
            Last_Line1i := I;  
            Last_Line2i := I;  
        end loop;

----Loop until we have gone over the entirety of both lists.

        Main_Diff_Loop:  
            loop

----Loop until we find a non-blank Line1 that has a matching Line2.

                Line1_Line2_Loop:  
                    loop

----Loop until we find the next non-blank Line1 of any flavor.

                        for I in Line1i + 1 .. List1'Last loop  
                            if not List1 (I).Blank then  
                                Line1  := List1 (I);  
                                Line1i := I;  
                                goto Have_Line1;  
                            end if;  
                        end loop;  
                        exit Main_Diff_Loop;    -- Can't find a next Line1.

----We get here when we have a Line1 and we need to find a matchine Line2.

                        <<Have_Line1>> null;  
                        for I in Line2i + 1 .. List2'Last loop  
                            Line2 := List2 (I);  
                            if not Line2.Blank then  
                                if Line1.Hash = Line2.Hash and then  
                                   Equal_Uc (Line1.Line.all,  
                                            Line2.Line.all) then  
                                    Line2i := I;  
                                    exit Line1_Line2_Loop;  
                                end if;  
                            end if;  
                        end loop;

----If we get here then there is no Line2 that matches Line1.  Loop and go
--  after the next non-blank Line1.

                    end loop Line1_Line2_Loop;

----Come here when we have Line1 = Line2 and we now want to expand the
--  region of match.  Loop backwards looking for a line that doesn't match.
--  Then report that region of difference.

                <<Have_A_Match>> null;  
                Tmp1 := Line1i;  
                Tmp2 := Line2i;  
                while Line1i - 1 > Last_Line1i and then  
                         Line2i - 1 > Last_Line2i loop  
                    Line1 := List1 (Line1i - 1);  
                    Line2 := List2 (Line2i - 1);  
                    if Line1.Hash /= Line2.Hash or else  
                       not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                        exit;  
                    end if;  
                    Line1i := Line1i - 1;  
                    Line2i := Line2i - 1;  
                end loop;  
                Regions_Differ (List1 (Last_Line1i + 1 .. Line1i - 1),  
                                List2 (Last_Line2i + 1 .. Line2i - 1));

----Now loop forwards looking for a line that doesn't match.  That defines
--  our next starting point.

                Line1i := Tmp1;  
                Line2i := Tmp2;  
                while Line1i < List1'Last and then  
                         Line2i < List2'Last loop  
                    Line1 := List1 (Line1i + 1);  
                    Line2 := List2 (Line2i + 1);  
                    if Line1.Hash /= Line2.Hash or else  
                       not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                        exit;  
                    end if;  
                    Line1i := Line1i + 1;  
                    Line2i := Line2i + 1;  
                end loop;  
                Last_Line1i := Line1i;  
                Last_Line2i := Line2i;

----Loop and go after the next matching region.

            end loop Main_Diff_Loop;

----Come here when we can't find a next-non-blank Line1.

        Regions_Differ (List1 (Last_Line1i + 1 .. List1'Last),  
                        List2 (Last_Line2i + 1 .. List2'Last));

    end Diff_Ib;

--\x0c
    procedure Diff_Ii (List1 : File_Line_Array;  
                       List2 : File_Line_Array) is
------------------------------------------------------------------------------
--  List1               - Specifies the lines from file 1
--  List2               - Specifies the lines from file 2
--
-- Called to perform the main work of the Files_Diff routine.
--
-- Ignore_Case        => TRUE
-- Ignore_Blank_Lines => TRUE
------------------------------------------------------------------------------
        Line1       : File_Line;  
        Line2       : File_Line;  
        Line1i      : S_Natural := 0;  
        Line2i      : S_Natural := 0;  
        Last_Line1i : S_Natural := 0;  
        Last_Line2i : S_Natural := 0;  
        Tmp1        : S_Natural;  
        Tmp2        : S_Natural;  
    begin

----Check our assumptions.

        if List1'First /= List2'First then  
            raise Program_Error;        -- Our assumption fails.
        end if;

----Match lines starting at the top until we don't match any more.

        Pre_Diff_Loop:  
            loop  
                loop  
                    if Last_Line1i = List1'Last then  
                        exit Pre_Diff_Loop;  
                    elsif not List1 (Last_Line1i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line1i := Last_Line1i + 1;  
                end loop;  
                Line1 := List1 (Last_Line1i + 1);  
                loop  
                    if Last_Line2i = List1'Last then  
                        exit Pre_Diff_Loop;  
                    elsif not List1 (Last_Line2i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line2i := Last_Line2i + 1;  
                end loop;  
                Line2 := List1 (Last_Line2i + 1);  
                if Line1.Hash /= Line2.Hash or else  
                   not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                    exit;  
                end if;  
                Last_Line1i := Last_Line1i + 1;  
                Last_Line2i := Last_Line2i + 1;  
            end loop Pre_Diff_Loop;

----Loop until we have gone over the entirety of both lists.

        Main_Diff_Loop:  
            loop

----Loop until we find a non-blank Line1 that has a matching Line2.

                Line1_Line2_Loop:                     loop

----Loop until we find the next non-blank Line1 of any flavor.

                        for I in Line1i + 1 .. List1'Last loop  
                            if not List1 (I).Blank then  
                                Line1  := List1 (I);  
                                Line1i := I;  
                                goto Have_Line1;  
                            end if;  
                        end loop;  
                        exit Main_Diff_Loop;    -- Can't find a next Line1.

----We get here when we have a Line1 and we need to find a matchine Line2.

                        <<Have_Line1>> null;  
                        for I in Line2i + 1 .. List2'Last loop  
                            Line2 := List2 (I);  
                            if not Line2.Blank then  
                                if Line1.Hash = Line2.Hash and then  
                                   Equal_Uc (Line1.Line.all,  
                                            Line2.Line.all) then  
                                    Line2i := I;  
                                    exit Line1_Line2_Loop;  
                                end if;  
                            end if;  
                        end loop;

----If we get here then there is no Line2 that matches Line1.  Loop and go
--  after the next non-blank Line1.

                    end loop Line1_Line2_Loop;

----Come here when we have Line1 = Line2 and we now want to expand the
--  region of match.  Loop backwards looking for a line that doesn't match.
--  Then report that region of difference.

                <<Have_A_Match>> null;  
                Tmp1 := Line1i;  
                Tmp2 := Line2i;

----Skip backwards from the match looking for lines that aren't blank and which
--  don't match.

                Post_Diff_Loop1:  
                    loop  
                        loop  
                            if Line1i - 1 <= Last_Line1i then  
                                exit Post_Diff_Loop1;  
                            end if;  
                            Line1 := List1 (Line1i - 1);  
                            if not Line1.Blank then  
                                exit;  
                            end if;  
                            Line1i := Line1i - 1;  
                        end loop;  
                        loop  
                            if Line2i - 1 <= Last_Line2i then  
                                exit Post_Diff_Loop1;  
                            end if;  
                            Line2 := List2 (Line2i - 1);  
                            if not Line2.Blank then  
                                exit;  
                            end if;  
                            Line2i := Line2i - 1;  
                        end loop;  
                        if Line1.Hash /= Line2.Hash or else  
                           not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                            exit Post_Diff_Loop1;  
                        end if;  
                        Line1i := Line1i - 1;  
                        Line2i := Line2i - 1;  
                    end loop Post_Diff_Loop1;

----Skip blanks at the start of the difference regions.

                while Last_Line1i + 1 < Line1i - 1 loop  
                    if not List1 (Last_Line1i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line1i := Last_Line1i + 1;  
                end loop;  
                while Last_Line2i + 1 < Line2i - 1 loop  
                    if not List1 (Last_Line2i + 1).Blank then  
                        exit;  
                    end if;  
                    Last_Line2i := Last_Line2i + 1;  
                end loop;  
                Regions_Differ (List1 (Last_Line1i + 1 .. Line1i - 1),  
                                List2 (Last_Line2i + 1 .. Line2i - 1));

----Now loop forwards looking for a line that doesn't match.  That defines
--  our next starting point.

                Line1i := Tmp1;  
                Line2i := Tmp2;  
                Post_Diff_Loop2:  
                    loop  
                        loop  
                            if Line1i >= List1'Last then  
                                exit Post_Diff_Loop2;  
                            end if;  
                            Line1 := List1 (Line1i + 1);  
                            if not Line1.Blank then  
                                exit;  
                            end if;  
                            Line1i := Line1i + 1;  
                        end loop;  
                        loop  
                            if Line2i >= List2'Last then  
                                exit Post_Diff_Loop2;  
                            end if;  
                            Line2 := List2 (Line2i + 1);  
                            if not Line2.Blank then  
                                exit;  
                            end if;  
                            Line2i := Line2i + 1;  
                        end loop;  
                        if Line1.Hash /= Line2.Hash or else  
                           not Equal_Uc (Line1.Line.all, Line2.Line.all) then  
                            exit Post_Diff_Loop2;  
                        end if;  
                        Line1i := Line1i + 1;  
                        Line2i := Line2i + 1;  
                    end loop Post_Diff_Loop2;  
                Last_Line1i := Line1i;  
                Last_Line2i := Line2i;

----Loop and go after the next matching region.

            end loop Main_Diff_Loop;

----Come here when we can't find a next-non-blank Line1.

        while Last_Line1i + 1 < List1'Last loop  
            if not List1 (Last_Line1i + 1).Blank then  
                exit;  
            end if;  
            Last_Line1i := Last_Line1i + 1;  
        end loop;  
        while Last_Line2i + 1 < List2'Last loop  
            if not List1 (Last_Line2i + 1).Blank then  
                exit;  
            end if;  
            Last_Line2i := Last_Line2i + 1;  
        end loop;  
        Regions_Differ (List1 (Last_Line1i + 1 .. List1'Last),  
                        List2 (Last_Line2i + 1 .. List2'Last));

    end Diff_Ii;

--\x0c
    procedure Files_Diff (File1              : String;  
                          File2              : String;  
                          Ignore_Case        : Boolean;  
                          Ignore_Blank_Lines : Boolean) is
------------------------------------------------------------------------------
--  File1               - Specifies the name of the first file
--  File2               - Specifies the name of the second file
--  Ignore_Case         - Specifies TRUE if 'a' = 'A'
--  Ignore_Blank_Lines  - Specifies TRUE if blanks lines "don't exist"
--
-- We do a simple minded "diff" of two files and send the output to the log.
-- We don't expect to handle megabytes of stuff so don't be surprised if we
-- blow up on very large files.  We bring the entirity of both files into
-- memory.
------------------------------------------------------------------------------
        F1 : Text_Io.File_Type;  
        F2 : Text_Io.File_Type;

        List1  : File_Line_List;  
        List2  : File_Line_List;  
        List1i : S_Natural;  
        List2i : S_Natural;

    begin

----Open the files for reading.

        Put_Line ("::: diff( " & File1 & ", " & File2 & " );");  
        begin  
            Text_Io.Open (F1, Text_Io.In_File, File1);  
        exception  
            when others =>  
                Put_Error ("Could not open file to compare [" & File1 & "}.");  
                return;  
        end;  
        begin  
            Text_Io.Open (F2, Text_Io.In_File, File2);  
        exception  
            when others =>  
                Put_Error ("Could not open file to compare [" & File2 & "}.");  
                Text_Io.Close (F1);  
                return;  
        end;

----Read in the files.

        Read_File (F1, List1, List1i, Ignore_Case);  
        Read_File (F2, List2, List2i, Ignore_Case);

----Close the files.

        Text_Io.Close (F1);  
        Text_Io.Close (F2);

----Diff the files.

        if Ignore_Case then  
            if Ignore_Blank_Lines then  
                Diff_Ii (List1 (1 .. List1i), List2 (1 .. List2i));  
            else  
                Diff_Ib (List1 (1 .. List1i), List2 (1 .. List2i));  
            end if;  
        else  
            if Ignore_Blank_Lines then  
                Diff_Ci (List1 (1 .. List1i), List2 (1 .. List2i));  
            else  
                Diff_Cb (List1 (1 .. List1i), List2 (1 .. List2i));  
            end if;  
        end if;

----Free our storage and return.

        Free_File_Line_List (List1);  
        Free_File_Line_List (List2);

    exception

        when others =>  
            Free_File_Line_List (List1);  
            Free_File_Line_List (List2);  
            Put_Line ("%%% Unexpected exception in Text_Io.Files_Diff?");  
            Text_Io.Close (F1);  
            Text_Io.Close (F2);  
            return;

    end Files_Diff;

--\x0c
    function Change_Wildcards (Wild_In : String) return String is
------------------------------------------------------------------------------
--  Wild_In - Specifies a wildcard file name in R1000 format
--
-- Returns the local equivalent to the R1000 wildcarded string.
------------------------------------------------------------------------------
--/ if Unix then
--//
--//         Wild_Out  : String (1 .. Wild_In'Length * 3);
--//         Wild_Outi : Natural := 0;
--//         Wild_Ini  : Natural := Wild_In'First;
--//
--/ end if;
    begin

--/ if R1000 then

        return Wild_In;

--/ elsif Unix then
--//
--//         while Wild_Ini <= Wild_In'Last loop
--//             if Wild_In (Wild_Ini) = '^' then
--//                 Wild_Ini := Wild_Ini + 1;
--//                 if Wild_Ini > Wild_In'Last then
--//                     Wild_Out (Wild_Outi + 1 .. Wild_Outi + 2) := "..";
--//                     Wild_Outi := Wild_Outi + 2;
--//                 else
--//                     if Wild_In (Wild_Ini) = '.' then
--//                         Wild_Ini := Wild_Ini + 1;
--//                     end if;
--//                     Wild_Out (Wild_Outi + 1 .. Wild_Outi + 3) := "../";
--//                     Wild_Outi := Wild_Outi + 3;
--//                 end if;
--//             elsif Wild_In (Wild_Ini) = '$' then
--//                 Wild_Ini := Wild_Ini + 1;
--//                 if Wild_Ini > Wild_In'Last then
--//                     Wild_Outi                        := Wild_Outi + 1;
--//                     Wild_Out (Wild_Outi .. Wild_Outi) := ".";
--//                 else
--//                     if Wild_In (Wild_Ini) = '.' then
--//                         Wild_Ini := Wild_Ini + 1;
--//                     end if;
--//                     Wild_Out (Wild_Outi + 1 .. Wild_Outi + 2) := "./";
--//                     Wild_Outi := Wild_Outi + 2;
--//                 end if;
--//             elsif Wild_In (Wild_Ini) = '.' then
--//                 Wild_Ini             := Wild_Ini + 1;
--//                 Wild_Outi            := Wild_Outi + 1;
--//                 Wild_Out (Wild_Outi) := '/';
--//             elsif Wild_In (Wild_Ini) = '@' then
--//                 Wild_Ini             := Wild_Ini + 1;
--//                 Wild_Outi            := Wild_Outi + 1;
--//                 Wild_Out (Wild_Outi) := '*';
--//             else
--//                 Wild_Outi            := Wild_Outi + 1;
--//                 Wild_Out (Wild_Outi) := Wild_In (Wild_Ini);
--//             end if;
--//         end loop;
--//         return Wild_Out (1 .. Wild_Outi);
--//
--/ else
--//
--//         Need_Something_Here;
--//
--/ end if;

    end Change_Wildcards;

--\x0c
    procedure Resolve_Wildcards (Names : String;  
                                 Extra : String) is
------------------------------------------------------------------------------
--  Names   - Specifies the name with wildcards
--  Extra   - Specifies extra stuff to go onto the end of the Name string
--
-- Called to find all of the files referenced by the Name string.
-- We process the Name string.  It is expected to be in R1000 wildcard
-- notation ($/^/./@ only are allowed).  We change it to be suitable for
-- the current system and then we append the Extra to it.
-- Usage:
--      Resolve_Wildcards( "@", ".exe" );
--      while not Wild_Done loop
--          do_something( Wild_Current );
--          Wild_Next;
--      end loop;
------------------------------------------------------------------------------
--/ if R1000 then

        Wild_Iter : Directory_Tools.Object.Iterator;  
        Name      : File_Name;  
        Last      : File_Name;  
        Str       : String_Pointer;

--/ elsif Unix then
--//
--//         File  : Text_Io.File_Type;
--//         Line  : String (1 .. 1024);
--//         Linei : Natural;
--//         Name  : File_Name;
--//         Last  : File_Name;
--//         Str   : String_Pointer;
--//
--/ end if;
    begin

--/ if R1000 then

----Resolve the wildcards and check for errors.

        Wild_Iter := Directory_Tools.Naming.Resolution (Names & Extra);  
        if Directory_Tools.Object.Is_Bad (Wild_Iter) then  
            Put_Error ("Could not locate wildcards [" & Names & Extra & "].");  
            Put_Error ("Error: " & Directory_Tools.Object.Message (Wild_Iter));  
            raise Test_Io_Error;  
        end if;

----Put all of the names onto our list.

        Name_List := null;  
        Last      := null;  
        while not Directory_Tools.Object.Done (Wild_Iter) loop  
            Str  := new String'(Directory_Tools.Naming.Full_Name  
                                   (Directory_Tools.Object.Value (Wild_Iter)));  
            Name := new File_Name_Rec'(Next => null,  
                                       Name => Str);  
            if Last /= null then  
                Last.Next := Name;  
            end if;  
            Last := Name;  
            if Name_List = null then  
                Name_List := Name;  
            end if;  
            Directory_Tools.Object.Next (Wild_Iter);  
        end loop;

--/ elsif TeleGen2 and then Unix then
--//
--// ----Resolve the wildcards and check for errors.
--//
--//         if System_Calls.System ("/bin/ls -l " &
--//                                 Change_Wildcards (Names) & Extra &
--//                                 " > ./_Wild_Test_Io") < 0 then
--//             Put_Error ("Could not execute ls to resolve wildcards?");
--//             Put_Error (Error_Messages.Sys_Error_Message (Error_Messages.Errno));
--//             raise Test_Io_Error;
--//         end if;
--//
--// ----Put all of the names onto our list.
--//
--//         Text_Io.Open (File, Text_Io.In_File, "./_Wild_Test_Io");
--//         Text_Io.Get_Line (File, Line, Linei);
--//         Name_List := null;
--//         Last      := null;
--//         while not Text_Io.End_Of_File (File) loop
--//             Text_Io.Get_Line (File, Line, Linei);
--//             for I in reverse 1 .. Linei loop
--//                 if Line (I) /= ' ' then
--//                     Linei := I;
--//                     exit;
--//                 end if;
--//             end loop;
--//             for I in reverse 1 .. Linei loop
--//                 if Line (I) = ' ' then
--//                     Str := new String'(Line (I + 1 .. Linei));
--//                     exit;
--//                 end if;
--//             end loop;
--//             Name := new File_Name_Rec' (Next => null,
--//                                         Name => Str);
--//             if Last /= null then
--//                 Last.Next := Name;
--//             end if;
--//             Last := Name;
--//             if Name_List = null then
--//                 Name_List := Name;
--//             end if;
--//         end loop;
--//         Text_Io.Close (File);
--//
--/ else
--//
--//         Need_Something_Here;
--//
--/ end if;

    end Resolve_Wildcards;

--\x0c
    procedure Wild_Next is
------------------------------------------------------------------------------
-- Called to progress to the next wildcard.
------------------------------------------------------------------------------
        Name : File_Name;  
    begin

        if Name_List = null then  
            raise Test_Io_Error;  
        end if;  
        Name      := Name_List;  
        Name_List := Name_List.Next;  
        Free_File_Name (Name);

    end Wild_Next;

--\x0c
    function Wild_Current return String is
------------------------------------------------------------------------------
-- Called to obtain the current value of the wildcard.
------------------------------------------------------------------------------
    begin

        if Name_List = null then  
            raise Test_Io_Error;  
        end if;  
        return Name_List.Name.all;

    end Wild_Current;

--\x0c
    function Wild_Done return Boolean is
------------------------------------------------------------------------------
-- Called to see if there are more wildcards available.
------------------------------------------------------------------------------
    begin

        return Name_List = null;

    end Wild_Done;

--\x0c
end Test_Io;  

E3 Meta Data

    nblk1=6e
    nid=6e
    hdr6=da
        [0x00] rec0=1e rec1=00 rec2=01 rec3=03e
        [0x01] rec0=10 rec1=00 rec2=02 rec3=044
        [0x02] rec0=1d rec1=00 rec2=03 rec3=00e
        [0x03] rec0=00 rec1=00 rec2=6d rec3=014
        [0x04] rec0=21 rec1=00 rec2=04 rec3=070
        [0x05] rec0=00 rec1=00 rec2=6c rec3=00a
        [0x06] rec0=1d rec1=00 rec2=6b rec3=06a
        [0x07] rec0=01 rec1=00 rec2=05 rec3=008
        [0x08] rec0=19 rec1=00 rec2=06 rec3=00a
        [0x09] rec0=1b rec1=00 rec2=07 rec3=056
        [0x0a] rec0=1c rec1=00 rec2=08 rec3=00e
        [0x0b] rec0=1c rec1=00 rec2=09 rec3=05a
        [0x0c] rec0=16 rec1=00 rec2=0a rec3=014
        [0x0d] rec0=1a rec1=00 rec2=0b rec3=06e
        [0x0e] rec0=00 rec1=00 rec2=6a rec3=00c
        [0x0f] rec0=25 rec1=00 rec2=0c rec3=006
        [0x10] rec0=00 rec1=00 rec2=69 rec3=002
        [0x11] rec0=19 rec1=00 rec2=0d rec3=096
        [0x12] rec0=1d rec1=00 rec2=0e rec3=01a
        [0x13] rec0=1f rec1=00 rec2=0f rec3=052
        [0x14] rec0=1f rec1=00 rec2=10 rec3=01a
        [0x15] rec0=1f rec1=00 rec2=11 rec3=06a
        [0x16] rec0=1b rec1=00 rec2=12 rec3=022
        [0x17] rec0=1a rec1=00 rec2=13 rec3=07a
        [0x18] rec0=1b rec1=00 rec2=14 rec3=036
        [0x19] rec0=1b rec1=00 rec2=15 rec3=026
        [0x1a] rec0=18 rec1=00 rec2=16 rec3=06a
        [0x1b] rec0=24 rec1=00 rec2=17 rec3=02a
        [0x1c] rec0=25 rec1=00 rec2=18 rec3=024
        [0x1d] rec0=1c rec1=00 rec2=19 rec3=006
        [0x1e] rec0=21 rec1=00 rec2=1a rec3=01e
        [0x1f] rec0=19 rec1=00 rec2=1b rec3=008
        [0x20] rec0=00 rec1=00 rec2=68 rec3=004
        [0x21] rec0=1b rec1=00 rec2=1c rec3=050
        [0x22] rec0=00 rec1=00 rec2=67 rec3=004
        [0x23] rec0=14 rec1=00 rec2=1d rec3=02c
        [0x24] rec0=22 rec1=00 rec2=1e rec3=020
        [0x25] rec0=19 rec1=00 rec2=1f rec3=012
        [0x26] rec0=00 rec1=00 rec2=66 rec3=024
        [0x27] rec0=1d rec1=00 rec2=20 rec3=02a
        [0x28] rec0=18 rec1=00 rec2=21 rec3=00e
        [0x29] rec0=16 rec1=00 rec2=22 rec3=034
        [0x2a] rec0=1b rec1=00 rec2=23 rec3=06c
        [0x2b] rec0=1f rec1=00 rec2=24 rec3=022
        [0x2c] rec0=1c rec1=00 rec2=25 rec3=040
        [0x2d] rec0=00 rec1=00 rec2=65 rec3=002
        [0x2e] rec0=1c rec1=00 rec2=26 rec3=000
        [0x2f] rec0=19 rec1=00 rec2=27 rec3=004
        [0x30] rec0=16 rec1=00 rec2=28 rec3=040
        [0x31] rec0=18 rec1=00 rec2=29 rec3=01e
        [0x32] rec0=1d rec1=00 rec2=2a rec3=01e
        [0x33] rec0=00 rec1=00 rec2=64 rec3=00c
        [0x34] rec0=17 rec1=00 rec2=2b rec3=00c
        [0x35] rec0=00 rec1=00 rec2=63 rec3=016
        [0x36] rec0=1b rec1=00 rec2=2c rec3=042
        [0x37] rec0=23 rec1=00 rec2=2d rec3=020
        [0x38] rec0=1c rec1=00 rec2=2e rec3=036
        [0x39] rec0=01 rec1=00 rec2=62 rec3=00e
        [0x3a] rec0=21 rec1=00 rec2=2f rec3=05e
        [0x3b] rec0=00 rec1=00 rec2=61 rec3=01c
        [0x3c] rec0=17 rec1=00 rec2=30 rec3=060
        [0x3d] rec0=00 rec1=00 rec2=60 rec3=002
        [0x3e] rec0=17 rec1=00 rec2=31 rec3=054
        [0x3f] rec0=17 rec1=00 rec2=32 rec3=00a
        [0x40] rec0=1c rec1=00 rec2=33 rec3=05e
        [0x41] rec0=1d rec1=00 rec2=34 rec3=01c
        [0x42] rec0=00 rec1=00 rec2=5f rec3=048
        [0x43] rec0=1c rec1=00 rec2=35 rec3=03e
        [0x44] rec0=14 rec1=00 rec2=36 rec3=03c
        [0x45] rec0=00 rec1=00 rec2=5e rec3=002
        [0x46] rec0=1b rec1=00 rec2=37 rec3=014
        [0x47] rec0=14 rec1=00 rec2=38 rec3=032
        [0x48] rec0=18 rec1=00 rec2=39 rec3=016
        [0x49] rec0=16 rec1=00 rec2=3a rec3=020
        [0x4a] rec0=19 rec1=00 rec2=3b rec3=054
        [0x4b] rec0=1a rec1=00 rec2=3c rec3=042
        [0x4c] rec0=01 rec1=00 rec2=5d rec3=01a
        [0x4d] rec0=22 rec1=00 rec2=3d rec3=012
        [0x4e] rec0=00 rec1=00 rec2=5c rec3=01c
        [0x4f] rec0=16 rec1=00 rec2=3e rec3=024
        [0x50] rec0=00 rec1=00 rec2=5b rec3=002
        [0x51] rec0=17 rec1=00 rec2=3f rec3=024
        [0x52] rec0=15 rec1=00 rec2=40 rec3=02a
        [0x53] rec0=1d rec1=00 rec2=41 rec3=03a
        [0x54] rec0=1c rec1=00 rec2=42 rec3=06a
        [0x55] rec0=02 rec1=00 rec2=5a rec3=006
        [0x56] rec0=1a rec1=00 rec2=43 rec3=002
        [0x57] rec0=15 rec1=00 rec2=44 rec3=044
        [0x58] rec0=00 rec1=00 rec2=59 rec3=002
        [0x59] rec0=1a rec1=00 rec2=45 rec3=06e
        [0x5a] rec0=14 rec1=00 rec2=46 rec3=082
        [0x5b] rec0=16 rec1=00 rec2=47 rec3=086
        [0x5c] rec0=18 rec1=00 rec2=48 rec3=02c
        [0x5d] rec0=19 rec1=00 rec2=49 rec3=026
        [0x5e] rec0=16 rec1=00 rec2=4a rec3=082
        [0x5f] rec0=1d rec1=00 rec2=4b rec3=014
        [0x60] rec0=00 rec1=00 rec2=58 rec3=004
        [0x61] rec0=21 rec1=00 rec2=4c rec3=020
        [0x62] rec0=1f rec1=00 rec2=4d rec3=008
        [0x63] rec0=19 rec1=00 rec2=4e rec3=006
    tail 0x21500a39e81978d6cab1b 0x42a00088462063203
Free Block Chain:
  0x6e: 0000  00 00 09 00 27 87 a1 e0 05 00 34 3e 00 a2 0f 00  ┆    '     4>    ┆