-------------------------------------------------------------------------------
-- (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 CommandLine;
with SPARK_IO;
with Directory_Operations;
with RegularExpression;
with SparkMakeDebug;
with SparkMakeErrors;
with SystemErrors;
with CommandLineData;
with FileSystem;
with Version;

package body SparkMakeCommandLine
--# own State is Anno_Char_Specified,
--#              Debug,
--#              Duplicates,
--#              Index_File,
--#              Language_Found,
--#              Meta_File,
--#              No_Index,
--#              No_Meta,
--#              Path,
--#              Root_File,
--#              The_Directories,
--#              The_Exc_Reg_Exps,
--#              The_Inc_Reg_Exps;
is

   Path       : Path_Type;
   Root_File  : E_Strings.T;
   Meta_File  : E_Strings.T;
   Index_File : E_Strings.T;
   Debug      : Boolean;
   Duplicates : Boolean;

   No_Meta  : Boolean;
   No_Index : Boolean;

   Anno_Char_Specified : Boolean;
   Language_Found      : Boolean;

   The_Directories  : StringList.Object;
   The_Inc_Reg_Exps : StringList.Object;
   The_Exc_Reg_Exps : StringList.Object;

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

   function Normalise_Root_Filename (N : E_Strings.T) return E_Strings.T
   --
   -- Ensures any relative path is fully expanded.
   is
      Result : E_Strings.T;
   begin
      Result := Directory_Operations.Normalize_Path_Name (Name      => N,
                                                          Directory => Directory_Operations.Current_Directory);
      return Result;
   end Normalise_Root_Filename;

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

   function Normalise_Dir_Name (N : E_Strings.T) return E_Strings.T
   --
   -- Ensures any relative path is fully expanded and that
   -- there is a directory separator at the end.
   is
      Result : E_Strings.T;
   begin
      Result := Normalise_Root_Filename (N => N);
      if E_Strings.Get_Length (E_Str => Result) > 0
        and then E_Strings.Get_Element (E_Str => Result,
                                        Pos   => E_Strings.Get_Length (E_Str => Result)) /=
        Directory_Operations.Directory_Separator then
         E_Strings.Append_Char (E_Str => Result,
                                Ch    => Directory_Operations.Directory_Separator);
      else
         Result := E_Strings.Empty_String;
      end if;
      return Result;
   end Normalise_Dir_Name;

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

   function Reg_Exp (For_File : in E_Strings.T) return E_Strings.T
   --
   -- Returns a regulat expression that will exactly match the given file.
   is
      Result : E_Strings.T;
      Char   : Character;
      Escape : constant Character := '\';
   begin
      Result := E_Strings.Empty_String;
      for I in E_Strings.Positions range 1 .. E_Strings.Get_Length (E_Str => For_File) loop
         Char := E_Strings.Get_Element (E_Str => For_File,
                                        Pos   => I);
         if Char = '\'
           or else Char = '('
           or else Char = ')'
           or else Char = '['
           or else Char = ']'
           or else Char = '.'
           or else Char = '*'
           or else Char = '+'
           or else Char = '?'
           or else Char = '^' then
            -- We must escape these characters
            E_Strings.Append_Char (E_Str => Result,
                                   Ch    => Escape);
         end if;
         E_Strings.Append_Char (E_Str => Result,
                                Ch    => Char);
      end loop;
      return Result;
   end Reg_Exp;

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

   procedure Report_Usage
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *;
   --
   -- Outputs the usage to the user.
   is
      --# hide Report_Usage;
   begin
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Usage: sparkmake [option] [rootfile]", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Options - all may be abbreviated to the shortest unique prefix", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Options - all input options may be repeated as necessary", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "option         = input_option | output_option | behaviour_option | help_option",
         0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "input_option   = dir_option | include_option | exclude_option", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "dir_option     = " & "-" & "directory=dirname - Look in and under dirname as well as cwd.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                      Default: none", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "include_option = " & "-" & "include=reg_exp   - Only include files if full path matches.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                      Default: *\.ad[bs]", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "exclude_option = " & "-" & "exclude=reg_exp   - Exclude files if full path matches.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                      Default: none", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "reg_exp        = term", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "term           = char", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "term           = elmt elmt ...     -- concatenation", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "term           = *                 -- any string of 0 or more characters", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "term           = ?                 -- matches any character", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "term           = [char char ...]   -- matches any character listed", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "term           = [char - char]     -- matches any character in given range",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = nchr              -- matches given character", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = [nchr nchr ...]   -- matches any character listed", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = [^ nchr nchr ...] -- matches any character not listed", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = [char - char]     -- matches chars in given range", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = .                 -- matches any single character", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "elmt           = ( regexp )        -- parens used for grouping", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "char           = any character, including special characters", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "nchr           = any character except \()[].*+?^ or \char to match char", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "The characters '{' and '}' are NOT allowed to appear in any regular expression",
         0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "behaviour_option = duplicates_option | annotation_option | language_option",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "duplicates_option = " & "-" & "duplicates_are_errors - Fail if duplicate units are found.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: false", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "annotation_option = " & "-" & "annotation_character  - Specify annotation character.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: #", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "language_option = " & "-" & "language=83 | 95 | 2005 - Specify language profile.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: 95", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "output_option  = index_option | meta_option | noindex_option | nometa_option |",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                 path_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "index_option   = " & "-" & "index=file-spec          - The index file.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default rootfile.idx", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "meta_option    = " & "-" & "meta=file-spec           - The meta file.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default rootfile.smf", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "noindex_option = " & "-" & "noindexfile              - Suppress generation of index file.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: false", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "nometa_option  = " & "-" & "nometafile               - Suppress generation of meta file.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: false", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "path_option    = " & "-" & "path=full | relative     - Produce relative or full pathnames.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             Default: full", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "help_option    = helper_option | version_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "helper_option  = " & "-" & "help    - print off help information.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "version_option = " & "-" & "version - print off version information.", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "rootfile       = file-spec                 - The file to make.", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                             Default: Produce index and metafile",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                             for analysis of all files in and",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                             under current directory.", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line1, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line2, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line3, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line4, 0);

   end Report_Usage;

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

   procedure Report_Version
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *;
   --
   -- Outputs the usage to the user.
   is
      --# hide Report_Version;
   begin
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "SPARKMake " & Version.Toolset_Banner_Line, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Copyright, 0);
   end Report_Version;

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

   procedure Process_Root_File_Argument (Arg     : in     E_Strings.T;
                                         Success :    out Boolean)
   --# global in     Debug;
   --#        in out SPARK_IO.File_Sys;
   --#           out Root_File;
   --# derives Root_File,
   --#         Success           from Arg &
   --#         SPARK_IO.File_Sys from *,
   --#                                Arg &
   --#         null              from Debug;
   is
   begin
      -- Expand the root file to its full path
      Root_File := Normalise_Root_Filename (N => Arg);

      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found root file argument: ",
                                            E_Text => Arg);
         SparkMakeDebug.Report_Text_E_Text (Text   => "Expanded to: ",
                                            E_Text => Root_File);
      end if;

      -- Does the file exist?
      if Directory_Operations.Is_File (Path => Root_File) then
         Success := True;
      else
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Cannot_Find_File,
            E_Str1    => Root_File,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Root_File_Argument;

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

   procedure Process_Include_Switch (Switch  : in     E_Strings.T;
                                     Arg     : in     E_Strings.T;
                                     Success :    out Boolean)
   --# global in     Debug;
   --#        in out SPARK_IO.File_Sys;
   --#        in out The_Inc_Reg_Exps;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Arg,
   --#                                Switch &
   --#         Success           from Arg &
   --#         The_Inc_Reg_Exps  from *,
   --#                                Arg &
   --#         null              from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found include switch: ",
                                            E_Text => Arg);
      end if;

      if E_Strings.Is_Empty (E_Str => Arg) or else RegularExpression.Is_Null (O => RegularExpression.Create (Arg)) then
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Invalid_Argument,
            E_Str1    => Arg,
            E_Str2    => Switch,
            E_Str3    => E_Strings.Empty_String);
      else
         Success := True;
         StringList.Add_To_Front (To_List  => The_Inc_Reg_Exps,
                                  The_Item => Arg);
      end if;
   end Process_Include_Switch;

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

   procedure Process_Exclude_Switch (Switch  : in     E_Strings.T;
                                     Arg     : in     E_Strings.T;
                                     Success :    out Boolean)
   --# global in     Debug;
   --#        in out SPARK_IO.File_Sys;
   --#        in out The_Exc_Reg_Exps;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Arg,
   --#                                Switch &
   --#         Success           from Arg &
   --#         The_Exc_Reg_Exps  from *,
   --#                                Arg &
   --#         null              from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found exclude switch: ",
                                            E_Text => Arg);
      end if;

      if E_Strings.Is_Empty (E_Str => Arg) or else RegularExpression.Is_Null (O => RegularExpression.Create (Arg)) then
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Invalid_Argument,
            E_Str1    => Arg,
            E_Str2    => Switch,
            E_Str3    => E_Strings.Empty_String);
      else
         Success := True;
         StringList.Add_To_Front (To_List  => The_Exc_Reg_Exps,
                                  The_Item => Arg);
      end if;
   end Process_Exclude_Switch;

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

   procedure Process_Directory_Switch (Switch  : in     E_Strings.T;
                                       Arg     : in     E_Strings.T;
                                       Success :    out Boolean)
   --# global in     Debug;
   --#        in out SPARK_IO.File_Sys;
   --#        in out The_Directories;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Arg,
   --#                                Switch &
   --#         Success           from Arg &
   --#         The_Directories   from *,
   --#                                Arg &
   --#         null              from Debug;
   is
      The_Dir : E_Strings.T;
   begin
      The_Dir := Normalise_Dir_Name (N => Arg);

      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found directory switch: ",
                                            E_Text => Arg);
         SparkMakeDebug.Report_Text_E_Text (Text   => "Normalised to: ",
                                            E_Text => The_Dir);
      end if;

      if Directory_Operations.Is_Directory (Path => The_Dir) then
         Success := True;
         -- the current directory is used by default so don't add it again
         if not E_Strings.Eq_String (E_Str1 => The_Dir,
                                     E_Str2 => Directory_Operations.Current_Directory) then
            StringList.Add_To_Back (To_List  => The_Directories,
                                    The_Item => The_Dir);
         end if;
      else
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Invalid_Argument,
            E_Str1    => The_Dir,
            E_Str2    => Switch,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Directory_Switch;

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

   procedure Process_Index_Switch (Switch  : in     E_Strings.T;
                                   Arg     : in     E_Strings.T;
                                   Success :    out Boolean)
   --# global in     Debug;
   --#        in out Index_File;
   --#        in out SPARK_IO.File_Sys;
   --# derives Index_File        from *,
   --#                                Arg &
   --#         SPARK_IO.File_Sys from *,
   --#                                Index_File,
   --#                                Switch &
   --#         Success           from Index_File &
   --#         null              from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found index switch: ",
                                            E_Text => Arg);
      end if;

      if E_Strings.Is_Empty (E_Str => Index_File) then
         Success    := True;
         Index_File := Arg;
         FileSystem.Check_Extension
           (Fn  => Index_File,
            Ext => E_Strings.Copy_String (Str => CommandLineData.Default_Index_Extension));

      else
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Duplicate_Switch,
            E_Str1    => Switch,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Index_Switch;

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

   procedure Process_Meta_Switch (Switch  : in     E_Strings.T;
                                  Arg     : in     E_Strings.T;
                                  Success :    out Boolean)
   --# global in     Debug;
   --#        in out Meta_File;
   --#        in out SPARK_IO.File_Sys;
   --# derives Meta_File         from *,
   --#                                Arg &
   --#         SPARK_IO.File_Sys from *,
   --#                                Meta_File,
   --#                                Switch &
   --#         Success           from Meta_File &
   --#         null              from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found meta switch: ",
                                            E_Text => Arg);
      end if;

      if E_Strings.Is_Empty (E_Str => Meta_File) then
         Success   := True;
         Meta_File := Arg;
         FileSystem.Check_Extension (Fn  => Meta_File,
                                     Ext => E_Strings.Copy_String (CommandLineData.Meta_File_Extension));
      else
         -- duplicate meta file switch
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Duplicate_Switch,
            E_Str1    => Switch,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Meta_Switch;

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

   procedure Process_Path_Switch (Switch  : in     E_Strings.T;
                                  Arg     : in     E_Strings.T;
                                  Success :    out Boolean)
   --# global in     Debug;
   --#        in out Path;
   --#        in out SPARK_IO.File_Sys;
   --# derives Path,
   --#         Success           from Arg,
   --#                                Path &
   --#         SPARK_IO.File_Sys from *,
   --#                                Arg,
   --#                                Path,
   --#                                Switch &
   --#         null              from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found path switch: ",
                                            E_Text => Arg);
      end if;

      if Path = Undefined then

         if E_Strings.Eq1_String (E_Str => Arg,
                                  Str   => "full")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "ful")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "fu")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "f") then
            Success := True;
            Path    := Full;
         elsif E_Strings.Eq1_String (E_Str => Arg,
                                     Str   => "relative")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "relativ")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "relati")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "relat")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "rela")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "rel")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "re")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "r") then
            Success := True;
            Path    := Relative;
         else
            Success := False;
            SparkMakeErrors.Report
              (The_Fault => SparkMakeErrors.Invalid_Argument,
               E_Str1    => Arg,
               E_Str2    => Switch,
               E_Str3    => E_Strings.Empty_String);
         end if;
      else
         -- duplicate path switch
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Duplicate_Switch,
            E_Str1    => Switch,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Path_Switch;

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

   procedure Process_Language_Switch (Switch  : in     E_Strings.T;
                                      Arg     : in     E_Strings.T;
                                      Success :    out Boolean)
   --# global in     Debug;
   --#        in out CommandLineData.Content;
   --#        in out Language_Found;
   --#        in out SPARK_IO.File_Sys;
   --# derives CommandLineData.Content,
   --#         Language_Found          from *,
   --#                                      Arg,
   --#                                      Language_Found &
   --#         SPARK_IO.File_Sys       from *,
   --#                                      Arg,
   --#                                      Language_Found,
   --#                                      Switch &
   --#         Success                 from Arg,
   --#                                      Language_Found &
   --#         null                    from Debug;
   is
   begin
      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found language switch: ",
                                            E_Text => Arg);
      end if;

      if not Language_Found then

         if E_Strings.Eq1_String (E_Str => Arg,
                                  Str   => "83") or else E_Strings.Eq1_String (E_Str => Arg,
                                                                               Str   => "8") then
            Success                                  := True;
            Language_Found                           := True;
            CommandLineData.Content.Language_Profile := CommandLineData.SPARK83;
         elsif E_Strings.Eq1_String (E_Str => Arg,
                                     Str   => "95") or else E_Strings.Eq1_String (E_Str => Arg,
                                                                                  Str   => "9") then
            Success                                  := True;
            Language_Found                           := True;
            CommandLineData.Content.Language_Profile := CommandLineData.SPARK95;
         elsif E_Strings.Eq1_String (E_Str => Arg,
                                     Str   => "2005")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "200")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "20")
           or else E_Strings.Eq1_String (E_Str => Arg,
                                         Str   => "2") then
            Success                                  := True;
            Language_Found                           := True;
            CommandLineData.Content.Language_Profile := CommandLineData.SPARK2005;
         else
            Success := False;
            SparkMakeErrors.Report
              (The_Fault => SparkMakeErrors.Invalid_Argument,
               E_Str1    => Arg,
               E_Str2    => Switch,
               E_Str3    => E_Strings.Empty_String);
         end if;
      else
         -- duplicate language switch
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Duplicate_Switch,
            E_Str1    => Switch,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Language_Switch;

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

   procedure Process_Anno_Char (Switch  : in     E_Strings.T;
                                Arg     : in     E_Strings.T;
                                Success :    out Boolean)
   --# global in     Debug;
   --#        in out Anno_Char_Specified;
   --#        in out CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Anno_Char_Specified,
   --#         CommandLineData.Content from *,
   --#                                      Anno_Char_Specified,
   --#                                      Arg &
   --#         SPARK_IO.File_Sys       from *,
   --#                                      Anno_Char_Specified,
   --#                                      Arg,
   --#                                      Switch &
   --#         Success                 from Anno_Char_Specified,
   --#                                      Arg &
   --#         null                    from Debug;
   is
   begin

      if Debug then
         SparkMakeDebug.Report_Text_E_Text (Text   => "Found annotation character argument: ",
                                            E_Text => Arg);
      end if;

      if not Anno_Char_Specified then

         if E_Strings.Get_Length (E_Str => Arg) = 1 then

            Success             := True;
            Anno_Char_Specified := True;
            -- set AnnoChar in the Examiner
            CommandLineData.Content.Anno_Char := E_Strings.Get_Element (E_Str => Arg,
                                                                        Pos   => 1);  -- expect warning here

         else
            Success := False;
            SparkMakeErrors.Report
              (The_Fault => SparkMakeErrors.Invalid_Argument,
               E_Str1    => Arg,
               E_Str2    => Switch,
               E_Str3    => E_Strings.Empty_String);
         end if;
      else
         -- duplicate annochar switch
         Success := False;
         SparkMakeErrors.Report
           (The_Fault => SparkMakeErrors.Duplicate_Switch,
            E_Str1    => Switch,
            E_Str2    => E_Strings.Empty_String,
            E_Str3    => E_Strings.Empty_String);
      end if;
   end Process_Anno_Char;

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

   procedure Process (Success           : out Boolean;
                      Help_Or_Ver_Found : out Boolean)
   --# global in     CommandLine.State;
   --#        in out CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --#           out Anno_Char_Specified;
   --#           out Debug;
   --#           out Duplicates;
   --#           out Index_File;
   --#           out Language_Found;
   --#           out Meta_File;
   --#           out No_Index;
   --#           out No_Meta;
   --#           out Path;
   --#           out Root_File;
   --#           out The_Directories;
   --#           out The_Exc_Reg_Exps;
   --#           out The_Inc_Reg_Exps;
   --# derives Anno_Char_Specified,
   --#         Debug,
   --#         Duplicates,
   --#         Help_Or_Ver_Found,
   --#         Index_File,
   --#         Language_Found,
   --#         Meta_File,
   --#         No_Index,
   --#         No_Meta,
   --#         Path,
   --#         Root_File,
   --#         Success,
   --#         The_Directories,
   --#         The_Exc_Reg_Exps,
   --#         The_Inc_Reg_Exps        from CommandLine.State &
   --#         CommandLineData.Content,
   --#         SPARK_IO.File_Sys       from *,
   --#                                      CommandLine.State;
   is
      Switch              : E_Strings.T;
      Argument            : E_Strings.T;
      The_Reg_Exp         : E_Strings.T;
      Switch_Or_Arg_Found : Boolean;
      Done                : Boolean;
      Option_OK           : Boolean;
   begin
      Path                := Undefined;
      Root_File           := E_Strings.Empty_String;
      Meta_File           := E_Strings.Empty_String;
      Index_File          := E_Strings.Empty_String;
      Anno_Char_Specified := False;
      Debug               := False;
      Duplicates          := False;
      The_Inc_Reg_Exps    := StringList.Null_Object;
      The_Exc_Reg_Exps    := StringList.Null_Object;
      Success             := True;
      Done                := False;
      Help_Or_Ver_Found   := False;
      No_Index            := False;
      No_Meta             := False;

      -- Default language profile is SPARK95
      Language_Found                           := False;
      CommandLineData.Content.Language_Profile := CommandLineData.SPARK95;

      -- Always allow FDL reserved words as identifiers. Leave it to the Examiner
      -- to reject them later if required.
      CommandLineData.Content.FDL_Reserved := False;

      -- The current directory is always assumed
      The_Directories := StringList.Null_Object;
      StringList.Add_To_Front (To_List  => The_Directories,
                               The_Item => Directory_Operations.Current_Directory);

      -- Setup the command line
      CommandLine.Setup;

      -- Read options
      while not Done and not Help_Or_Ver_Found loop

         CommandLine.Read (Switch   => Switch,
                           Argument => Argument,
                           Success  => Switch_Or_Arg_Found);

         if Switch_Or_Arg_Found then

            if E_Strings.Is_Empty (E_Str => Switch) then
               -- ARGUMENT: root file
               Process_Root_File_Argument (Arg     => Argument,
                                           Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "help")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "hel")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "he")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "h") then
               -- SWITCH: help
               Report_Usage;
               Help_Or_Ver_Found := True;
               Option_OK         := True;
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "version")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "versio")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "versi")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "vers")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "ver")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "ve")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "v") then
               -- SWITCH: version
               Report_Version;
               Help_Or_Ver_Found := True;
               Option_OK         := True;
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "language")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "languag")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "langua")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "langu")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "lang")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "lan")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "la")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "l") then
               -- SWITCH: language
               Process_Language_Switch (Switch  => Switch,
                                        Arg     => Argument,
                                        Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "path")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "pat")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "pa")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "p") then
               -- SWITCH: path
               Process_Path_Switch (Switch  => Switch,
                                    Arg     => Argument,
                                    Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "directory")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "director")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "directo")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "direct")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "direc")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "dire")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "dir")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "di") then
               -- SWITCH: directory
               Process_Directory_Switch (Switch  => Switch,
                                         Arg     => Argument,
                                         Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "include")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "includ")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "inclu")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "incl")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "inc") then
               -- SWITCH: include
               Process_Include_Switch (Switch  => Switch,
                                       Arg     => Argument,
                                       Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "exclude")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "exclud")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "exclu")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "excl")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "exc")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "ex")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "e") then
               -- SWITCH: exclude
               Process_Exclude_Switch (Switch  => Switch,
                                       Arg     => Argument,
                                       Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "meta")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "met")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "me")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "m") then
               -- SWITCH: meta
               Process_Meta_Switch (Switch  => Switch,
                                    Arg     => Argument,
                                    Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "index")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "inde")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "ind") then
               -- SWITCH: index
               Process_Index_Switch (Switch  => Switch,
                                     Arg     => Argument,
                                     Success => Option_OK);
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "noindexfile")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noindexfil")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noindexfi")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noindexf")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noindex")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noinde")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noind")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noin")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "noi") then
               -- SWITCH: noindexfile
               No_Index  := True;
               Option_OK := True;
               SparkMakeDebug.Report_Text (Text => "Found noindexfile switch");
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "nometafile")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nometafil")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nometafi")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nometaf")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nometa")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nomet")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nome")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "nom") then
               -- SWITCH: nometafile
               No_Meta   := True;
               Option_OK := True;
               SparkMakeDebug.Report_Text (Text => "Found nometafile switch");

            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "debug") then
               -- SWITCH: debug
               Debug     := True;
               Option_OK := True;
               SparkMakeDebug.Report_Text (Text => "Found debug switch");
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "duplicates_are_errors")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_error")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_erro")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_err")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_er")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_e")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are_")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_are")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_ar")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_a")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates_")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicates")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicate")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplicat")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplica")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "duplic")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "dupli")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "dupl")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "dup")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "du") then
               -- SWITCH: duplicates
               Duplicates := True;
               Option_OK  := True;
               SparkMakeDebug.Report_Text (Text => "Found duplicates switch");
            elsif E_Strings.Eq1_String (E_Str => Switch,
                                        Str   => "annotation_character")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_characte")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_charact")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_charac")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_chara")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_char")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_cha")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_ch")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_c")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation_")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotation")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotatio")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotati")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annotat")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annota")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "annot")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "anno")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "ann")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "an")
              or else E_Strings.Eq1_String (E_Str => Switch,
                                            Str   => "a") then
               Process_Anno_Char (Switch  => Switch,
                                  Arg     => Argument,
                                  Success => Option_OK);
            else
               -- unrecognised switch
               Option_OK := False;
               SparkMakeErrors.Report
                 (The_Fault => SparkMakeErrors.Invalid_Switch,
                  E_Str1    => Switch,
                  E_Str2    => E_Strings.Empty_String,
                  E_Str3    => E_Strings.Empty_String);
            end if;
            Success := Success and Option_OK;
         else
            -- nothing more on the command line.
            Done := True;
         end if;
      end loop;

      -- If usage or version info requested then don't continue with any other processing
      if Success and not Help_Or_Ver_Found then

         -- The command line has parsed OK

         -- Set non specified switches to default values.

         if Path = Undefined then
            Path := Full;
         end if;

         if E_Strings.Is_Empty (E_Str => Index_File) then
            -- Index file not specified so index file is <rootfile>.idx
            if E_Strings.Is_Empty (E_Str => Root_File) then
               Index_File := E_Strings.Copy_String (Str => "spark");
            else
               Index_File := Root_File;
            end if;
            Directory_Operations.Set_Extension
              (Path => Index_File,
               Ext  => E_Strings.Copy_String (CommandLineData.Default_Index_Extension));
            if Debug then
               SparkMakeDebug.Report_Text_E_Text (Text   => "Using default index file: ",
                                                  E_Text => Index_File);
            end if;
         end if;

         if E_Strings.Is_Empty (E_Str => Meta_File) then
            -- Meta file not specified so meta file is <rootfile>.smf
            if E_Strings.Is_Empty (E_Str => Root_File) then
               Meta_File := E_Strings.Copy_String (Str => "spark");
            else
               Meta_File := Root_File;
            end if;
            Directory_Operations.Set_Extension
              (Path => Meta_File,
               Ext  => E_Strings.Copy_String (CommandLineData.Meta_File_Extension));
            if Debug then
               SparkMakeDebug.Report_Text_E_Text (Text   => "Using default meta file: ",
                                                  E_Text => Meta_File);
            end if;
         end if;

         if StringList.Is_Null (It => StringList.Get_First (In_List => The_Inc_Reg_Exps)) then
            -- No include was specified so use the default (GNAT file naming convention).
            StringList.Add_To_Front (To_List  => The_Inc_Reg_Exps,
                                     The_Item => E_Strings.Copy_String (Str => "*\.ad[bs]"));
            if Debug then
               SparkMakeDebug.Report_Text (Text => "No include switch. Will use the GNAT naming convention");
            end if;
         end if;

         if not E_Strings.Is_Empty (E_Str => Root_File) then
            -- Make sure the root file will be included.
            The_Reg_Exp := Reg_Exp (For_File => Root_File);
            if E_Strings.Is_Empty (E_Str => The_Reg_Exp) then
               SystemErrors.Fatal_Error
                 (Sys_Err => SystemErrors.Other_Internal_Error,
                  Msg     => "Cannot generate regular expression for root file.");
            else
               SparkMakeDebug.Report_Text_E_Text (Text   => "Root file will be included by ",
                                                  E_Text => The_Reg_Exp);
               StringList.Add_To_Front (To_List  => The_Inc_Reg_Exps,
                                        The_Item => The_Reg_Exp);
            end if;
         end if;

      end if;
   end Process;

   function Path_Required return Path_Type
   --# global in Path;
   is
   begin
      return Path;
   end Path_Required;

   function Meta_Filename return  E_Strings.T
   --# global in Meta_File;
   is
   begin
      return Meta_File;
   end Meta_Filename;

   function Index_Filename return  E_Strings.T
   --# global in Index_File;
   is
   begin
      return Index_File;
   end Index_Filename;

   function Root_Filename return  E_Strings.T
   --# global in Root_File;
   is
   begin
      return Root_File;
   end Root_Filename;

   function Duplicates_Error return Boolean
   --# global in Duplicates;
   is
   begin
      return Duplicates;
   end Duplicates_Error;

   function Debug_On return Boolean
   --# global in Debug;
   is
   begin
      return Debug;
   end Debug_On;

   function No_Index_File return Boolean
   --# global in No_Index;
   is
   begin
      return No_Index;
   end No_Index_File;

   function No_Meta_File return Boolean
   --# global in No_Meta;
   is
   begin
      return No_Meta;
   end No_Meta_File;

   function The_Directory_Names return  StringList.Object
   --# global in The_Directories;
   is
   begin
      return The_Directories;
   end The_Directory_Names;

   function The_Inc_File_Reg_Exps return  StringList.Object
   --# global in The_Inc_Reg_Exps;
   is
   begin
      return The_Inc_Reg_Exps;
   end The_Inc_File_Reg_Exps;

   function The_Exc_File_Reg_Exps return  StringList.Object
   --# global in The_Exc_Reg_Exps;
   is
   begin
      return The_Exc_Reg_Exps;
   end The_Exc_File_Reg_Exps;

end SparkMakeCommandLine;
