-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

with SPARK_IO;
with Ada.Characters.Handling;
with FileSystem;
with ScreenEcho;
with E_Strings;
with CommandLineData;
with ContextManager.Ops;

use type SPARK_IO.File_Status;
use type FileSystem.Typ_File_Spec_Status;

package body SparkHTML is

   Error_Reference_Filename : constant String := "errors.htm";

   -- These states are used in the parsing of the report file.
   type Report_File_States is (
                               Report_Just_Started,
                               Report_Banner_Started,
                               Report_Banner_Finished,
                               Report_Date_Found,
                               Report_Options_Found,
                               Report_Index_File_Found,
                               Report_Warning_File_Found,
                               Report_Target_Compiler_Data_Found,
                               Report_Target_Config_File_Found,
                               Report_Source_Extension_Found,
                               Report_Listing_Extension_Found,
                               Report_Dictionary_Found,
                               Report_Report_File_Found,
                               Report_HTML_Flag_Found,
                               Report_Statistics_Flag_Found,
                               Report_FDL_Identifiers_Found,
                               Report_Flow_Analysis_Found,
                               Report_Language_Option_Found,
                               Report_Annotation_Character_Found,
                               Report_Selected_Files_Started,
                               Report_Index_Files_Started,
                               Report_Meta_Files_Started,
                               Report_Warning_List_Started,
                               Report_Source_List_Started,
                               Report_Missing_Files_Started,
                               Report_Source_File_Started,
                               Report_Listing_Filename_Found,
                               Report_Unit_Name_Found,
                               Report_No_Units_Found,
                               Report_Unit_Type_Found,
                               Report_Analysed_Message_Found,
                               Report_Start_Of_Errors,
                               Report_End_Of_Errors,
                               Report_Justifications_Summary_Found,
                               Report_Summarized_Warnings_Found,
                               Report_Line_Header_Found,
                               Report_Error_Source_Line_Found,
                               Report_Error_Source_Pointer_Found,
                               Report_Error_Message_Found,
                               Report_Blank_After_Error_Found,
                               Report_Target_Config_List_Started,
                               Report_Target_Error_Line,
                               Report_Target_Error_Next_Line,
                               Report_End_Of_Report_File);

   type Listing_File_States is (
                                Listing_Just_Started,
                                Listing_Banner_Started,
                                Listing_Banner_Finished,
                                Listing_Date_Found,
                                Listing_Line_Heading_Found,
                                Listing_Source_Line_Found,
                                Listing_Error_Source_Pointer_Found,
                                Listing_Error_Message_Found,
                                Listing_End_Of_Listing_File);

   Generate_HTML : Boolean := True;
   -- Set to false if fatal HTML error occurs to prevent further HTML generation.

   HTML_Work_Dir  : E_Strings.T;
   SPARK_Work_Dir : E_Strings.T;

   ---------------------------------------------------------------------------

   -- This function returns true if and only if C is a character representing
   -- a digit.

   function Digit (C : Character) return Boolean is
   begin
      return C in '0' .. '9';
   end Digit;

   ---------------------------------------------------------------------------

   -- This function prepends the given string with the name of the selected
   -- direcotry into which HTML is being generated.  The appropriate
   -- target-dependant directory separator is also added.
   --
   -- No error checking is performed.  If the string overflows it is
   -- truncated by the Append_String routines.

   function HTML_Filename (Filename : E_Strings.T) return E_Strings.T
   --# global in CommandLineData.Content;
   is
      Return_Filename : E_Strings.T;
   begin
      Return_Filename := FileSystem.Start_Of_Directory;
      if CommandLineData.Content.Output_Directory then
         E_Strings.Append_Examiner_String (E_Str1 => Return_Filename,
                                           E_Str2 => CommandLineData.Content.Output_Directory_Name);
         E_Strings.Append_Examiner_String (E_Str1 => Return_Filename,
                                           E_Str2 => FileSystem.Directory_Separator);
      end if;
      E_Strings.Append_Examiner_String (E_Str1 => Return_Filename,
                                        E_Str2 => CommandLineData.Content.HTML_Directory);
      E_Strings.Append_Examiner_String (E_Str1 => Return_Filename,
                                        E_Str2 => FileSystem.Directory_Separator);
      E_Strings.Append_Examiner_String (E_Str1 => Return_Filename,
                                        E_Str2 => Filename);
      return Return_Filename;
   end HTML_Filename;

   ---------------------------------------------------------------------------

   -- This function returns the index of the first character on a line that isn't
   -- a space.
   --
   -- No error checking is necessary.  The for loop ensures that bounds aren't
   -- exceeded.

   function First_Char (The_String : E_Strings.T) return E_Strings.Positions is
      Pos : E_Strings.Positions;
   begin
      Pos := E_Strings.Positions'First;

      for I in E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => The_String) loop
         if E_Strings.Get_Element (E_Str => The_String,
                                   Pos   => I) /= ' ' then
            Pos := I;
            exit;
         end if;
      end loop;

      return Pos;
   end First_Char;

   ---------------------------------------------------------------------------

   -- Applies HTML_Name_Char to each character in the string.
   --
   -- No error checking is necessary.  The for loop ensures that bounds aren't
   -- exceeded.

   function HTML_Name (E_Str : E_Strings.T) return E_Strings.T is
      Out_String : E_Strings.T;

      -- This function returns the given character if the character can be used in
      -- an HTML name and '_' otherwise.
      -- Characters the are allowed in HTML names are letters ([A-Za-z]), digits ([0-9]
      -- hyphens ("-"), underscores ("_"), colons (":") and periods (".").
      --
      -- No error checking necessary.

      function HTML_Name_Char (C : Character) return Character is
         Out_Char : Character;
      begin
         if C in 'A' .. 'Z' or C in 'a' .. 'z' or C in '0' .. '9' or C = '-' or C = '_' or C = ':' or C = '.' then
            Out_Char := C;
         else
            Out_Char := '_';
         end if;
         return Out_Char;
      end HTML_Name_Char;

   begin
      Out_String := E_Strings.Empty_String;
      for I in E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => E_Str) loop
         E_Strings.Append_Char
           (E_Str => Out_String,
            Ch    => HTML_Name_Char (C => E_Strings.Get_Element (E_Str => E_Str,
                                                                 Pos   => I)));
      end loop;
      return Out_String;
   end HTML_Name;

   ---------------------------------------------------------------------------

   -- This function removes all occurrences of special HTML characters and replaces
   -- them with the HTML codes required to display them.
   -- The conversions done are:
   --
   --    >   becomes   &gt;
   --    <   becomes   &lt;
   --    &   becomes   &amp;
   --    "   becomes   &quot;
   --
   -- The for loop ensures that bounds of the input string aren't exceeded.  If
   -- string overflow occurs then the output string is truncated by the Append
   -- routines.

   function Convert_Special_HTML_Chars (Line : E_Strings.T) return E_Strings.T is
      Out_String : E_Strings.T;
   begin
      Out_String := E_Strings.Empty_String;

      for I in E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => Line) loop

         case E_Strings.Get_Element (E_Str => Line,
                                     Pos   => I) is

            when '<' =>
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "&lt;");
            when '>' =>
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "&gt;");
            when '&' =>
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "&amp;");
            when '"' =>
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "&quot;");
            when others =>
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
         end case;

      end loop;

      return Out_String;
   end Convert_Special_HTML_Chars;

   ---------------------------------------------------------------------------

   -- This function encloses the line given in HTML tags to make it bold type.
   --
   -- If the line given is too long for the tags to be added then it is
   -- returned unchanged.

   function HTML_Embolden (Line : E_Strings.T) return E_Strings.T is
      Out_String : E_Strings.T;
   begin
      if E_Strings.Get_Length (E_Str => Line) < E_Strings.Lengths'Last - 7 then
         Out_String := E_Strings.Copy_String (Str => "<B>");
         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Line);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => "</B>");
      else
         Out_String := Line;
      end if;

      return Out_String;
   end HTML_Embolden;

   ---------------------------------------------------------------------------

   -- This function does a target-specific append of a subdirectory name.
   -- The function works with or without trailing separators.
   --
   -- The path returned will always have a trailing separator.

   function Append_Directory_String (Path : E_Strings.T;
                                     Dir  : E_Strings.T) return E_Strings.T is
      Path_Out : E_Strings.T;
   begin
      Path_Out := Path;

      -- Add trailing separator if necessary.
      if E_Strings.Get_Element (E_Str => Path_Out,
                                Pos   => E_Strings.Get_Length (E_Str => Path_Out)) /=
        E_Strings.Get_Element
        (E_Str => FileSystem.Directory_Separator,
         Pos   => E_Strings.Get_Length (E_Str => FileSystem.Directory_Separator)) then
         E_Strings.Append_Examiner_String (E_Str1 => Path_Out,
                                           E_Str2 => FileSystem.Directory_Separator);
      end if;

      E_Strings.Append_Examiner_String (E_Str1 => Path_Out,
                                        E_Str2 => Dir);
      E_Strings.Append_Examiner_String (E_Str1 => Path_Out,
                                        E_Str2 => FileSystem.Directory_Separator);

      return Path_Out;
   end Append_Directory_String;

   ---------------------------------------------------------------------------
   -- This procedure takes two paths and returns the URI of the first path
   -- relative to the second path using the global value of the SPARK_Work_Dir as
   -- the current directory.
   --
   -- The directories passed must have trailing separators.  The directory returned has
   -- a trailing separator.  A separator is defined as being the first character
   -- of the Examiner_String returned by FileSystem.Directory_Separator.
   --
   -- For use on Windows, it is not possible to give a URI in all instances if
   -- the directories exist on two different drives.  For example, if the
   -- paths given are "C:\foo\" and "D:\bar\".
   --
   -- We could simply return the absolute path but if we then want to view
   -- the URI on a Unix system we will find that the path does not exist.
   --
   -- I therefore have included a "Success" parameter which is set to false if
   -- the paths are on 2 different drives.
   --
   procedure HTML_Relative_Dir
     (Dir_In, Relative_To_In : in     E_Strings.T;
      Dir_Relative           :    out E_Strings.T;
      Success_Out            :    out Boolean)
   --# global in SPARK_Work_Dir;
   --# derives Dir_Relative,
   --#         Success_Out  from Dir_In,
   --#                           Relative_To_In,
   --#                           SPARK_Work_Dir;
   is

      Dir, Relative_To      : E_Strings.T;
      Out_String            : E_Strings.T;
      Remaining_Dir         : E_Strings.T;
      Remaining_Relative_To : E_Strings.T;
      Popped_Directory      : E_Strings.T;

      Working_Device     : E_Strings.T;
      Dir_Device         : E_Strings.T;
      Relative_To_Device : E_Strings.T;

      I : E_Strings.Positions;

      Directory_Found          : Boolean;
      Working_Device_Found     : Boolean;
      Dir_Device_Found         : Boolean;
      Relative_To_Device_Found : Boolean;

      Success : Boolean;

      -- This function chops the first character from the Examiner_String.
      -- If the Empty_String is given then the Empty_String is returned.

      function Get_Rest (S : E_Strings.T) return E_Strings.T is
         Result : E_Strings.T;
      begin
         if E_Strings.Get_Length (E_Str => S) <= 1 then
            Result := E_Strings.Empty_String;
         else
            Result := E_Strings.Section (S, E_Strings.Positions'First + 1, E_Strings.Get_Length (E_Str => S) - 1);
         end if;

         return Result;
      end Get_Rest;

      -- This function looks for a device name and returns it.
      --
      -- If no device name is present then the Empty_String is returned.
      --
      -- A device name is ususally the name of a Windows drive (of the form "X:")
      function Get_Device_Prefix (Path : in E_Strings.T) return E_Strings.T is
         Colon_Found : Boolean;
         Pos         : E_Strings.Positions;
         Device      : E_Strings.T;
      begin
         if E_Strings.Get_Length (E_Str => Path) = 0 then

            Device := E_Strings.Empty_String;

         else
            Colon_Found := False;
            Pos         := E_Strings.Get_Length (E_Str => Path);

            for I in E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => Path) loop

               -- Have we found a colon?
               if E_Strings.Get_Element (E_Str => Path,
                                         Pos   => I) = ':' then
                  Colon_Found := True;
                  Pos         := I;
                  exit;
               end if;

               -- If we find a directory separator or StartOf_Directory then
               -- exit the loop with Colon_Found = False.
               if E_Strings.Get_Element (E_Str => Path,
                                         Pos   => I) =
                 E_Strings.Get_Element (E_Str => FileSystem.Start_Of_Directory,
                                        Pos   => 1)
                 or else E_Strings.Get_Element (E_Str => Path,
                                                Pos   => I) =
                 E_Strings.Get_Element (E_Str => FileSystem.Directory_Separator,
                                        Pos   => 1) then
                  exit;  -- Colon_Found is already False;
               end if;

            end loop;

            if Colon_Found then
               Device := E_Strings.Section (E_Str     => Path,
                                            Start_Pos => E_Strings.Positions'First,
                                            Length    => Pos);
            else
               -- Leave Path as it is.  Set Device to empty string.
               Device := E_Strings.Empty_String;
            end if;

         end if;

         return Device;
      end Get_Device_Prefix;

      -- This procedure does a Get_Device_Prefix and removes the device name from
      -- the path given.
      procedure Chop_Device_Prefix (Path   : in out E_Strings.T;
                                    Device :    out E_Strings.T;
                                    Found  :    out Boolean)
      --# derives Device,
      --#         Found,
      --#         Path   from Path;
      is
         Device_Name : E_Strings.T;
      begin
         Device_Name := Get_Device_Prefix (Path => Path);

         -- Get_Device_Prefix returns Empty_String if no device name was found.
         if E_Strings.Get_Length (E_Str => Device_Name) > 0 then

            Path   :=
              E_Strings.Section
              (Path,
               E_Strings.Get_Length (E_Str => Device_Name) + 1,
               E_Strings.Get_Length (E_Str => Path) - E_Strings.Get_Length (E_Str => Device_Name));
            Device := Device_Name;
            Found  := True;

         else
            Device := E_Strings.Empty_String;
            Found  := False;

         end if;
      end Chop_Device_Prefix;

      -- This function takes a pathname (must NOT be preceded by an device name) parameter.
      -- If the pathname is absolute (begins with a Directory_Separator character) then it is
      -- returned unchanged.  Otherwise it is returned with SPARK_Work_Dir prepended to it.
      -- The returned path will not have a device name.
      function Make_Absolute (Path : E_Strings.T) return E_Strings.T
      --# global in SPARK_Work_Dir;
      is
         Out_String : E_Strings.T;
         Device     : E_Strings.T;
      begin
         if Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => Path,
                                                                     Pos   => 1)) =
           Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => FileSystem.Directory_Separator,
                                                                    Pos   => 1)) then
            Out_String := Path;
         else
            -- Directory is relative to current - append Current_Dir
            Out_String := SPARK_Work_Dir;
            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => Path);
         end if;

         -- Remove device name if it exists.
         Device := Get_Device_Prefix (Path => Out_String);

         -- Length of device name is 0 if no device was found.
         if E_Strings.Get_Length (E_Str => Device) > 0 then
            Out_String :=
              E_Strings.Section
              (Out_String,
               E_Strings.Get_Length (E_Str => Device) + 1,
               E_Strings.Get_Length (E_Str => Out_String) - E_Strings.Get_Length (E_Str => Device));
         end if;

         return Out_String;
      end Make_Absolute;

      -- This procedure returns and removes the first subdirectory name from
      -- the given path.  The given path must not have an device name or a
      -- leading separator.  The popped subdirectory name will also not have a
      -- leading separator. The remaining directory name has no leading
      -- separator.
      --
      -- e.g. if the input path is "foo/bar/baz/" then the path returned
      -- will be "bar/baz/" and the subdirectory returned will be "foo".
      --
      -- If the URL flag is set then the separator '/' is used rather than
      -- a target specific separator.
      --
      -- Dir_Found is true if and only if a directory separator was found in the
      -- path.
      --
      -- If the path has length 0 or a directory separator is not found then
      --       - Path is returned unchanged
      --       - Dir is ELStrings.Empty_String
      --       - Dirfound is false
      --
      procedure Pop_Sub_Dir
        (Path      : in out E_Strings.T;
         URL       : in     Boolean;
         Dir       :    out E_Strings.T;
         Dir_Found :    out Boolean)
      --# derives Dir,
      --#         Dir_Found,
      --#         Path      from Path,
      --#                        URL;
      is
         Between_Separator : E_Strings.T;
         End_Separator     : E_Strings.T;
         Separator_Pos     : E_Strings.Positions;
         Separator_Found   : Boolean;
      begin
         if E_Strings.Get_Length (E_Str => Path) = 0 then

            Dir       := E_Strings.Empty_String;
            Dir_Found := False;

         else
            if URL then

               Between_Separator := E_Strings.Copy_String (Str => "/");
               End_Separator     := Between_Separator;

            else
               Between_Separator := FileSystem.Directory_Separator;
               End_Separator     := FileSystem.Directory_Separator;

            end if;

            E_Strings.Find_Examiner_Sub_String
              (E_Str         => Path,
               Search_String => Between_Separator,
               String_Found  => Separator_Found,
               String_Start  => Separator_Pos);

            if not Separator_Found then
               -- Maybe last directory?
               E_Strings.Find_Examiner_Sub_String
                 (E_Str         => Path,
                  Search_String => End_Separator,
                  String_Found  => Separator_Found,
                  String_Start  => Separator_Pos);
            end if;

            if Separator_Found then

               Dir       := E_Strings.Section (Path, E_Strings.Positions'First, Separator_Pos - 1);
               Path      := E_Strings.Section (Path, Separator_Pos + 1, E_Strings.Get_Length (E_Str => Path) - Separator_Pos);
               Dir_Found := True;

            else
               Dir       := E_Strings.Empty_String;
               Dir_Found := False;

            end if;

         end if;
      end Pop_Sub_Dir;

      -- This function removes the last directory in the URL given.
      -- The input path should have a trailing separator and the output
      -- path will also have a trailing separator.
      --
      -- If no separator is found then the string is returned unchanged.
      --
      -- e.g. Remove_Last_Directory ("foo/bar/baz/") = "foo/bar/"
      --
      -- This routine is used when removing "../" from pathnames.
      --
      -- NOTE: This only works with URLs - the directory separator must be '/'
      --

      function Remove_Last_Directory (Path : E_Strings.T) return E_Strings.T is
         Out_String : E_Strings.T;
         Pos        : E_Strings.Lengths;
      begin
         Out_String := Path;
         Pos        := E_Strings.Get_Length (E_Str => Path);

         -- Remember not to include the trailing Directory_Separator character in the search.
         for I in reverse E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => Path) - 1 loop
            if E_Strings.Get_Element (E_Str => Path,
                                      Pos   => I) = '/' then
               Pos := I;
               exit;
            end if;
         end loop;

         -- Pos will still equal Path.Length if separator was not found.
         if Pos /= E_Strings.Get_Length (E_Str => Path) then  -- separator was found
            Out_String := E_Strings.Section (E_Str     => Path,
                                             Start_Pos => E_Strings.Positions'First,
                                             Length    => Pos);
         end if;

         return Out_String;
      end Remove_Last_Directory;

      -- This function resolves a URL containing references to the previous
      -- directory "../" and the current directory "./".  The path given must have
      -- a trailing '/' character.
      -- The function works with or without a leading directory separator (this is
      -- copied if it exists.
      --
      -- NOTE: This function only works with URL's.  The directory separator
      --       used is always '/' and current and parent directories are
      --       "." and ".." respectively.

      function Remove_Dots (In_Path : E_Strings.T) return E_Strings.T is
         Sub_Dir_Found : Boolean;
         Path          : E_Strings.T;
         Out_Path      : E_Strings.T;
         Next_Dir      : E_Strings.T;
      begin
         Path     := In_Path;
         Out_Path := E_Strings.Empty_String;

         if E_Strings.Get_Element (E_Str => Path,
                                   Pos   => E_Strings.Positions'First) = '/' then
            -- Copy the leading separator.
            Path := Get_Rest (S => Path);
            E_Strings.Append_Char (E_Str => Out_Path,
                                   Ch    => '/');
         end if;

         loop

            Pop_Sub_Dir (Path      => Path,
                         URL       => True,
                         Dir       => Next_Dir,
                         Dir_Found => Sub_Dir_Found);

            exit when not Sub_Dir_Found;

            if E_Strings.Eq1_String (E_Str => Next_Dir,
                                     Str   => ".") then
               null; -- do nothing
            elsif E_Strings.Eq1_String (E_Str => Next_Dir,
                                        Str   => "..") then
               Out_Path := Remove_Last_Directory (Path => Out_Path);
            else
               E_Strings.Append_Examiner_String (E_Str1 => Out_Path,
                                                 E_Str2 => Next_Dir);
               E_Strings.Append_Char (E_Str => Out_Path,
                                      Ch    => '/');
            end if;

         end loop;

         return Out_Path;
      end Remove_Dots;

      -- This function converts a directory name into a URL.
      function Convert_To_URL (Path : E_Strings.T) return E_Strings.T is
      begin
         -- On Windows, pathnames might contain '\' which need
         -- to be transformed unto '/' to be a URL.  On other
         -- platforms, this is a no-op.
         return E_Strings.Translate (E_Str     => Path,
                                     From_Char => '\',
                                     To_Char   => '/');
      end Convert_To_URL;

   begin  -- HTML_Relative_Dir

      -- Initialise variables
      Success    := True;
      Out_String := E_Strings.Empty_String;

      -- Copy input parameters to local variables.
      Dir         := Dir_In;
      Relative_To := Relative_To_In;

      -- Get device names.
      -- Device names are removed from the input directories.
      -- SPARK_Work_Dir is not to be modified.

      Working_Device       := Get_Device_Prefix (Path => SPARK_Work_Dir);
      Working_Device_Found := E_Strings.Get_Length (E_Str => Working_Device) > 0;

      Chop_Device_Prefix (Path   => Dir,
                          Device => Dir_Device,
                          Found  => Dir_Device_Found);
      Chop_Device_Prefix (Path   => Relative_To,
                          Device => Relative_To_Device,
                          Found  => Relative_To_Device_Found);

      -- We cannot create links if files are on different NT drives or different
      -- VAX devices.
      if Dir_Device_Found then
         --Dir contains a device name.
         if Relative_To_Device_Found then
            -- Relative_To also contains a device name.  Are they the same?
            -- Fail if different devices.
            if not E_Strings.Eq_String (E_Str1 => Dir_Device,
                                        E_Str2 => Relative_To_Device) then
               Success := False;  -- Files are on different drives.
            end if;
         else
            -- Dir contains a device name and Relative_To is on the current drive.
            -- Is the current device equal to the device that Dir is on?
            if (not Working_Device_Found)
              or else (not E_Strings.Eq_String (E_Str1 => Dir_Device,
                                                E_Str2 => Working_Device)) then
               Success := False;
            end if;
         end if;
      else
         -- Dir does not contain a drive specification - does Relative_To?
         if Relative_To_Device_Found then
            -- Relative_To contains a device name and Dir is on the current drive.
            -- Is the current device equal to the device that Relative_To is on?
            if (not Working_Device_Found)
              or else (not E_Strings.Eq_String (E_Str1 => Relative_To_Device,
                                                E_Str2 => Working_Device)) then
               Success := False;
            end if;
         end if;
      end if;

      -- Do nothing (return empty string) if directories are identical, or if
      -- the previous checks failed.
      if (not E_Strings.Eq_String (E_Str1 => Dir,
                                   E_Str2 => Relative_To)) and then Success then
         -- Make directories absolute
         Dir         := Make_Absolute (Path => Dir);
         Relative_To := Make_Absolute (Path => Relative_To);

         -- Convert directories to URL's.
         Dir         := Convert_To_URL (Path => Dir);
         Relative_To := Convert_To_URL (Path => Relative_To);

         -- Remove "./" and "../" and make case-insensitive where required.
         Dir         := Remove_Dots (In_Path => Dir);
         Relative_To := Remove_Dots (In_Path => Relative_To);

         -- Initialize counter.
         I := E_Strings.Positions'First;

         -- Skip common prefix.  We want I to point to the character that begins
         -- the name of the first different subdirectory.  e.g. if we have
         -- /foo/bar/ and /foo/baz/ we want to point to the 'b'
         loop
            exit when I > E_Strings.Get_Length (E_Str => Dir)
              or else I > E_Strings.Get_Length (E_Str => Relative_To)
              or else E_Strings.Get_Element (E_Str => Dir,
                                             Pos   => I) /=
              E_Strings.Get_Element (E_Str => Relative_To,
                                     Pos   => I);
            I := I + 1;
         end loop;

         if I < E_Strings.Get_Length (E_Str => Dir) and then I < E_Strings.Get_Length (E_Str => Relative_To) then
            -- Back up to previous directory separator (in case we're comparing, e.g.
            -- \foo\bar and \foo\baz, in which case the common prefix is c:\foo\ba)
            while E_Strings.Get_Element (E_Str => Dir,
                                         Pos   => I) /= '/' and I > E_Strings.Positions'First loop
               I := I - 1;
            end loop;

            -- Now we want to point to just past the separator so
            I := I + 1;
         end if;

         -- Now remove the common ancestor directories.
         if I > E_Strings.Get_Length (E_Str => Dir) then
            Remaining_Dir := E_Strings.Empty_String;
         else
            Remaining_Dir := E_Strings.Section (Dir, I, (E_Strings.Get_Length (E_Str => Dir) - I) + 1);
         end if;

         if I > E_Strings.Get_Length (E_Str => Relative_To) then
            Remaining_Relative_To := E_Strings.Empty_String;
         else
            Remaining_Relative_To := E_Strings.Section (Relative_To, I, (E_Strings.Get_Length (E_Str => Relative_To) - I) + 1);
         end if;

         -- For each subdirectory remaining in Relative_To we add a "../" to Out_String
         loop
            --# accept Flow, 10, Popped_Directory, "Expected ineffective assignment to Popped_Directory";
            Pop_Sub_Dir (Path      => Remaining_Relative_To,
                         URL       => True,
                         Dir       => Popped_Directory,
                         Dir_Found => Directory_Found);
            --# end accept;

            exit when not Directory_Found;

            E_Strings.Append_String (E_Str => Out_String,
                                     Str   => "../");

         end loop;

         -- For each subdirectory remaining in Dir we add "<subdir>/"
         loop

            Pop_Sub_Dir (Path      => Remaining_Dir,
                         URL       => True,
                         Dir       => Popped_Directory,
                         Dir_Found => Directory_Found);
            exit when not Directory_Found;

            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => Popped_Directory);
            E_Strings.Append_String (E_Str => Out_String,
                                     Str   => "/");

         end loop;

      end if;

      Dir_Relative := Out_String;
      Success_Out  := Success;
   end HTML_Relative_Dir;  -- Flow error expected.  SPARK_Work_Dir is NOT updated.

   ---------------------------------------------------------------------------

   -- This procedure uses HTML_Relative_Dir to give the URI of a file relative
   -- to a given directory and the global SPARK_Work_Dir as the current directory.
   --
   -- The

   procedure HTML_Relative_File (File, Relative_To : in     E_Strings.T;
                                 File_Relative     :    out E_Strings.T;
                                 Success_Out       :    out Boolean)
   --# global in SPARK_Work_Dir;
   --# derives File_Relative,
   --#         Success_Out   from File,
   --#                            Relative_To,
   --#                            SPARK_Work_Dir;
   is
      Directory_Name    : E_Strings.T;
      Relative_Dir_Name : E_Strings.T;
      Filename          : E_Strings.T;
      Out_String        : E_Strings.T;
      Separator_Pos     : E_Strings.Positions;
      Success           : Boolean;
   begin
      Separator_Pos := E_Strings.Get_Length (E_Str => File);

      -- Get the filename and extension.  Filename might have a path
      -- or a drive name as a prefix.
      for I in reverse E_Strings.Positions range E_Strings.Positions'First .. E_Strings.Get_Length (E_Str => File) loop
         if E_Strings.Get_Element (E_Str => File,
                                   Pos   => I) =
           E_Strings.Get_Element
           (E_Str => FileSystem.Directory_Separator,
            Pos   => E_Strings.Get_Length (E_Str => FileSystem.Directory_Separator))
           or else E_Strings.Get_Element (E_Str => File,
                                          Pos   => I) = ':' then
            Separator_Pos := I;
            exit;
         end if;
      end loop;

      -- Separate the file and directory name.
      if Separator_Pos = E_Strings.Get_Length (E_Str => File) then -- no pathname given, just filename.
         Directory_Name := E_Strings.Empty_String;
         Filename       := File;
      else
         Directory_Name := E_Strings.Section (File, E_Strings.Positions'First, Separator_Pos);
         Filename       := E_Strings.Section (File, Separator_Pos + 1, E_Strings.Get_Length (E_Str => File) - Separator_Pos);
      end if;

      -- Interpret the directory name as a URL relative to Relative_To
      HTML_Relative_Dir
        (Dir_In         => Directory_Name,
         Relative_To_In => Relative_To,
         Dir_Relative   => Relative_Dir_Name,
         Success_Out    => Success);

      -- If the relative directory operation was successful then we use the
      -- new directory name.  Otherwise we simply return the filename given.
      if Success then
         Out_String := Relative_Dir_Name;
         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Filename);
      else
         Out_String := File;
      end if;

      File_Relative := Out_String;
      Success_Out   := Success;
   end HTML_Relative_File;

   ---------------------------------------------------------------------------

   -- This procedure is global as it is used by both the report and listing file
   -- translators.
   --
   -- This procedure processes an error message line.  Some errors get wrapped over
   -- more than one line and this procedure should only be used in generating
   -- HTML for the first line of any error message.
   --
   -- It assumes that the line is formatted as follows:
   --
   --   <flash> (nnnn)  <error_type> : <xxx> : <error_message>
   --
   -- The actual pattern matched is slightly different but we only expect lines
   -- of this type (or the special cases described below) and lines of this type
   -- will be parsed correctly.
   --
   -- The <error_type> and the error code <xxx> are used to generate an HTML
   -- link of the form <A HREF="error_file#error_link">, where error_file is
   -- the (relative) location of the error messages file and error_link is the
   -- name of an anchor in the file, typically made from the first word of the
   -- error type (converted to lower case) and the error number, separated by a hyphen.
   -- Note that there are some special cases where the name of the anchor is not
   -- constructed in this way.
   --
   -- The HTML link is placed before the start of the line (the closing tag is
   -- placed at the end of the line).  The link is also returned in the Error_Link
   -- out parameter so that it can be saved for use in any continuations
   -- of this message line.
   --
   -- For example, if the Line parameter is the Examiner_String:
   --
   --    *** (419)   Semantic Error      : 1: Message
   --
   -- The line output will be:
   --
   -- <A HREF="error_file#semantic-1">  *** (419)   Semantic Error      : 1: Message</A>
   --
   -- and the Error_Link parameter will be:
   --
   -- <A HREF="error_file#semantic-1">
   --
   -- If no tag is added then the Error_Link parameter is set to Empty_String.
   --
   -- The special cases (i.e. those which don't fit the above format, mainly
   -- because they don't have error numbers) are handled by the procedure
   -- Handle_Special_Cases as described below.
   procedure Process_First_Error_Message_Line
     (Line       : in out E_Strings.T;
      Lookahead  : in     E_Strings.T;
      Error_Link :    out E_Strings.T)
   --# global in out SPARK_IO.File_Sys;
   --# derives Error_Link,
   --#         Line              from Line,
   --#                                Lookahead &
   --#         SPARK_IO.File_Sys from *,
   --#                                Line,
   --#                                Lookahead;
   is
      Saved_Line   : E_Strings.T;
      Error_Type   : E_Strings.T;
      Error_No     : E_Strings.T;
      Out_String   : E_Strings.T;
      Char         : Character;
      Char2        : Character;
      Char3        : Character;
      Error        : Boolean;
      Special_Case : Boolean;

      -- The function Flash_Character is true if and only if the input is
      -- a character used by the Examiner in a "flash" at the start of
      -- an error message.

      function Flash_Character (Char : Character) return Boolean is
      begin
         return Char = '?' or else Char = '-' or else Char = '+' or else Char = '*' or else Char = '!';
      end Flash_Character;
      pragma Inline (Flash_Character);

      -- This procedure handles the special cases of error messages that are not
      -- of the form described in the commentary for Process_First_Error_Message_Line.
      -- These special cases are mainly errors that don't have error numbers
      -- associated with them or that have no reference in the error reference
      -- file.
      --
      -- The "special case" errors are as follows:
      --
      --   *** Syntax Error : ";" expected.
      --   *** Syntax Error : No APRAGMA can be start with reserved word "IS".
      --   *** Syntax Error : No complete PROCEDURE_SPECIFICATION can be followed by ANNOTATION_START here.
      --   *** Syntax Error : No complete PROCEDURE_SPECIFICATION can be followed by reserved word "IS" here.
      --   *** Syntax Error : reserved word "INHERIT" expected.
      --   Any other syntax errors (these won't have links)
      --   Warning : No semantic checks carried out, text may not be legal SPARK.
      --   Any lexical errors (these won't have links)
      --
      --   The line output is the line input but with an HTML link to the correct
      --   place in the error reference file (or no link if no such reference
      --   exists).
      --
      --   The error_link parameter is set to the HTML tag used to generate the link
      --   or, if not link is generated, the empty string.
      --
      --   The Special_Case parameter is set to true if and only if a special case
      --   was found.  If this parameter is set then the caller should not try
      --   to process the error message line in the usual way.
      --
      --   The procedure uses a fairly naive Sub_String search mechanism that could
      --   potentially incorrectly match a string and flag it as a Special_Case
      --   but this is unlikely because when this procedure is called we know that
      --   we have a line containing an error message and we know what the error
      --   messages are.
      procedure Handle_Special_Cases
        (Line         : in out E_Strings.T;
         Lookahead    : in     E_Strings.T;
         Error_Link   :    out E_Strings.T;
         Special_Case :    out Boolean)
      --# derives Error_Link,
      --#         Line         from Line,
      --#                           Lookahead &
      --#         Special_Case from Line;
      is
         Add_Tag            : Boolean;
         Syntax_Error_Found : Boolean;
         Warning_Found      : Boolean;
         Lexical_Found      : Boolean;
         Error_Link_Name    : E_Strings.T;
         Error_Link_String  : E_Strings.T;
         Out_String         : E_Strings.T;
         String_Found       : Boolean;
         String_Start       : E_Strings.Positions;
      begin
         Add_Tag         := False;
         Special_Case    := False;
         Error_Link      := E_Strings.Empty_String;
         Error_Link_Name := E_Strings.Empty_String;

         -- Check for the various error types that have special cases.

         --# accept F, 10, String_Start, "Ineffective assignment here OK";
         E_Strings.Find_Sub_String
           (E_Str         => Line,
            Search_String => "Syntax Error",
            String_Found  => Syntax_Error_Found,
            String_Start  => String_Start);
         E_Strings.Find_Sub_String
           (E_Str         => Line,
            Search_String => "Warning",
            String_Found  => Warning_Found,
            String_Start  => String_Start);
         E_Strings.Find_Sub_String
           (E_Str         => Line,
            Search_String => "Lexical Error",
            String_Found  => Lexical_Found,
            String_Start  => String_Start);
         --# end accept;
         if Syntax_Error_Found then

            -- HTML directives:
            --! <NameFormat> <"syntax-"><Name>
            --! <ErrorFormat> <"*** Syntax Error : "><Error>

            -- HTML output
            --! <Name> semicolon-expected
            --! <Error> &quot;;&quot; expected.
            --! If this is reported at the end of the input file it may well
            --!  be caused by the misspelling of an identifier in a hide directive.
            --!  The parser then skips all the following text looking for the
            --!  misspelled identifier but finds the end of file first where it
            --!  reports a syntax error.

            --! <Name> no-apragma
            --! <Error> No APRAGMA can be start with reserved word &quot;IS&quot;
            --! This can occur when a stub for an embedded subprogram is wrongly
            --! terminated by a semicolon.

            --! <Name> procedure-spec-annotation-start
            --! <Error> No complete PROCEDURE_SPECIFICATION can be followed by ANNOTATION_START here.
            --! This can occur when the reserved word </i>body<i> has been
            --! omitted from the declaration of a package body. This error
            --! will occur at the annotation placed between the
            --! specification and the reserved word </i>is<i> of the first
            --! subprogram.

            --! <Name> procedure-spec-is
            --! <Error> No complete PROCEDURE_SPECIFICATION can be followed by reserved word &quot;IS&quot; here.
            --! This can occur when the reserved word </i>body<i> has been omitted
            --! from the declaration of a package body. This error will occur at the
            --! reserved word </i>is<i> which introduces the body of the first subprogram.

            --! <Name> inherit-expected
            --! <Error> reserved word &quot;INHERIT&quot; expected.
            --! This occurs where the annotation on a subprogram body is placed after
            --! the reserved word </i>is<i> instead of before it.

            --! <Name> simple-expression-rbracket
            --! <Error> No complete SIMPLE_EXPRESSION can be followed by &quot;)&quot; here.
            --! This can occur in an aggregate expression when there is a mixure of
            --! named and positional association being used.

            --! <Name> simple-expression-comma
            --! <Error> No complete SIMPLE_EXPRESSION can be followed by &quot;,&quot; here.
            --! This can occur in an aggregate expression when there is a mixure of
            --! named and positional association being used.

            -- All syntax errors are special cases.
            Special_Case := True;
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => ";&quot; expected",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               Error_Link_Name := E_Strings.Copy_String (Str => "syntax-semicolon-expected");
               Add_Tag         := True;
            end if;
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => "No APRAGMA",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               Error_Link_Name := E_Strings.Copy_String (Str => "syntax-no-apragma");
               Add_Tag         := True;
            end if;
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => "No complete PROCEDURE_SPECIFICATION",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               --# accept F, 10, String_Start, "Ineffective assignment here OK";
               E_Strings.Find_Sub_String
                 (E_Str         => Lookahead,
                  Search_String => "_START here.",
                  String_Found  => String_Found,
                  String_Start  => String_Start);
               --# end accept;
               if String_Found then

                  Error_Link_Name := E_Strings.Copy_String (Str => "syntax-procedure-spec-annotation-start");
                  Add_Tag         := True;

               end if;
               --# accept F, 10, String_Start, "Ineffective assignment here OK";
               E_Strings.Find_Sub_String
                 (E_Str         => Lookahead,
                  Search_String => "&quot;IS&quot; here.",
                  String_Found  => String_Found,
                  String_Start  => String_Start);
               --# end accept;
               if String_Found then

                  Error_Link_Name := E_Strings.Copy_String (Str => "syntax-procedure-spec-is");
                  Add_Tag         := True;

               end if;

            end if;
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => "No complete SIMPLE_EXPRESSION",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               --# accept F, 10, String_Start, "Ineffective assignment here OK";
               E_Strings.Find_Sub_String
                 (E_Str         => Lookahead,
                  Search_String => "&quot;)&quot; here.",
                  String_Found  => String_Found,
                  String_Start  => String_Start);
               --# end accept;
               if String_Found then

                  Error_Link_Name := E_Strings.Copy_String (Str => "syntax-simple-expression-rbracket");
                  Add_Tag         := True;

               end if;
               --# accept F, 10, String_Start, "Ineffective assignment here OK";
               E_Strings.Find_Sub_String
                 (E_Str         => Lookahead,
                  Search_String => "&quot;,&quot; here.",
                  String_Found  => String_Found,
                  String_Start  => String_Start);
               --# end accept;
               if String_Found then

                  Error_Link_Name := E_Strings.Copy_String (Str => "syntax-simple-expression-comma");
                  Add_Tag         := True;

               end if;

            end if;
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => "reserved word &quot;INHERIT&quot; expected",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               Error_Link_Name := E_Strings.Copy_String (Str => "syntax-inherit-expected");
               Add_Tag         := True;
            end if;

         elsif Warning_Found then

            -- Not all warnings are special cases - only set Special_Case to True if
            -- a special case is detected.
            --# accept F, 10, String_Start, "Ineffective assignment here OK";
            E_Strings.Find_Sub_String
              (E_Str         => Line,
               Search_String => "No semantic checks carried out,",
               String_Found  => String_Found,
               String_Start  => String_Start);
            --# end accept;
            if String_Found then
               Error_Link_Name := E_Strings.Copy_String (Str => "warning-no-semantic-checks");
               Add_Tag         := True;
               Special_Case    := True;
            end if;

         elsif Lexical_Found then

            -- All lexical errors are special cases.
            Special_Case := True;

            -- Lexical errors are not included in the file of error explanations
            -- so no processing is done.

         end if;

         if Add_Tag then
            Error_Link_String := E_Strings.Copy_String (Str => "<A TARGET=""rbottom"" HREF=""");
            E_Strings.Append_String (E_Str => Error_Link_String,
                                     Str   => Error_Reference_Filename);
            E_Strings.Append_String (E_Str => Error_Link_String,
                                     Str   => "#");
            E_Strings.Append_Examiner_String (E_Str1 => Error_Link_String,
                                              E_Str2 => Error_Link_Name);
            E_Strings.Append_String (E_Str => Error_Link_String,
                                     Str   => """>");
            Error_Link := Error_Link_String;

            Out_String := Error_Link_String;
            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => Line);
            E_Strings.Append_String (E_Str => Out_String,
                                     Str   => "</A>");
            Line := Out_String;
         end if;
         --# accept F, 33, String_Start, "Expect String_Start unused";
      end Handle_Special_Cases;

   begin  -- Process_First_Error_Message_Line

      Handle_Special_Cases (Line         => Line,
                            Lookahead    => Lookahead,
                            Error_Link   => Error_Link,
                            Special_Case => Special_Case);

      if not Special_Case then

         Error := False;

         Error_Type := E_Strings.Empty_String;
         Error_No   := E_Strings.Empty_String;

         Saved_Line := Line;

         -- Get characters until flash is found (either ???, ---, +++, ***, !!!)
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Flash_Character (Char => Char) or else E_Strings.Is_Empty (E_Str => Line);
         end loop;

         -- Look for two more flash characters
         E_Strings.Pop_Char (E_Str => Line,
                             Char  => Char2);
         E_Strings.Pop_Char (E_Str => Line,
                             Char  => Char3);

         if Char2 /= Char or Char3 /= Char then
            Error := True;
         end if;

         -- Look for a space and a '('
         E_Strings.Pop_Char (E_Str => Line,
                             Char  => Char2);
         E_Strings.Pop_Char (E_Str => Line,
                             Char  => Char3);

         if Char2 /= ' ' or Char3 /= '(' then
            Error := True;
         end if;

         -- Skip line number (up to next ')')
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char = ')' or else E_Strings.Is_Empty (E_Str => Line);
         end loop;

         -- Skip whitespace
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char /= ' ' or else E_Strings.Is_Empty (E_Str => Line);
         end loop;

         -- Char is first character of Error_Type
         E_Strings.Append_Char (E_Str => Error_Type,
                                Ch    => Char);

         -- Get rest of Error_Type (up to next ' ')
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char = ' ' or else E_Strings.Is_Empty (E_Str => Line);
            E_Strings.Append_Char (E_Str => Error_Type,
                                   Ch    => Char);
         end loop;

         -- Skip up to colon
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char = ':' or else E_Strings.Is_Empty (E_Str => Line);
         end loop;

         -- Skip whitespace
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char /= ' ' or else E_Strings.Is_Empty (E_Str => Line);
         end loop;

         -- Char is first character of Error_No
         E_Strings.Append_Char (E_Str => Error_No,
                                Ch    => Char);

         -- Get rest of Error_No (up to next ':')
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char = ':' or else E_Strings.Is_Empty (E_Str => Line);
            E_Strings.Append_Char (E_Str => Error_No,
                                   Ch    => Char);
         end loop;

         -- If an error occurred report this and show which line it occurred on.
         if Error or else E_Strings.Is_Empty (E_Str => Line) then
            ScreenEcho.Put_Line ("An error occurred while parsing the following error message line.");
            E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                E_Str => Saved_Line);
         end if;

         Error_Type := E_Strings.Lower_Case (E_Str => Error_Type);

         -- Generate the output string.
         Out_String := E_Strings.Copy_String (Str => "<A HREF=""");
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => Error_Reference_Filename);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => "#");
         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Error_Type);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => "-");
         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Error_No);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => """ TARGET=""rbottom"">");

         Error_Link := Out_String;

         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Saved_Line);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => "</A>");

         Line := Out_String;

      end if;

      Line := HTML_Embolden (Line => Line);
   end Process_First_Error_Message_Line;

   -- This procedure processes subsequent error message lines.  These contain
   -- word-wrapped portions of the error message, but no flash, error number
   -- and so on.  We therefore need to use the tag stored in Saved_Error_Link
   -- as the link for this line.
   --
   -- The line is assumed to have some spaces at the front of the it.  Placing the
   -- tag before these turns the spaces into links which looks really silly, so
   -- the tag is placed immediately before the first non-space character.
   --
   -- If Saved_Error_Link is Empty_String then no tag is added.

   procedure Process_Next_Error_Message_Line (Line : in out E_Strings.T;
                                              Link : in     E_Strings.T)
   --# derives Line from *,
   --#                   Link;
   is
      Out_String : E_Strings.T;
      Char       : Character;
   begin
      if not E_Strings.Is_Empty (E_Str => Line) then
         Out_String := E_Strings.Empty_String;

         -- Copy up to first non-space character
         loop
            E_Strings.Pop_Char (E_Str => Line,
                                Char  => Char);
            exit when Char /= ' ';
            E_Strings.Append_Char (E_Str => Out_String,
                                   Ch    => Char);
         end loop;

         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Link);

         E_Strings.Append_Char (E_Str => Out_String,
                                Ch    => Char);
         E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                           E_Str2 => Line);
         E_Strings.Append_String (E_Str => Out_String,
                                  Str   => "</A>");

         Line := Out_String;

      end if;

      Line := HTML_Embolden (Line => Line);
   end Process_Next_Error_Message_Line;

   --
   --  -------------
   --  Init_SPARK_HTML
   --  -------------
   --
   --  This procedure initialises the HTML output of the examiner.
   --
   --  It is assumed that the HTML output flag has already been tested.  The call to
   --  Init_SPARK_HTML should look something like:
   --
   --    if CommandLineData.Content.HTML then
   --       SparkHTML.Init_SPARK_HTML;
   --    end if;
   --
   --  The procedure does exactly the following:
   --
   --     - creates a subdirectory of the current directory if one
   --       doesn't already exist.  The directory name is given by
   --       CommandLineData.Content.HTML_Directory
   --     - prints a message to the screen informing the user that HTML output
   --       has been requested;
   --     - creates a top-level frames file in the HTML directory as described in
   --       the design;
   --     - creates an empty HTML file to fill the bottom frame (this is so that
   --       Netscape Navigator behaves correctly);
   --     - sets Generate_HTML to false if anything went wrong, to prevent further
   --       HTML generation.
   --
   --  Error trapping:
   --
   --  If anything unusual happens then the procedure will echo a message to the
   --  screen saying what went wrong and, in the case of errors which should stop
   --  HTML generation, Generate_HTML will be set to "false".
   --
   --  Failed HTML initialisation should not affect the analysis.  If intialisation
   --  fails, the Generate_HTML flag will stop calls to other SparkHTML routines
   --  from doing anything.  It is therefore safe to make those calls.

   procedure Init_SPARK_HTML is
      New_Dir_OK    : Boolean;
      Frame_File_OK : Boolean;
      Dir_Name      : E_Strings.T;

      -- This procedure writes HTML content to the base frame file.
      -- The procedure does not open or close the frame file, it assumes
      -- that a file has been opened.
      -- The Frame_File parameter must be the handle of an open file.
      -- The Report_Filename parameter is the location of the file to
      -- go in the top frame, relative to the frame file.
      -- The Blankfile_Name parameter is the location of the file to go
      -- in the top frame, relative to the frame file.
      --
      -- Error trapping:
      --
      -- All error trapping is performed within SPARK_IO.  The SPARK_IO
      -- procedures called do not return status information and so error
      -- trapping is not possible here.

      procedure Write_Frame_File
        (Frame_File      : in SPARK_IO.File_Type;
         Report_Filename : in E_Strings.T;
         Blank_Filename  : in E_Strings.T)
      --# global in     CommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                Blank_Filename,
      --#                                CommandLineData.Content,
      --#                                Frame_File,
      --#                                Report_Filename;
      is
         Temp_Examiner_String : E_Strings.T;
      begin
         SPARK_IO.Put_Line (Frame_File, "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Frameset//EN""", 57);
         SPARK_IO.Put_Line (Frame_File, "            ""http://www.w3.org/TR/REC-html40/frameset.dtd"">", 59);
         SPARK_IO.Put_Line (Frame_File, "<html>", 6);
         SPARK_IO.Put_Line (Frame_File, "  <head>", 8);
         SPARK_IO.Put_Line (Frame_File, "    <title>SPARK HTML Output</title>", 36);
         SPARK_IO.Put_Line (Frame_File, "  </head>", 9);
         SPARK_IO.Put_Line (Frame_File, "  <frameset rows=""60%,*"">", 25);

         Temp_Examiner_String := E_Strings.Copy_String (Str => "    <frame name=""rtop"" scrolling=""auto"" src=""");
         if CommandLineData.Content.Plain_Output then
            E_Strings.Append_Examiner_String
              (E_Str1 => Temp_Examiner_String,
               E_Str2 => FileSystem.Just_File (Fn  => Report_Filename,
                                               Ext => True));
         else
            E_Strings.Append_Examiner_String (E_Str1 => Temp_Examiner_String,
                                              E_Str2 => Report_Filename);
         end if;
         E_Strings.Append_String (E_Str => Temp_Examiner_String,
                                  Str   => """>");
         E_Strings.Put_Line (File  => Frame_File,
                             E_Str => Temp_Examiner_String);

         Temp_Examiner_String := E_Strings.Copy_String (Str => "    <frame name=""rbottom"" scrolling=""auto"" src=""");
         E_Strings.Append_Examiner_String (E_Str1 => Temp_Examiner_String,
                                           E_Str2 => Blank_Filename);
         E_Strings.Append_String (E_Str => Temp_Examiner_String,
                                  Str   => """>");

         E_Strings.Put_Line (File  => Frame_File,
                             E_Str => Temp_Examiner_String);

         SPARK_IO.Put_Line (Frame_File, "  </frameset>", 13);
         SPARK_IO.Put_Line (Frame_File, "</html>", 7);
      end Write_Frame_File;

      -- This procedure does the following:
      --
      --    - creates a file called "spark.htm" in the HTML subdirectory
      --      of the current directory (the HTML directory is assumed to
      --      already exist),
      --    - derives the report file name from that held in
      --      CommandLineData.Content for inclusion in the frame file,
      --    - creates a blank file to fill the bottom frame,
      --    - calls Write_Frame_File to write the content of the base frame
      --      file,
      --    - closes the frame file and blank file,
      --    - returns a status value of True if no errors occurred,
      --    - returns a status value of False and displays a message to
      --      the user if an error occurred.
      --
      -- Error Trapping:
      --
      -- Where calls to procedures in SPARK_IO return status parameters the
      -- parameter is checked.  If the status is not OK then a message is
      -- echoed to the screen to inform the user.  The message does not
      -- display the type of error (but could in future) but explains what
      -- was happening when the error occurred.
      --
      -- This procedure returns a status value itself.  If the status value
      -- is False then it is suggested that no further HTML generation should
      -- take place.  The caller should test the returned status and set the
      -- Generate_HTML flag appropriately.

      procedure Create_Frame_File (Success : out Boolean)
      --# global in     CommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys,
      --#         Success           from CommandLineData.Content,
      --#                                SPARK_IO.File_Sys;
      is
         Frame_File            : SPARK_IO.File_Type;
         Blank_File            : SPARK_IO.File_Type;
         Frame_File_Created_OK : SPARK_IO.File_Status;
         Frame_File_Closed_OK  : SPARK_IO.File_Status;
         Blank_File_Created_OK : SPARK_IO.File_Status;
         Blank_File_Closed_OK  : SPARK_IO.File_Status;

         HTML_Report_Filename : E_Strings.T;

         Frame_Filename      : E_Strings.T;
         Blank_Filename      : E_Strings.T;
         Full_Frame_Filename : E_Strings.T;
         Full_Blank_Filename : E_Strings.T;

      begin
         -- Initialise variables.
         Success    := True;  -- Set to false when fatal error occurs.
         Frame_File := SPARK_IO.Null_File;
         Blank_File := SPARK_IO.Null_File;

         Frame_Filename := E_Strings.Copy_String (Str => "spark.htm");
         Blank_Filename := E_Strings.Copy_String (Str => "blank.htm");

         -- These files both reside in the HTML directory so their
         -- names need to have the HTML directory name prepended.
         Full_Frame_Filename := FileSystem.Case_Of_Files_For_Create (E_Str => HTML_Filename (Filename => Frame_Filename));
         Full_Blank_Filename := FileSystem.Case_Of_Files_For_Create (E_Str => HTML_Filename (Filename => Blank_Filename));

         -- Get the name of the HTML report file for inclusion in the HTML.
         HTML_Report_Filename :=
           E_Strings.Translate
           (E_Str     => FileSystem.Just_File (CommandLineData.Content.Report_File_Name, True),
            From_Char => '.',
            To_Char   => '_');
         E_Strings.Append_String (E_Str => HTML_Report_Filename,
                                  Str   => ".htm");

         -- Create the frame file.
         E_Strings.Create
           (File         => Frame_File,
            Name_Of_File => Full_Frame_Filename,
            Form_Of_File => "",
            Status       => Frame_File_Created_OK);

         if Frame_File_Created_OK /= SPARK_IO.Ok then
            ScreenEcho.Put_Line ("An error occurred while attemping to create the HTML frame file.");
            Success := False;
         else
            E_Strings.Create
              (File         => Blank_File,
               Name_Of_File => Full_Blank_Filename,
               Form_Of_File => "",
               Status       => Blank_File_Created_OK);

            if Blank_File_Created_OK /= SPARK_IO.Ok then

               ScreenEcho.Put_Line ("An error occurred while attempting to create the blank HTML file.");
               -- There is not an else here as not being able to write the blank file is
               -- not fatal.  Success remains true and we continue as we still have to
               -- close the Frame_File.

            end if;

            Write_Frame_File (Frame_File      => Frame_File,
                              Report_Filename => HTML_Report_Filename,
                              Blank_Filename  => Blank_Filename);

            --# accept Flow, 10, Frame_File, "Expected ineffective assignment to Frame_File";
            SPARK_IO.Close (File   => Frame_File,
                            Status => Frame_File_Closed_OK);
            --# end accept;

            if Frame_File_Closed_OK /= SPARK_IO.Ok then
               ScreenEcho.Put_Line ("An error occurred while attempting to close the HTML frame file.");
               Success := False;
            end if;
            -- There is not an else here as we need to try and close the other file.

            --# accept Flow, 10, Blank_File, "Expected ineffective assignment to Blank_File";
            SPARK_IO.Close (File   => Blank_File,
                            Status => Blank_File_Closed_OK);
            --# end accept;

            if Blank_File_Closed_OK /= SPARK_IO.Ok then
               ScreenEcho.Put_Line ("An error occurred while attempting to close the blank HTML file.");
               -- Not closing the blank file is non-fatal, Success remains true.
            end if;

         end if;
      end Create_Frame_File;

      -- Returns the name of the HTML subdirectory required for passing
      -- to FileSystem.IdempotentCreate_Subdir.

      function HTML_Sub_Dir_Name return  E_Strings.T
      --# global in CommandLineData.Content;
      is
         Dir_Name : E_Strings.T;
      begin
         Dir_Name := FileSystem.Start_Of_Directory;
         if CommandLineData.Content.Output_Directory then
            E_Strings.Append_Examiner_String (E_Str1 => Dir_Name,
                                              E_Str2 => CommandLineData.Content.Output_Directory_Name);
            E_Strings.Append_Examiner_String (E_Str1 => Dir_Name,
                                              E_Str2 => FileSystem.Directory_Separator);
         end if;
         E_Strings.Append_Examiner_String (E_Str1 => Dir_Name,
                                           E_Str2 => CommandLineData.Content.HTML_Directory);

         -- IdempotentCreate_Subdir expects:
         --   on Unix/NT:  just the name of the directory

         return Dir_Name;
      end HTML_Sub_Dir_Name;

      procedure Copy_Errors_File
      --# global in     CommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                CommandLineData.Content;
      is
         Source_Filename       : E_Strings.T;
         Source_Full_Filename  : E_Strings.T;
         Source_File_Status    : FileSystem.Typ_File_Spec_Status;
         Source_File           : SPARK_IO.File_Type;
         Source_File_Open_OK   : SPARK_IO.File_Status;
         Source_File_Closed_OK : SPARK_IO.File_Status;

         Dest_Filename        : E_Strings.T;
         Dest_File            : SPARK_IO.File_Type;
         Dest_File_Created_OK : SPARK_IO.File_Status;
         Dest_File_Closed_OK  : SPARK_IO.File_Status;

         Copy_Buffer : E_Strings.T;
      begin
         Source_File := SPARK_IO.Null_File;
         Dest_File   := SPARK_IO.Null_File;

         -- Start by trying to open output file.
         Dest_Filename :=
           FileSystem.Case_Of_Files_For_Create
           (E_Str => HTML_Filename (Filename => E_Strings.Copy_String (Str => "errors.htm")));

         E_Strings.Create (File         => Dest_File,
                           Name_Of_File => Dest_Filename,
                           Form_Of_File => "",
                           Status       => Dest_File_Created_OK);

         if Dest_File_Created_OK /= SPARK_IO.Ok then
            ScreenEcho.Put_Line ("An error occurred while trying to create the HTML errors file.");
         else
            -- Now locate the input file.

            Source_Filename := FileSystem.Examiner_Lib_Directory;

            E_Strings.Append_Examiner_String (E_Str1 => Source_Filename,
                                              E_Str2 => FileSystem.Directory_Separator);

            E_Strings.Append_String (E_Str => Source_Filename,
                                     Str   => "errors.htm");
            FileSystem.Find_Full_File_Name
              (File_Spec      => Source_Filename,
               File_Status    => Source_File_Status,
               Full_File_Name => Source_Full_Filename);
            if Source_File_Status /= FileSystem.File_Found then
               -- Output simple message to destination file.
               SPARK_IO.Put_Line (Dest_File, "Sorry, could not locate errors.htm.", 35);
               --# accept Flow, 10, Dest_File_Closed_OK, "Expected ineffective assignment to Dest_File_Closed_OK";
               SPARK_IO.Close (Dest_File, Dest_File_Closed_OK);      -- Unused variable Dest_File_Closed_OK
                                                                     --Ignore error on close.
                                                                     --# end accept;
            else

               -- Open the file and copy it.
               E_Strings.Open
                 (File         => Source_File,
                  Mode_Of_File => SPARK_IO.In_File,
                  Name_Of_File => Source_Full_Filename,
                  Form_Of_File => "",
                  Status       => Source_File_Open_OK);

               if Source_File_Open_OK /= SPARK_IO.Ok then
                  ScreenEcho.Put_Line ("An error occurred while opening the HTML errors file");
               else
                  while not SPARK_IO.End_Of_File (Source_File) loop
                     E_Strings.Get_Line (File  => Source_File,
                                         E_Str => Copy_Buffer);
                     E_Strings.Put_Line (File  => Dest_File,
                                         E_Str => Copy_Buffer);
                  end loop;
               end if;

               --# accept Flow, 10, Source_File, "Expected ineffective assignment to Source_File" &
               --#        Flow, 10, Source_File_Closed_OK, "Expected ineffective assignment to Source_File_Closed_OK";
               SPARK_IO.Close (Source_File, Source_File_Closed_OK);
               --# end accept;

            end if;
         end if;
         --# accept Flow, 10, Dest_File, "Expected ineffective assignment to Dest_File" &
         --#        Flow, 10, Dest_File_Closed_OK, "Expected ineffective assignment to Dest_File_Closed_OK";
         SPARK_IO.Close (Dest_File, Dest_File_Closed_OK);
         --# end accept;

         --# accept Flow, 33, Source_File_Closed_OK, "Expected Source_File_Closed_OK to be neither referenced nor exported" &
         --#        Flow, 33, Dest_File_Closed_OK, "Expected Dest_File_Closed_OK to be neither referenced nor exported";
      end Copy_Errors_File;  -- 2 Flow errors OK

   begin -- Init_SPARK_HTML

      -- Initialise working directories - ensure trailing slashes for use in
      -- relative filename procedures.
      SPARK_Work_Dir := FileSystem.Working_Directory;

      -- Ensure trailing separator.
      if E_Strings.Get_Element (E_Str => SPARK_Work_Dir,
                                Pos   => E_Strings.Get_Length (E_Str => SPARK_Work_Dir)) /=
        E_Strings.Get_Element
        (E_Str => FileSystem.Directory_Separator,
         Pos   => E_Strings.Get_Length (E_Str => FileSystem.Directory_Separator)) then
         E_Strings.Append_Examiner_String (E_Str1 => SPARK_Work_Dir,
                                           E_Str2 => FileSystem.Directory_Separator);
      end if;

      if CommandLineData.Content.Output_Directory then
         HTML_Work_Dir :=
           Append_Directory_String (Path => FileSystem.Working_Directory,
                                    Dir  => CommandLineData.Content.Output_Directory_Name);
      else
         HTML_Work_Dir := FileSystem.Working_Directory;
      end if;
      HTML_Work_Dir := Append_Directory_String (Path => HTML_Work_Dir,
                                                Dir  => CommandLineData.Content.HTML_Directory);

      -- Get name for creating subdirectory.
      Dir_Name := HTML_Sub_Dir_Name;

      -- Create subdirectory.
      FileSystem.Idempotent_Create_Subdirectory (Path => Dir_Name,
                                                 Ok   => New_Dir_OK);
      Copy_Errors_File;
      Create_Frame_File (Success => Frame_File_OK);
      if not (New_Dir_OK and Frame_File_OK) then
         ScreenEcho.Put_Line ("Error ocurred while initialising HTML generation.");
         ScreenEcho.Put_Line ("No further HTML generation will occur.");
         Generate_HTML := False;
         -- Suppress further HTML generation.
      end if;
      --  flow errors expected due to false coupling through SPARK_IO
      --  and File_Status not being used.
   end Init_SPARK_HTML;

   ---------------------------------------------------------------------------

   --
   --  -------------
   --  Gen_Report_HTML
   --  -------------
   --
   --  This procedure generates the HTMLified report file from the plain text
   --  report file.
   --
   --  It is assumed that the HTML output flag has already been tested.  The call to
   --  Gen_Report_HTML should look something like:
   --
   --    if CommandLineData.Content.HTML then
   --       SparkHTML.Gen_Report_HTML;
   --    end if;
   --
   --  The checking of the Generate_HTML flag is done internally.
   --
   --  If the Generate_HTML flag is false this procedure does nothing, otherwise,
   --  it does exactly the following:
   --
   --     - creates an HTML file (named <rep_file>.htm where <rep_file> is the name
   --       of the report file with all '.' characters changed to '_' characters;
   --     - processes each line of the plain text report file using the
   --       Process_Report_Line procedure (the functionality of this is described
   --       at the declaration of Process_Report_Line);
   --     - writes the processed lines to the HTML report file;
   --     - sets Generate_HTML to false if anything goes wrong, to prevent further
   --       HTML generation.
   --
   --  Error trapping:
   --
   --  The HTML report file is essentially a guide to the HTMLified listings and
   --  so if something causes the HTML report file generation to fail we should
   --  suppress all following HTML generation by settign Generate_HTML to "false".
   --
   --  If anything unusual happens then the procedure will echo a message to the
   --  screen saying what went wrong and, in the case of errors which should stop
   --  HTML generation, Generate_HTML will be set to "false".
   --
   procedure Gen_Report_HTML is
      HTML_Report_File            : SPARK_IO.File_Type;
      HTML_Report_Filename        : E_Strings.T;
      HTML_Report_File_Created_OK : SPARK_IO.File_Status;
      HTML_Report_File_Closed_OK  : SPARK_IO.File_Status;

      Plain_Report_File           : SPARK_IO.File_Type;
      Plain_Report_Filename       : E_Strings.T;
      Plain_Report_File_Open_OK   : SPARK_IO.File_Status;
      Plain_Report_File_Closed_OK : SPARK_IO.File_Status;

      Line_Buffer        : E_Strings.T;
      Lookahead_Buffer   : E_Strings.T;
      Saved_Error_Link   : E_Strings.T;
      Saved_Listing_File : E_Strings.T;

      Report_File_State : Report_File_States := Report_Just_Started;

      -- Sub Programs
      -- ------------

      -- This sub-procedure writes HTML content to the start of the report file.
      -- It assumes that the file handle HTML_Report_File is the open HTML
      -- report file.  This procedure will not open or close the file.
      --
      -- The HTML written specifies the title of the page and some formatting
      -- tags.  The formatting is <PRE> (pre-processed text) which displays
      -- text exactly as given, and <TT> which sets the typewriter-text font;
      -- I use this because it usually results in a fixed-width font being
      -- used.
      --
      -- When writing the end of the report file these tags need to be closed,
      -- as do the <BODY> and <HTML> tags.  This should be done by calling
      -- the Write_HTML_Report_Footer procedure.
      --
      -- Error trapping:
      --
      -- All error trapping is performed within SPARK_IO.
      -- The SPARK_IO routines called do not return status parameters and so
      -- no error trapping can be done here.

      procedure Write_HTML_Report_Header
      --# global in     HTML_Report_File;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                HTML_Report_File;
      is
      begin
         SPARK_IO.Put_Line (HTML_Report_File, "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN""", 61);
         SPARK_IO.Put_Line (HTML_Report_File, "            ""http://www.w3.org/TR/REC-html40/loose.dtd"">", 56);
         SPARK_IO.Put_Line (HTML_Report_File, "<html>", 6);
         SPARK_IO.Put_Line (HTML_Report_File, "  <head>", 8);
         SPARK_IO.Put_Line (HTML_Report_File, "    <title>Examiner HTML Report File</title>", 44);
         SPARK_IO.Put_Line (HTML_Report_File, "  </head>", 9);
         SPARK_IO.Put_Line (HTML_Report_File, "  <body>", 8);
         SPARK_IO.Put_Line (HTML_Report_File, "    <pre>", 9);
         SPARK_IO.Put_Line (HTML_Report_File, "      <tt>", 10);
      end Write_HTML_Report_Header;

      -- This subprocedure writes HTML content to the end of the report file.
      -- It assumes that the file handle HTML_Report_File is the open HTML
      -- report file.  This procedure will not open or close the file.
      --
      -- The HTML written closes all the formatting tags that were opened
      -- by the call to Write_HTML_Report_Header.
      --
      -- Error trapping:
      --
      -- All error trapping is performed within SPARK_IO.
      -- The SPARK_IO routines called do not return status parameters and so
      -- no error trapping can be done here.

      procedure Write_HTML_Report_Footer
      --# global in     HTML_Report_File;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                HTML_Report_File;
      is
      begin
         SPARK_IO.Put_Line (HTML_Report_File, "      </tt>", 11);
         SPARK_IO.Put_Line (HTML_Report_File, "    </pre>", 10);
         SPARK_IO.Put_Line (HTML_Report_File, "  </body>", 9);
         SPARK_IO.Put_Line (HTML_Report_File, "</html>", 7);
      end Write_HTML_Report_Footer;

      -- This procedure is used to convert a line of the report file into HTML.
      -- It is effectively a parser for the report file.
      --
      -- The procedure design is based on a state machine.  The global variable
      -- Report_File_State records our current location within the report file (in
      -- terms of what information has already been read).
      --
      -- Given a line, we can determine the line's meaning from our current state
      -- and the contents of the line (all blank lines are ignored).  For example,
      -- if we are reading the warning list and we discover a line starting with
      -- the string "Source Filename(s) used were:" then we know that we are now
      -- processing the source file list.
      --
      -- We can use this method to parse almost all the information in the source
      -- file.  Those bits that we don't parse are usually optional (such as the
      -- flag "rtc" in the options list) and require no translation to HTML anyway.
      --
      -- Once the procedure understands what a line represents it updates the
      -- Report_File_State and processes the line by calling an appropriate
      -- subroutine.
      --
      -- The procedure contains a debugging feature which reports each line that it
      -- finds to the screen along with a message for each line that it recognises.
      -- This only happens if the -debug switch is given on the commandline.

      procedure Process_Report_Line (Line      : in out E_Strings.T;
                                     Lookahead : in     E_Strings.T)
      --# global in     CommandLineData.Content;
      --#        in     HTML_Work_Dir;
      --#        in     SPARK_Work_Dir;
      --#        in out Report_File_State;
      --#        in out Saved_Error_Link;
      --#        in out Saved_Listing_File;
      --#        in out SPARK_IO.File_Sys;
      --# derives Line               from *,
      --#                                 CommandLineData.Content,
      --#                                 HTML_Work_Dir,
      --#                                 Lookahead,
      --#                                 Report_File_State,
      --#                                 Saved_Error_Link,
      --#                                 Saved_Listing_File,
      --#                                 SPARK_Work_Dir &
      --#         Report_File_State  from *,
      --#                                 Line &
      --#         Saved_Error_Link   from *,
      --#                                 Line,
      --#                                 Lookahead,
      --#                                 Report_File_State &
      --#         Saved_Listing_File from *,
      --#                                 CommandLineData.Content,
      --#                                 Line,
      --#                                 Report_File_State &
      --#         SPARK_IO.File_Sys  from *,
      --#                                 CommandLineData.Content,
      --#                                 Line,
      --#                                 Lookahead,
      --#                                 Report_File_State;
      is
         Start_Pos      : Integer;
         Compare_String : E_Strings.T;
         Debug_Message  : E_Strings.T;

         -- This function takes a filename and some text as input and returns the text
         -- enclosed in HTML tags that form a link to the file relative to the
         -- specified "Relative_To" directory.
         --
         -- A filename beginning with an @ symbol is interpreted as a metafile and
         -- the @ is removed from the location placed in the tag.
         --
         -- Error checking is assumed to occur within the string handling routines.
         -- If the HTML_Relative_File procedure can not generate the URI then
         -- the link is not added and the file name returned, followed by a
         -- message saying "[Link unavailable]".

         function Create_File_Link_Tag_With_Text
           (Filename_In : E_Strings.T;
            Relative_To : E_Strings.T;
            Text        : E_Strings.T)
           return        E_Strings.T
         --# global in SPARK_Work_Dir;
         is
            Out_String : E_Strings.T;
            Filename   : E_Strings.T;
            Location   : E_Strings.T;
            Success    : Boolean;
         begin
            Filename := Filename_In;

            -- The Out_String is the complete string that will be output including
            -- the tags and the original filename.
            Out_String := E_Strings.Empty_String;

            -- Chop off the leading @ (if there is one).
            if E_Strings.Get_Element (E_Str => Filename,
                                      Pos   => E_Strings.Positions'First) = '@' then
               Filename :=
                 E_Strings.Section (Filename, E_Strings.Positions'First + 1, E_Strings.Get_Length (E_Str => Filename) - 1);
            end if;

            -- Find the relative URI.
            HTML_Relative_File (File          => Filename,
                                Relative_To   => Relative_To,
                                File_Relative => Location,
                                Success_Out   => Success);

            if not Success then
               Out_String := Filename_In;
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => " [Link unavailable] ");
            else
               -- Create the tag.
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "<A HREF=""");

               E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                                 E_Str2 => Location);
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => """  TYPE=""x-text/spark"" TARGET=""rbottom"" >");
               E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                                 E_Str2 => Text);
               E_Strings.Append_String (E_Str => Out_String,
                                        Str   => "</A>");
            end if;

            return Out_String;
         end Create_File_Link_Tag_With_Text;

         -- This function does the same as Create_File_Link_Tag_With_Text but the text
         -- used is the filename given.

         function Create_File_Link_Tag (Filename_In : E_Strings.T;
                                        Relative_To : E_Strings.T) return E_Strings.T
         --# global in SPARK_Work_Dir;
         is
         begin
            return Create_File_Link_Tag_With_Text (Filename_In => Filename_In,
                                                   Relative_To => Relative_To,
                                                   Text        => Filename_In);
         end Create_File_Link_Tag;

         -- This file turns a line from the options section of the report file
         -- that specifies a file location and adds HTML tags to create a link
         -- to the file.
         --
         -- An "Option Line" is defined to be a line from the options section
         -- of the report file.  This line is of the form:
         --
         --    <option_name> = <file_location>
         --
         -- The procedure simply copies the line up to the '=' character
         -- and assumes that whatever follows the '=' is the filename.

         function Create_Option_File_Link (Line : E_Strings.T) return E_Strings.T
         --# global in HTML_Work_Dir;
         --#        in SPARK_Work_Dir;
         is
            Temp_Line : E_Strings.T;
            I         : E_Strings.Positions;
            Filename  : E_Strings.T;
            File_Link : E_Strings.T;
         begin
            Temp_Line := E_Strings.Empty_String;
            Filename  := E_Strings.Empty_String;
            I         := E_Strings.Positions'First;

            -- Copy up to the '='
            loop
               E_Strings.Append_Char (E_Str => Temp_Line,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) = '=';
               I := I + 1;
            end loop;

            -- Point to the first character of the filename.
            I := I + 1;

            -- Get the name of the index file by copying up to the end of line
            while I <= E_Strings.Get_Length (E_Str => Line) loop
               E_Strings.Append_Char (E_Str => Filename,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Add HTML tag
            File_Link := Create_File_Link_Tag (Filename_In => Filename,
                                               Relative_To => HTML_Work_Dir);

            E_Strings.Append_Examiner_String (E_Str1 => Temp_Line,
                                              E_Str2 => File_Link);

            return Temp_Line;
         end Create_Option_File_Link;

         -- This function takes a line containing a number of spaces followed by
         -- a file name and puts HTML link tags in to form a link to the file.
         -- If the file starts with an @ symbol it is interpreted as a metafile
         -- and the @ is removed.
         --
         -- This function is used to generate HTML for lines which just contain
         -- whitespace and filenames, such as the selected files list, index files
         -- list, and metafiles list.

         function Create_File_Link (Line : E_Strings.T) return E_Strings.T
         --# global in HTML_Work_Dir;
         --#        in SPARK_Work_Dir;
         is
            Temp_Line : E_Strings.T;
            I         : E_Strings.Positions;
            Filename  : E_Strings.T;
            File_Link : E_Strings.T;
         begin
            Temp_Line := E_Strings.Empty_String;
            Filename  := E_Strings.Empty_String;
            I         := E_Strings.Positions'First;

            -- Copy whitespace up to the first character.
            loop
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) /= ' ';
               E_Strings.Append_Char (E_Str => Temp_Line,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Get the name of the file by copying up to the end of line
            while I <= E_Strings.Get_Length (E_Str => Line) loop
               E_Strings.Append_Char (E_Str => Filename,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Add HTML tag
            File_Link := Create_File_Link_Tag (Filename_In => Filename,
                                               Relative_To => HTML_Work_Dir);

            E_Strings.Append_Examiner_String (E_Str1 => Temp_Line,
                                              E_Str2 => File_Link);

            return Temp_Line;
         end Create_File_Link;

         -- This function is used to process a line from the source file list.
         -- The line is assumed to be some space characters followed by a
         -- file name.  The function creates the file link as described in the
         -- commentary for Create_File_Link, and appends to it a link to the
         -- analysis section for that file.
         --
         -- The link created is a link of the form:
         --
         --    <A HREF="#html_name"> ...  </A>
         --
         -- where html_name is the result of applying the HTML_Name function to
         -- the filename.
         --
         -- The HTML_Name function must also be used to create the anchor that this
         -- link refers to.

         function Process_Source_List_Line (Line : E_Strings.T) return E_Strings.T
         --# global in HTML_Work_Dir;
         --#        in SPARK_Work_Dir;
         is
            Out_String : E_Strings.T;
            Filename   : E_Strings.T;
            I          : E_Strings.Positions;
         begin
            I        := First_Char (The_String => Line);
            Filename := E_Strings.Section (Line, I, (E_Strings.Get_Length (E_Str => Line) - I) + 1);

            Out_String := Create_File_Link (Line => Line);

            E_Strings.Append_String (E_Str => Out_String,
                                     Str   => " [<A HREF=""#");
            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => HTML_Name (E_Str => Filename));
            E_Strings.Append_String (E_Str => Out_String,
                                     Str   => """>View analysis</A>]");

            return Out_String;
         end Process_Source_List_Line;

         -- This function processes a line from the main section of the report file
         -- of the form:
         --
         --    some_text: file_name
         --
         --  to produce a line of the form:
         --
         --    some_text: <A NAME="html_name">file_link</A>
         --
         --  where:
         --
         --    html_name is the result of applying the function HTML_Name to file_name
         --              to generate the anchor tag referenced from the source file
         --              list (see commentary for Process_Source_List_Line)
         --    file_link is the result of applying the function Create_File_Link_Tag to
         --              file_name to produce a link to the file
         --
         --  This function should be used to produce HTML for the lines in the body
         --  of the report file that beign "Source Filename: "

         function Process_Source_Filename_Line (Line : E_Strings.T) return E_Strings.T
         --# global in HTML_Work_Dir;
         --#        in SPARK_Work_Dir;
         is
            Out_String : E_Strings.T;
            I          : E_Strings.Positions;
            Filename   : E_Strings.T;
            Anchor     : E_Strings.T;
         begin
            Out_String := E_Strings.Empty_String;
            I          := E_Strings.Positions'First;

            -- Copy up to and including ':'
            loop
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) = ':';
               I := I + 1;
            end loop;

            -- Point to next character
            I := I + 1;

            -- Copy spaces up to first char of file name
            loop
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) /= ' ';
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Extract the filename
            Filename := E_Strings.Section (Line, I, (E_Strings.Get_Length (E_Str => Line) - I) + 1);

            -- Create the anchor tag
            Anchor := E_Strings.Copy_String (Str => "<A NAME=""");
            E_Strings.Append_Examiner_String (E_Str1 => Anchor,
                                              E_Str2 => HTML_Name (E_Str => Filename));
            E_Strings.Append_String (E_Str => Anchor,
                                     Str   => """></A>");

            -- Append the anchor tag to the output line
            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => Anchor);

            -- Create the link to the source file and append that to the output line
            E_Strings.Append_Examiner_String
              (E_Str1 => Out_String,
               E_Str2 => Create_File_Link_Tag (Filename_In => Filename,
                                               Relative_To => HTML_Work_Dir));

            return Out_String;
         end Process_Source_Filename_Line;

         -- This procedure processes a line from the main section of the report file
         -- of the form:
         --
         --    some_text: file_name
         --
         --  to produce a line of the form:
         --
         --    some_text: file_link html_link
         --
         --  where:
         --
         --    file_link is the result of applying the function Create_File_Link_Tag to
         --              file_name to produce a link to the file
         --
         --    html_link is the text "[View HTML]" enclosed in tags that make it a
         --              link to the HTML version of the above file.  The HTML
         --              version is assumed to be the name (without directory)
         --              of the specified listing file, with all '.'s changed
         --              to '_'s, with a ".htm" extension and residing in the HTML
         --              subdirectory of the current directory; e.g. H:\foo\bar.lst
         --              becomes HTML/bar_lst.htm
         --
         --  It also updates the value of Saved_Listing_File to be a reference to the
         --  HTML version of the listing for use in processing the source error lines.
         --
         --  This function should be used to produce HTML for the lines in the body
         --  of the report file that beign "Listing Filename: "

         procedure Process_Listing_Filename_Line (Line : in out E_Strings.T)
         --# global in     CommandLineData.Content;
         --#        in     HTML_Work_Dir;
         --#        in     SPARK_Work_Dir;
         --#           out Saved_Listing_File;
         --# derives Line               from *,
         --#                                 CommandLineData.Content,
         --#                                 HTML_Work_Dir,
         --#                                 SPARK_Work_Dir &
         --#         Saved_Listing_File from CommandLineData.Content,
         --#                                 Line;
         is
            Out_String : E_Strings.T;
            I          : E_Strings.Positions;

            Filename       : E_Strings.T;
            HTML_File_Link : E_Strings.T;
         begin
            Out_String := E_Strings.Empty_String;
            I          := E_Strings.Positions'First;

            -- Copy up to and including ':'
            loop
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) = ':';
               I := I + 1;
            end loop;

            -- Point to next character
            I := I + 1;

            -- Copy spaces up to first char of file name
            loop
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) /= ' ';
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Extract the filename
            Filename := E_Strings.Section (Line, I, (E_Strings.Get_Length (E_Str => Line) - I) + 1);

            -- Append link to plain text listing.
            E_Strings.Append_Examiner_String
              (E_Str1 => Out_String,
               E_Str2 => Create_File_Link_Tag (Filename_In => Filename,
                                               Relative_To => HTML_Work_Dir));

            -- And add a space
            E_Strings.Append_Char (E_Str => Out_String,
                                   Ch    => ' ');

            -- Create URL for HTML listing file in Saved_Listing_File
            Saved_Listing_File := CommandLineData.Content.HTML_Directory;
            E_Strings.Append_String (E_Str => Saved_Listing_File,
                                     Str   => "/");
            E_Strings.Append_Examiner_String
              (E_Str1 => Saved_Listing_File,
               E_Str2 => E_Strings.Translate
                 (E_Str     => FileSystem.Just_File (Fn  => Filename,
                                                     Ext => True),
                  From_Char => '.',
                  To_Char   => '_'));

            E_Strings.Append_String (E_Str => Saved_Listing_File,
                                     Str   => ".htm");

            -- Create HTML tags.
            HTML_File_Link :=
              Create_File_Link_Tag_With_Text
              (Filename_In => Saved_Listing_File,
               Relative_To => HTML_Work_Dir,
               Text        => E_Strings.Copy_String (Str => "[View HTML]"));

            E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                              E_Str2 => HTML_File_Link);

            Line := Out_String;
         end Process_Listing_Filename_Line;

         -- *** Comment

         procedure Process_Error_Source_Line (Line : in out E_Strings.T)
         --# global in HTML_Work_Dir;
         --#        in Saved_Listing_File;
         --#        in SPARK_Work_Dir;
         --# derives Line from *,
         --#                   HTML_Work_Dir,
         --#                   Saved_Listing_File,
         --#                   SPARK_Work_Dir;
         is
            Out_String : E_Strings.T;
            I          : E_Strings.Positions;
            Line_No    : E_Strings.T;

            Relative_Listing : E_Strings.T;
            Link_Success     : Boolean;
            Link             : E_Strings.T;
         begin
            if E_Strings.Get_Length (E_Str => Saved_Listing_File) /= 0 then  -- there is a listing file

               Out_String := E_Strings.Empty_String;
               I          := E_Strings.Positions'First;
               Line_No    := E_Strings.Empty_String;

               -- Copy up to the first non-space.
               loop
                  exit when E_Strings.Get_Element (E_Str => Line,
                                                   Pos   => I) /= ' ';
                  E_Strings.Append_Char (E_Str => Out_String,
                                         Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                         Pos   => I));
                  I := I + 1;
               end loop;

               -- Copy digits to Line_No
               while Digit (C => E_Strings.Get_Element (E_Str => Line,
                                                        Pos   => I)) loop
                  E_Strings.Append_Char (E_Str => Line_No,
                                         Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                         Pos   => I));
                  I := I + 1;
               end loop;

               -- Create relative location of Saved_Listing_File
               HTML_Relative_File
                 (File          => Saved_Listing_File,
                  Relative_To   => HTML_Work_Dir,
                  File_Relative => Relative_Listing,
                  Success_Out   => Link_Success);

               if Link_Success then
                  -- Create link to listing based on saved listing file.
                  Link := E_Strings.Copy_String (Str => "<A TARGET=""rbottom"" HREF=""");
                  E_Strings.Append_Examiner_String (E_Str1 => Link,
                                                    E_Str2 => Relative_Listing);
                  E_Strings.Append_String (E_Str => Link,
                                           Str   => "#line");
                  E_Strings.Append_Examiner_String (E_Str1 => Link,
                                                    E_Str2 => Line_No);
                  E_Strings.Append_String (E_Str => Link,
                                           Str   => """>");
                  E_Strings.Append_Examiner_String (E_Str1 => Link,
                                                    E_Str2 => Line_No);
                  E_Strings.Append_String (E_Str => Link,
                                           Str   => "</A>");

               else
                  -- The link is just the number.
                  Link := Line_No;
               end if;

               -- Append link to Out_String
               E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                                 E_Str2 => Link);

               -- Append rest of line
               E_Strings.Append_Examiner_String
                 (E_Str1 => Out_String,
                  E_Str2 => E_Strings.Section (Line, I, (E_Strings.Get_Length (E_Str => Line) - I) + 1));

               Line := Out_String;

            end if;
         end Process_Error_Source_Line;

      begin  -- Process_Report_Line
         if E_Strings.Get_Length (E_Str => Line) > 0 then

            Start_Pos     := First_Char (The_String => Line);
            Debug_Message := E_Strings.Empty_String;

            if CommandLineData.Content.Debug.HTML then
               E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                   E_Str => Line);  -- Line used for debugging.
            end if;

            case Report_File_State is
               when Report_Just_Started =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 5),
                     Str   => "*****") then
                     Report_File_State := Report_Banner_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Banner_Started");
                  end if;
               when Report_Banner_Started =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 5),
                     Str   => "*****") then
                     Report_File_State := Report_Banner_Finished;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Banner_Finished");
                  end if;
               when Report_Banner_Finished =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 7),
                     Str   => "DATE : ") then
                     Report_File_State := Report_Date_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Date_Found");
                  end if;
               when Report_Date_Found =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 8),
                     Str   => "Options:") then
                     Report_File_State := Report_Options_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Options_Found");
                  end if;
               when Report_Options_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 11);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Index_File & "=") then
                     Report_File_State := Report_Index_File_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Index_File_Found");
                  elsif E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Index_File),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 11)) then
                     Report_File_State := Report_Index_File_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Index_File_Found");
                  end if;
               when Report_Index_File_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 13);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Warning_File & "=") then
                     Report_File_State := Report_Warning_File_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Warning_File_Found");
                  elsif E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Warning_File),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 13)) then
                     Report_File_State := Report_Warning_File_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Warning_File_Found");
                  end if;
               when Report_Warning_File_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 21);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Target_Compiler_Data & "=") then
                     Report_File_State := Report_Target_Compiler_Data_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Compiler_Data_Found");
                  elsif E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Target_Compiler_Data),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 21)) then
                     Report_File_State := Report_Target_Compiler_Data_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Compiler_Data_Found");
                  end if;
               when Report_Target_Compiler_Data_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 12);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Config_File & "=") then
                     Report_File_State := Report_Target_Config_File_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Config_File_Found");
                  elsif E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Config_File),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 12)) then
                     Report_File_State := Report_Target_Config_File_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Config_File_Found");
                  end if;
               when Report_Target_Config_File_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 17);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Source_Extension & "=") then
                     Report_File_State := Report_Source_Extension_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_Extension_Found");
                  end if;
               when Report_Source_Extension_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 18);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Listing_Extension & "=") then
                     Report_File_State := Report_Listing_Extension_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Listing_Extension_Found");
                  end if;
               when Report_Listing_Extension_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 12);
                  if E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => CommandLineData.Option_Dictionary_File),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 12)) then
                     Report_File_State := Report_Dictionary_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Dictionary_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => CommandLineData.Option_No_Dictionary) then
                     Report_File_State := Report_Dictionary_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Dictionary_Found");
                  end if;
               when Report_Dictionary_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 12);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Report_File & "=") then
                     Report_File_State := Report_Report_File_Found;
                     Line              := Create_Option_File_Link (Line => Line);
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Report_File_Found");
                  end if;
                  -- NOTE: checking for "noreport_file" is a stupid thing to do!
               when Report_Report_File_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 4);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Html) then
                     -- NOTE: checking for nohtml is an equally stupid thing to do!
                     Report_File_State := Report_HTML_Flag_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_HTML_Flag_Found");
                  end if;
               when Report_HTML_Flag_Found =>
                  -- The RTC and VCG options appear here but as they are optional
                  -- we shall ignore them.
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 10);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Statistics)
                    or else E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Statistics),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 10)) then
                     Report_File_State := Report_Statistics_Flag_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Statistics_Flag_Found");
                  end if;
               when Report_Statistics_Flag_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 15);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Fdl_Identifiers)
                    or else E_Strings.Eq_String
                    (E_Str1 => Compare_String,
                     E_Str2 => E_Strings.Section
                       (E_Str     => E_Strings.Copy_String (Str => "no" & CommandLineData.Option_Fdl_Identifiers),
                        Start_Pos => E_Strings.Positions'First,
                        Length    => 15)) then
                     Report_File_State := Report_FDL_Identifiers_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_FDL_Identifiers_Found");
                  end if;
               when Report_FDL_Identifiers_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 14);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Flow_Analysis & "=") then
                     Report_File_State := Report_Flow_Analysis_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Flow_Analysis_Found");
                  end if;
               when Report_Flow_Analysis_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 5);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "ada83")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "ada95")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "ada20") then
                     Report_File_State := Report_Language_Option_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Language_Option_Found");
                  end if;
               when Report_Language_Option_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 21);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => CommandLineData.Option_Annotation_Character & "=") then
                     Report_File_State := Report_Annotation_Character_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Annotation_Character_Found");
                  end if;
               when Report_Annotation_Character_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 15);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Selected files:") then
                     Report_File_State := Report_Selected_Files_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Selected_Files_Started");
                  end if;
               when Report_Selected_Files_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 24);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Index Filename(s) used w")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "No Index files were used") then
                     Report_File_State := Report_Index_Files_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Index_Files_Started");
                  else
                     Line := Create_File_Link (Line => Line);
                  end if;
               when Report_Index_Files_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 18);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Meta File(s) used ")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "No Meta Files used") then
                     Report_File_State := Report_Meta_Files_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Meta_Files_Started");
                  else
                     Line := Create_File_Link (Line => Line);
                  end if;
               when Report_Meta_Files_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 22);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Full warning reporting")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "Summary warning report") then
                     Report_File_State := Report_Warning_List_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Warning_List_Started");
                  else
                     Line := Create_File_Link (Line => Line);
                  end if;
               when Report_Warning_List_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 26);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Target configuration file:") then
                     Report_File_State := Report_Target_Config_List_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Config_List_Started");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Source Filename(s) used we") then
                     Report_File_State := Report_Source_List_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_List_Started");
                  end if;
               when Report_Target_Config_List_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 4);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Line") then
                     Report_File_State := Report_Target_Error_Line;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Error_Line");
                  end if;
               when Report_Target_Error_Line =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 3);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "!!!")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "***")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "---")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "???") then
                     Process_First_Error_Message_Line (Line       => Line,
                                                       Lookahead  => Lookahead,
                                                       Error_Link => Saved_Error_Link);
                     Report_File_State := Report_Target_Error_Next_Line;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Error_Next_Line");
                  else
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 29);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Source Filename(s) used were:") then
                        Report_File_State := Report_Source_List_Started;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_List_Started");
                     end if;
                  end if;
               when Report_Target_Error_Next_Line =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 29);
                  if Start_Pos > 10 then -- Extra lines of the error message will be indented by 11 characters
                     Process_Next_Error_Message_Line (Line => Line,
                                                      Link => Saved_Error_Link);
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Source Filename(s) used were:") then
                     Report_File_State := Report_Source_List_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_List_Started");
                  else
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 3);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "!!!")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "***")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "---")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "???") then
                        Process_First_Error_Message_Line (Line       => Line,
                                                          Lookahead  => Lookahead,
                                                          Error_Link => Saved_Error_Link);
                        Report_File_State := Report_Target_Error_Next_Line;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Error_Next_Line");
                     else
                        Report_File_State := Report_Target_Error_Line;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Target_Error_Line");
                     end if;
                  end if;
               when Report_Source_List_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 16);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Source Filename:") then
                     Line              := Process_Source_Filename_Line (Line => Line);
                     Report_File_State := Report_Source_File_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_File_Started");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "The following we") then
                     Report_File_State := Report_Missing_Files_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Missing_Files_Started");
                  else
                     Line := Process_Source_List_Line (Line => Line);
                  end if;
               when Report_Missing_Files_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 16);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Source Filename:") then
                     Line              := Process_Source_Filename_Line (Line => Line);
                     Report_File_State := Report_Source_File_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_File_Started");
                  end if;
               when Report_Source_File_Started =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 15);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Listing Filenam") then
                     Process_Listing_Filename_Line (Line => Line);
                     Report_File_State := Report_Listing_Filename_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Listing_Filename_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "No Listing File") then
                     Saved_Listing_File := E_Strings.Empty_String;
                     Report_File_State  := Report_Listing_Filename_Found;
                     Debug_Message      := E_Strings.Copy_String (Str => "Report_Listing_Filename_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "***     no unit") then
                     Report_File_State := Report_No_Units_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_No_Units_Found");
                  end if;
               when Report_Listing_Filename_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 10);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Unit name:") then
                     Report_File_State := Report_Unit_Name_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Unit_Name_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "***     no") then
                     Report_File_State := Report_No_Units_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_No_Units_Found");
                  end if;
               when Report_Unit_Name_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 10);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Unit type:") then
                     Report_File_State := Report_Unit_Type_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Unit_Type_Found");
                  end if;
               when Report_Unit_Type_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 22);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Unit has been analysed")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "Unit has been parsed o") then
                     Report_File_State := Report_Analysed_Message_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Analysed_Message_Found");
                  end if;
               when Report_Analysed_Message_Found | Report_No_Units_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 10);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "No errors ")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "No summari") then
                     Report_File_State := Report_End_Of_Errors;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Errors");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Unit name:") then
                     Report_File_State := Report_Unit_Name_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Unit_Name_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "***     Un") then
                     Report_File_State := Report_End_Of_Errors;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Errors");
                  else
                     Compare_String := E_Strings.Section (Line, E_Strings.Get_Length (E_Str => Line) - 9, 10);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "warning(s)") then
                        Report_File_State := Report_Start_Of_Errors;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Start_Of_Errors");
                     end if;
                  end if;
               when Report_Start_Of_Errors =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 4);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Line") then
                     Report_File_State := Report_Line_Header_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Line_Header_Found");
                  end if;
               when Report_Line_Header_Found =>
                  Process_Error_Source_Line (Line => Line);
                  Report_File_State := Report_Error_Source_Line_Found;
                  Debug_Message     := E_Strings.Copy_String (Str => "Report_Error_Source_Line_Found");
               when Report_Error_Source_Line_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 1);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "^") then
                     -- Process Error Pointer Line (do nothing?)
                     Report_File_State := Report_Error_Source_Pointer_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Error_Source_Pointer_Found");
                  else  -- Some errors don't have pointers
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 3);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "!!!")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "***")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "---")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "???") then

                        Process_First_Error_Message_Line (Line       => Line,
                                                          Lookahead  => Lookahead,
                                                          Error_Link => Saved_Error_Link);

                        Report_File_State := Report_Error_Message_Found;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Error_Message_Found");
                     end if;
                  end if;
               when Report_Error_Source_Pointer_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 3);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "!!!")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "***")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "---")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "???") then

                     Process_First_Error_Message_Line (Line       => Line,
                                                       Lookahead  => Lookahead,
                                                       Error_Link => Saved_Error_Link);
                     Report_File_State := Report_Error_Message_Found;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Error_Message_Found");
                  end if;
               when Report_Error_Message_Found | Report_End_Of_Errors =>
                  if Start_Pos > 10 then -- Extra lines of the error message will be indented by 11 characters
                     Process_Next_Error_Message_Line (Line => Line,
                                                      Link => Saved_Error_Link);
                     Debug_Message := E_Strings.Copy_String (Str => "Report_Next_Error_Message_Line_Found");
                  else
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 16);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Source Filename:") then
                        Line              := Process_Source_Filename_Line (Line => Line);
                        Report_File_State := Report_Source_File_Started;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_File_Started");
                     elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                                 Str   => "No summarized wa") then
                        Report_File_State := Report_End_Of_Errors;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Errors_Found");
                     elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                                 Str   => "--End of file---") then
                        Report_File_State := Report_End_Of_Report_File;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Report_File");

                     elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                                 Str   => "Expected message") then
                        Report_File_State := Report_Justifications_Summary_Found;
                        Debug_Message     := E_Strings.Copy_String (Str => "Report_Justifications_Summary_Found");

                     else -- next error source line found, next error found or

                        -- summarized warnings
                        Compare_String := E_Strings.Section (E_Str     => Line,
                                                             Start_Pos => Start_Pos,
                                                             Length    => 3);
                        if E_Strings.Eq1_String (E_Str => Compare_String,
                                                 Str   => "!!!")
                          or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                        Str   => "***")
                          or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                        Str   => "---")
                          or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                        Str   => "???") then

                           Process_First_Error_Message_Line (Line       => Line,
                                                             Lookahead  => Lookahead,
                                                             Error_Link => Saved_Error_Link);
                           Report_File_State := Report_Error_Message_Found;
                           Debug_Message     := E_Strings.Copy_String (Str => "Report_Error_Message_Found");
                        else  -- error source line or summarized warnings
                           if E_Strings.Get_Length (E_Str => Line) > 9 then
                              Compare_String := E_Strings.Section (Line, E_Strings.Get_Length (E_Str => Line) - 9, 10);
                              if E_Strings.Eq1_String (E_Str => Compare_String,
                                                       Str   => "omprising:") then
                                 Report_File_State := Report_Summarized_Warnings_Found;
                              else
                                 Report_File_State := Report_Error_Source_Line_Found;
                              end if;
                           else
                              Report_File_State := Report_Error_Source_Line_Found;
                           end if;
                           if Report_File_State = Report_Summarized_Warnings_Found then
                              Debug_Message := E_Strings.Copy_String (Str => "Report_Summarized_Warnings_Found");
                           else
                              Process_Error_Source_Line (Line => Line);
                              Debug_Message := E_Strings.Copy_String (Str => "Report_Next_Error_Source_Line_Found");
                           end if;
                        end if;
                     end if;
                  end if;
               when Report_Justifications_Summary_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 16);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "Source Filename:") then
                     Line              := Process_Source_Filename_Line (Line => Line);
                     Report_File_State := Report_Source_File_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_File_Started");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "No summarized wa") then
                     Report_File_State := Report_End_Of_Errors;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Errors_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "--End of file---") then
                     Report_File_State := Report_End_Of_Report_File;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Report_File");
                  else
                     -- Here, we could process the justification table line to include
                     -- HTML links the relevant source files, lines of code, and error
                     -- messages.  TBD!
                     -- Both "Brief" and "Full" justifications mode need to be dealt with here.
                     null;
                  end if;

               when Report_Summarized_Warnings_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 7);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "(*Note:") then
                     Report_File_State := Report_End_Of_Errors;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_End_Of_Errors_Found");
                  elsif E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "Source ") then
                     Line              := Process_Source_Filename_Line (Line => Line);
                     Report_File_State := Report_Source_File_Started;
                     Debug_Message     := E_Strings.Copy_String (Str => "Report_Source_File_Started");
                  end if;
               when Report_Blank_After_Error_Found | Report_End_Of_Report_File =>
                  ScreenEcho.Put_Line ("An error occurred during HTML report file generation: Invalid Report_File_State");
            end case;

            if CommandLineData.Content.Debug.HTML then
               E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                   E_Str => Debug_Message);  -- Line used for debugging.
            end if;

         end if;
      end Process_Report_Line;

   begin  -- Gen_Report_HTML

      -- Do nothing if previous HTML generation failed in some way.
      if Generate_HTML then

         -- Inform user that HTML generation is taking place.
         if CommandLineData.Content.Echo and not CommandLineData.Content.Brief then
            ScreenEcho.Echo (E_Strings.Copy_String (Str => "Generating report file HTML"));
         end if;

         -- Initialise Saved_Error_Link and Saved_Listing_File.
         Saved_Error_Link   := E_Strings.Empty_String;
         Saved_Listing_File := E_Strings.Empty_String;

         -- Initialise file handles.
         HTML_Report_File  := SPARK_IO.Null_File;
         Plain_Report_File := SPARK_IO.Null_File;

         -- Generate filename of the form HTML/<report_filename>.htm
         Plain_Report_Filename := FileSystem.Just_File (CommandLineData.Content.Report_File_Name, True);
         HTML_Report_Filename  := E_Strings.Translate (E_Str     => Plain_Report_Filename,
                                                       From_Char => '.',
                                                       To_Char   => '_');
         E_Strings.Append_String (E_Str => HTML_Report_Filename,
                                  Str   => ".htm");
         HTML_Report_Filename := FileSystem.Case_Of_Files_For_Create (E_Str => HTML_Filename (Filename => HTML_Report_Filename));

         -- Create HTML report file.
         E_Strings.Create
           (File         => HTML_Report_File,
            Name_Of_File => HTML_Report_Filename,
            Form_Of_File => "",
            Status       => HTML_Report_File_Created_OK);

         -- Check for errors.  Stop HTML generation if create failed.
         if HTML_Report_File_Created_OK /= SPARK_IO.Ok then

            ScreenEcho.Put_Line ("An error occurred while creating the HTML report file.");
            ScreenEcho.Put_Line ("No further HTML generation will occur.");
            Generate_HTML := False;

         else  -- file created successfully.

            -- Open report file for input
            CommandLineData.Normalize_File_Name_To_Output_Directory (F => Plain_Report_Filename);

            -- Open report file for input
            E_Strings.Open
              (File         => Plain_Report_File,
               Mode_Of_File => SPARK_IO.In_File,
               Name_Of_File => Plain_Report_Filename,
               Form_Of_File => "",
               Status       => Plain_Report_File_Open_OK);

            -- Check for errors.  Stop HTML generation if open failed.
            if Plain_Report_File_Open_OK /= SPARK_IO.Ok then

               ScreenEcho.Put_Line ("An error occurred while opening the report file for HTML generation.");
               ScreenEcho.Put_Line ("No further HTML generation will occur.");
               Generate_HTML := False;

            else  -- file opened successfully.
               Write_HTML_Report_Header;

               -- Fill Line_Buffer and Lookahead_Buffer
               if not SPARK_IO.End_Of_File (Plain_Report_File) then
                  E_Strings.Get_Line (File  => Plain_Report_File,
                                      E_Str => Line_Buffer);
                  Line_Buffer := Convert_Special_HTML_Chars (Line => Line_Buffer);
                  if not SPARK_IO.End_Of_File (Plain_Report_File) then
                     E_Strings.Get_Line (File  => Plain_Report_File,
                                         E_Str => Lookahead_Buffer);
                     Lookahead_Buffer := Convert_Special_HTML_Chars (Line => Lookahead_Buffer);

                     -- Process first line
                     Process_Report_Line (Line      => Line_Buffer,
                                          Lookahead => Lookahead_Buffer);

                     -- Write line and process rest of file.
                     loop
                        E_Strings.Put_Line (File  => HTML_Report_File,
                                            E_Str => Line_Buffer);

                        if SPARK_IO.End_Of_File (Plain_Report_File) then

                           -- Process and output the lookahead buffer.
                           --# accept Flow, 10, Saved_Error_Link, "Expected ineffective assignment to Saved_Error_Link" &
                           --#        Flow, 10, Saved_Listing_File, "Expected ineffective assignment to Saved_Listing_File" &
                           --#        Flow, 10, Report_File_State, "Expected ineffective assignment to Report_File_State";
                           Process_Report_Line (Line      => Lookahead_Buffer,
                                                Lookahead => E_Strings.Empty_String);
                           --# end accept;
                           -- this is the last call and so the saved values will not be used.
                           E_Strings.Put_Line (File  => HTML_Report_File,
                                               E_Str => Lookahead_Buffer);
                           exit;
                        end if;

                        Line_Buffer := Lookahead_Buffer;

                        E_Strings.Get_Line (File  => Plain_Report_File,
                                            E_Str => Lookahead_Buffer);
                        Lookahead_Buffer := Convert_Special_HTML_Chars (Line => Lookahead_Buffer);

                        Process_Report_Line (Line      => Line_Buffer,
                                             Lookahead => Lookahead_Buffer);

                     end loop;

                  end if;

               end if;

               Write_HTML_Report_Footer;

               -- Close input report file.
               --# accept Flow, 10, Plain_Report_File, "Expected ineffective assignment to Plain_Report_File";
               SPARK_IO.Close (File   => Plain_Report_File,
                               Status => Plain_Report_File_Closed_OK);
               --# end accept;

               -- Check for errors.  Stop HTML generation if close failed.
               if Plain_Report_File_Closed_OK /= SPARK_IO.Ok then
                  ScreenEcho.Put_Line ("An error occurred while closing the report file after HTML generation.");
                  ScreenEcho.Put_Line ("No further HTML generation will occur.");
                  Generate_HTML := False;
               end if;
               --  We don't use an else here as we need to try and close the HTML file too.

               -- Close HTML output file.
               --# accept Flow, 10, HTML_Report_File, "Expected ineffective assignment to HTML_Report_File";
               SPARK_IO.Close (File   => HTML_Report_File,
                               Status => HTML_Report_File_Closed_OK);
               --# end accept;

               -- Check for errors.  Stop HTML generation if close failed.
               if HTML_Report_File_Closed_OK /= SPARK_IO.Ok then
                  ScreenEcho.Put_Line ("An error occurred while closing the HTML report file after HTML generation.");
                  ScreenEcho.Put_Line ("No further HTML generation will occur.");
                  Generate_HTML := False;
               end if;

            end if;

         end if;

      end if;
      --# accept Flow, 601, Generate_HTML, HTML_Work_Dir, "False coupling in SPARK_IO" &
      --#        Flow, 601, Generate_HTML, SPARK_Work_Dir, "False coupling in SPARK_IO";
   end Gen_Report_HTML;  -- Flow errors expected due to false coupling in SPARK_IO.

   -----------------------------------------------------------------------------
   --
   --  -------------
   --  Gen_Listing_HTML
   --  -------------
   --
   --  This procedure generates the HTMLified listing file from the plain text
   --  listing file.
   --
   --  It is assumed that the HTML output flag has already been tested.  The call to
   --  Gen_Listing_HTML should look something like:
   --
   --    if CommandLineData.Content.HTML then
   --       SparkHTML.Gen_Listing_HTML;
   --    end if;
   --
   --  The checking of the Generate_HTML flag is done internally.
   --
   --  If the Generate_HTML flag is false this procedure does nothing, otherwise,
   --  it does exactly the following:
   --
   --     - creates an HTML file (named <lis_file>.htm where <lis_file> is the name
   --       of the listing file associated with the file descriptor passed as a
   --       parameter with all '.' characters changed to '_' characters;
   --     - processes each line of the plain text listing file using the
   --       Process_Listing_Line procedure (the functionality of this is described
   --       at the declaration of Process_Listing_Line);
   --     - writes the processed lines to the HTML listing file;
   --
   --  Error trapping:
   --
   --  Incorrect generation of a listing files should not affect further HTML generation.
   --  In fact, it is better that we try and generate HTML for as many listing files as
   --  possible.  So if HTML generation fails in this procedure the Generate_HTML flag is
   --  not set to false.
   --
   procedure Gen_Listing_HTML (File_Descriptor : in ContextManager.FileDescriptors) is
      Message : E_Strings.T;

      Saved_Error_Link : E_Strings.T;

      HTML_Listing_File  : SPARK_IO.File_Type;
      Plain_Listing_File : SPARK_IO.File_Type;

      HTML_Listing_Filename   : E_Strings.T;
      Plain_Listing_Filename  : E_Strings.T;
      Echoed_Listing_Filename : E_Strings.T;

      HTML_Listing_File_Created_OK : SPARK_IO.File_Status;
      Plain_Listing_File_Open_OK   : SPARK_IO.File_Status;

      HTML_Listing_File_Closed_OK  : SPARK_IO.File_Status;
      Plain_Listing_File_Closed_OK : SPARK_IO.File_Status;

      Line_Buffer      : E_Strings.T;
      Lookahead_Buffer : E_Strings.T;

      Listing_File_State : Listing_File_States := Listing_Just_Started;

      --
      -- Subprograms
      --
      -- This sub-procedure writes HTML content to the start of the listing file.
      -- It assumes that the file handle HTML_Listing_File is the open HTML
      -- listing file.  This procedure will not open or close the file.
      --
      -- The HTML written specifies the title of the page (using the filename
      -- specified as a parameter) and some formatting tags.  The formatting
      -- is <PRE> (pre-processed text) which displays text exactly as given,
      -- and <TT> which sets the typewriter-text font; I use this because it
      -- usually results in a fixed-width font being used.
      --
      -- When writing the end of the listing file these tags need to be closed,
      -- as do the <BODY> and <HTML> tags.  This should be done by calling
      -- the Write_HTML_Listing_Footer procedure.
      --
      -- Error trapping:
      --
      -- All error trapping is performed within SPARK_IO.
      -- The SPARK_IO routines called do not return status parameters and so
      -- no error trapping can be done here.

      procedure Write_HTML_Listing_Header (Filename : in E_Strings.T)
      --# global in     CommandLineData.Content;
      --#        in     HTML_Listing_File;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                CommandLineData.Content,
      --#                                Filename,
      --#                                HTML_Listing_File;

      is
         Local_Filename : E_Strings.T;
      begin
         SPARK_IO.Put_Line (HTML_Listing_File, "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN""", 61);
         SPARK_IO.Put_Line (HTML_Listing_File, "            ""http://www.w3.org/TR/REC-html40/loose.dtd"">", 56);
         SPARK_IO.Put_Line (HTML_Listing_File, "<html>", 6);
         SPARK_IO.Put_Line (HTML_Listing_File, "  <head>", 8);
         SPARK_IO.Put_String (HTML_Listing_File, "    <title>Examiner HTML Listing File: ", 39);
         if CommandLineData.Content.Plain_Output then
            Local_Filename := FileSystem.Just_File (Fn  => Filename,
                                                    Ext => True);
         else
            Local_Filename := Filename;
         end if;
         E_Strings.Put_String (File  => HTML_Listing_File,
                               E_Str => Local_Filename);
         SPARK_IO.Put_Line (HTML_Listing_File, "</title>", 8);
         SPARK_IO.Put_Line (HTML_Listing_File, "  </head>", 9);
         SPARK_IO.Put_Line (HTML_Listing_File, "  <body>", 8);
         SPARK_IO.Put_Line (HTML_Listing_File, "    <pre>", 9);
         SPARK_IO.Put_Line (HTML_Listing_File, "      <tt>", 10);
      end Write_HTML_Listing_Header;

      -- This subprocedure writes HTML content to the end of the listing file.
      -- It assumes that the file handle HTML_Listing_File is the open HTML
      -- listing file.  This procedure will not open or close the file.
      --
      -- The HTML written closes all the formatting tags that were opened
      -- by the call to Write_HTML_Listing_Header.
      --
      -- Error trapping:
      --
      -- All error trapping is performed within SPARK_IO.
      -- The SPARK_IO routines called do not return status parameters and so
      -- no error trapping can be done here.

      procedure Write_HTML_Listing_Footer
      --# global in     HTML_Listing_File;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                HTML_Listing_File;

      is
      begin
         SPARK_IO.Put_Line (HTML_Listing_File, "      </tt>", 11);
         SPARK_IO.Put_Line (HTML_Listing_File, "    </pre>", 10);
         SPARK_IO.Put_Line (HTML_Listing_File, "  </body>", 9);
         SPARK_IO.Put_Line (HTML_Listing_File, "</html>", 7);
      end Write_HTML_Listing_Footer;

      -- This procedure is used to convert a line of the listing file into HTML.
      -- It is effectively a parser for the listing file.
      --
      -- The procedure design is based on a state machine.  The global variable
      -- Listing_File_State records our current location within the listing file (in
      -- terms of what information has already been read).
      --
      -- Given a line, we can determine the line's meaning from our current state
      -- and the contents of the line (all blank lines are ignored).
      --
      -- We can use this method to parse almost all the information in the source   --***
      -- file.  Those bits that we don't parse are usually optional (such as the    --***
      -- flag "rtc" in the options list) and require no translation to HTML anyway. --***
      --
      -- Once the procedure understands what a line represents it updates the
      -- Listing_File_State and processes the line by calling an appropriate
      -- subroutine.
      --
      -- The procedure contains a debugging feature which reports each line that it
      -- finds to the screen along with a message for each line that it recognises.
      -- This only happens if the -debug switch is given on the commandline.

      procedure Process_Listing_Line (Line      : in out E_Strings.T;
                                      Lookahead : in     E_Strings.T)
      --# global in     CommandLineData.Content;
      --#        in out Listing_File_State;
      --#        in out Saved_Error_Link;
      --#        in out SPARK_IO.File_Sys;
      --# derives Line,
      --#         Saved_Error_Link   from Line,
      --#                                 Listing_File_State,
      --#                                 Lookahead,
      --#                                 Saved_Error_Link &
      --#         Listing_File_State from *,
      --#                                 Line &
      --#         SPARK_IO.File_Sys  from *,
      --#                                 CommandLineData.Content,
      --#                                 Line,
      --#                                 Listing_File_State,
      --#                                 Lookahead;
      is
         Start_Pos      : E_Strings.Positions;
         Compare_String : E_Strings.T;
         Debug_Message  : E_Strings.T;

         -- This procedure processes a listing source line.  This line should
         -- consist of some spaces followed by a number followed by some more
         -- spaces and the text of the source line.
         --
         -- The line number is extracted and put into an HTML tag of the form
         -- <A NAME="lineXXX">XXX</A> where XXX is the line number.  The rest of the
         -- source line is unchanged.

         procedure Process_Listing_Source_Line (Line : in out E_Strings.T)
         --# derives Line from *;
         is
            Out_String : E_Strings.T;
            I          : E_Strings.Positions;
            Line_No    : E_Strings.T;
            Link       : E_Strings.T;
         begin
            Out_String := E_Strings.Empty_String;
            I          := E_Strings.Positions'First;
            Line_No    := E_Strings.Empty_String;

            -- Copy up to the first non-space.
            loop
               exit when E_Strings.Get_Element (E_Str => Line,
                                                Pos   => I) /= ' ';
               E_Strings.Append_Char (E_Str => Out_String,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            -- Copy digits to Line_No
            while Digit (C => E_Strings.Get_Element (E_Str => Line,
                                                     Pos   => I)) loop
               E_Strings.Append_Char (E_Str => Line_No,
                                      Ch    => E_Strings.Get_Element (E_Str => Line,
                                                                      Pos   => I));
               I := I + 1;
            end loop;

            if E_Strings.Get_Length (E_Str => Line_No) > 0 then
               -- Create anchor based on number
               Link := E_Strings.Copy_String (Str => "<A NAME=""line");
               E_Strings.Append_Examiner_String (E_Str1 => Link,
                                                 E_Str2 => Line_No);
               E_Strings.Append_String (E_Str => Link,
                                        Str   => """>");
               E_Strings.Append_Examiner_String (E_Str1 => Link,
                                                 E_Str2 => Line_No);
               E_Strings.Append_String (E_Str => Link,
                                        Str   => "</A>");

               -- Append link to Out_String
               E_Strings.Append_Examiner_String (E_Str1 => Out_String,
                                                 E_Str2 => Link);

            end if;

            -- Append rest of line
            E_Strings.Append_Examiner_String
              (E_Str1 => Out_String,
               E_Str2 => E_Strings.Section (Line, I, (E_Strings.Get_Length (E_Str => Line) - I) + 1));

            Line := Out_String;
         end Process_Listing_Source_Line;

      begin  -- Process_Listing_Line
         if E_Strings.Get_Length (E_Str => Line) > 0 then

            Start_Pos     := First_Char (The_String => Line);
            Debug_Message := E_Strings.Empty_String;

            if CommandLineData.Content.Debug.HTML then
               E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                   E_Str => Line);  -- Line used for debugging.
            end if;

            case Listing_File_State is
               when Listing_Just_Started =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 5),
                     Str   => "*****") then
                     Listing_File_State := Listing_Banner_Started;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Banner_Started");
                  end if;
               when Listing_Banner_Started =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 5),
                     Str   => "*****") then
                     Listing_File_State := Listing_Banner_Finished;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Banner_Finished");
                  end if;
               when Listing_Banner_Finished =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 7),
                     Str   => "DATE : ") then
                     Listing_File_State := Listing_Date_Found;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Date_Found");
                  end if;
               when Listing_Date_Found =>
                  if E_Strings.Eq1_String
                    (E_Str => E_Strings.Section (E_Str     => Line,
                                                 Start_Pos => Start_Pos,
                                                 Length    => 4),
                     Str   => "Line") then
                     Listing_File_State := Listing_Line_Heading_Found;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Line_Heading_Found");
                  end if;
               when Listing_Line_Heading_Found | Listing_Source_Line_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 1);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "^") then
                     -- Process Error Pointer Line (do nothing?)
                     Listing_File_State := Listing_Error_Source_Pointer_Found;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Error_Source_Pointer_Found");
                  else  -- Some errors don't have pointers
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 3);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "!!!")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "***")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "---")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "???") then

                        Process_First_Error_Message_Line (Line       => Line,
                                                          Lookahead  => Lookahead,
                                                          Error_Link => Saved_Error_Link);

                        Listing_File_State := Listing_Error_Message_Found;
                        Debug_Message      := E_Strings.Copy_String (Str => "Listing_Error_Message_Found");
                     else
                        Process_Listing_Source_Line (Line => Line);
                        Listing_File_State := Listing_Source_Line_Found;
                        Debug_Message      := E_Strings.Copy_String (Str => "Listing_Source_Line_Found");
                     end if;
                  end if;
               when Listing_Error_Source_Pointer_Found =>
                  Compare_String := E_Strings.Section (E_Str     => Line,
                                                       Start_Pos => Start_Pos,
                                                       Length    => 3);
                  if E_Strings.Eq1_String (E_Str => Compare_String,
                                           Str   => "!!!")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "***")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "---")
                    or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                  Str   => "???") then

                     Process_First_Error_Message_Line (Line       => Line,
                                                       Lookahead  => Lookahead,
                                                       Error_Link => Saved_Error_Link);
                     Listing_File_State := Listing_Error_Message_Found;
                     Debug_Message      := E_Strings.Copy_String (Str => "Listing_Error_Message_Found");
                  end if;
               when Listing_Error_Message_Found =>
                  if Start_Pos > 5 then -- Extra lines of the error message will be indented by 11 characters
                     Process_Next_Error_Message_Line (Line => Line,
                                                      Link => Saved_Error_Link);
                     Debug_Message := E_Strings.Copy_String (Str => "Listing_Next_Error_Message_Line_Found");
                  else
                     Compare_String := E_Strings.Section (E_Str     => Line,
                                                          Start_Pos => Start_Pos,
                                                          Length    => 3);
                     if E_Strings.Eq1_String (E_Str => Compare_String,
                                              Str   => "!!!")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "***")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "---")
                       or else E_Strings.Eq1_String (E_Str => Compare_String,
                                                     Str   => "???") then

                        Process_First_Error_Message_Line (Line       => Line,
                                                          Lookahead  => Lookahead,
                                                          Error_Link => Saved_Error_Link);
                        Listing_File_State := Listing_Error_Message_Found;
                        Debug_Message      := E_Strings.Copy_String (Str => "Listing_Error_Message_Found");
                     else  -- error source line or summarized warnings
                        Process_Listing_Source_Line (Line => Line);
                        Listing_File_State := Listing_Source_Line_Found;
                        Debug_Message      := E_Strings.Copy_String (Str => "Listing_Next_Source_Line_Found");
                     end if;
                  end if;
               when Listing_End_Of_Listing_File =>
                  ScreenEcho.Put_Line ("An error occurred during HTML listing file generation: Invalid Report_File_State");
            end case;

            if CommandLineData.Content.Debug.HTML then
               E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                   E_Str => Debug_Message);  -- Line used for debugging.
            end if;

         end if;
      end Process_Listing_Line;

   begin
      if Generate_HTML then

         -- Get name of listing file.
         ContextManager.Ops.GetListingFileName (File_Descriptor, Plain_Listing_Filename);

         if CommandLineData.Content.Echo and not CommandLineData.Content.Brief then
            -- Echo message to screen.
            Message := E_Strings.Copy_String (Str => "Generating listing file HTML for ");

            if CommandLineData.Content.Plain_Output then
               Echoed_Listing_Filename := FileSystem.Just_File (Fn  => Plain_Listing_Filename,
                                                                Ext => True);
            else
               Echoed_Listing_Filename := Plain_Listing_Filename;
            end if;
            E_Strings.Append_Examiner_String (E_Str1 => Message,
                                              E_Str2 => Echoed_Listing_Filename);
            ScreenEcho.Echo (Message);
         end if;

         -- Initialise Saved_Error_Link.
         Saved_Error_Link := E_Strings.Empty_String;

         -- Initialise file handles.
         HTML_Listing_File  := SPARK_IO.Null_File;
         Plain_Listing_File := SPARK_IO.Null_File;

         -- Generate filename of the form HTML/<listing_filename>.htm
         HTML_Listing_Filename := E_Strings.Translate (E_Str     => Plain_Listing_Filename,
                                                       From_Char => '.',
                                                       To_Char   => '_');
         E_Strings.Append_String (E_Str => HTML_Listing_Filename,
                                  Str   => ".htm");
         HTML_Listing_Filename :=
           FileSystem.Case_Of_Files_For_Create (E_Str => HTML_Filename (Filename => HTML_Listing_Filename));

         -- Create HTML listing file.
         E_Strings.Create
           (File         => HTML_Listing_File,
            Name_Of_File => HTML_Listing_Filename,
            Form_Of_File => "",
            Status       => HTML_Listing_File_Created_OK);

         -- Check for errors.
         if HTML_Listing_File_Created_OK /= SPARK_IO.Ok then

            ScreenEcho.Put_String ("An error occurred while creating the HTML listing file ");
            E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                E_Str => HTML_Listing_Filename);

         else  -- file created successfully.
            CommandLineData.Normalize_File_Name_To_Output_Directory (F => Plain_Listing_Filename);

            -- Open listing file for input
            E_Strings.Open
              (File         => Plain_Listing_File,
               Mode_Of_File => SPARK_IO.In_File,
               Name_Of_File => Plain_Listing_Filename,
               Form_Of_File => "",
               Status       => Plain_Listing_File_Open_OK);

            -- Check for errors.
            if Plain_Listing_File_Open_OK /= SPARK_IO.Ok then

               ScreenEcho.Put_String ("An error occurred while opening the listing file ");
               E_Strings.Put_String (File  => SPARK_IO.Standard_Output,
                                     E_Str => Plain_Listing_Filename);
               ScreenEcho.Put_Line (" for HTML generation.");

            else  -- file opened successfully.
               Write_HTML_Listing_Header (Filename => Plain_Listing_Filename);

               -- Fill Line_Buffer and Lookahead_Buffer
               if not SPARK_IO.End_Of_File (Plain_Listing_File) then
                  E_Strings.Get_Line (File  => Plain_Listing_File,
                                      E_Str => Line_Buffer);
                  Line_Buffer := Convert_Special_HTML_Chars (Line => Line_Buffer);
                  if not SPARK_IO.End_Of_File (Plain_Listing_File) then
                     E_Strings.Get_Line (File  => Plain_Listing_File,
                                         E_Str => Lookahead_Buffer);
                     Lookahead_Buffer := Convert_Special_HTML_Chars (Line => Lookahead_Buffer);

                     -- Process first line
                     Process_Listing_Line (Line      => Line_Buffer,
                                           Lookahead => Lookahead_Buffer);

                     -- Write line and process rest of file.
                     loop
                        E_Strings.Put_Line (File  => HTML_Listing_File,
                                            E_Str => Line_Buffer);

                        if SPARK_IO.End_Of_File (Plain_Listing_File) then

                           -- Process and output the lookahead buffer.
                           --# accept Flow, 10, Listing_File_State, "Expected ineffective assignment to Listing_File_State" &
                           --#        Flow, 10, Saved_Error_Link, "Expected ineffective assignment to Saved_Error_Link";
                           Process_Listing_Line (Line      => Lookahead_Buffer,
                                                 Lookahead => E_Strings.Empty_String);
                           --# end accept;
                           -- this is the last call and so the saved values will not be used.
                           E_Strings.Put_Line (File  => HTML_Listing_File,
                                               E_Str => Lookahead_Buffer);
                           exit;
                        end if;

                        Line_Buffer := Lookahead_Buffer;

                        E_Strings.Get_Line (File  => Plain_Listing_File,
                                            E_Str => Lookahead_Buffer);
                        Lookahead_Buffer := Convert_Special_HTML_Chars (Line => Lookahead_Buffer);

                        Process_Listing_Line (Line      => Line_Buffer,
                                              Lookahead => Lookahead_Buffer);

                     end loop;

                  end if;

               end if;

               Write_HTML_Listing_Footer;

               -- Close input listing file.
               --# accept Flow, 10, Plain_Listing_File, "Expected ineffective assignment to Plain_Listing_File";
               SPARK_IO.Close (File   => Plain_Listing_File,
                               Status => Plain_Listing_File_Closed_OK);
               --# end accept;

               -- Check for errors.
               if Plain_Listing_File_Closed_OK /= SPARK_IO.Ok then
                  ScreenEcho.Put_String ("An error occurred while closing the listing file ");
                  E_Strings.Put_String (File  => SPARK_IO.Standard_Output,
                                        E_Str => Plain_Listing_Filename);
                  ScreenEcho.Put_Line (" after HTML generation.");
               end if;
               --  We don't use an else here as we need to try and close the HTML file too.

               -- Close HTML output file.
               --# accept Flow, 10, HTML_Listing_File, "Expected ineffective assignment to HTML_Listing_File";
               SPARK_IO.Close (File   => HTML_Listing_File,
                               Status => HTML_Listing_File_Closed_OK);
               --# end accept;

               -- Check for errors.
               if HTML_Listing_File_Closed_OK /= SPARK_IO.Ok then
                  ScreenEcho.Put_String ("An error occurred while closing the HTML listing file ");
                  E_Strings.Put_String (File  => SPARK_IO.Standard_Output,
                                        E_Str => HTML_Listing_Filename);
                  ScreenEcho.Put_Line (" after HTML generation.");
               end if;

            end if;

         end if;

      end if;
   end Gen_Listing_HTML;

end SparkHTML;
