-------------------------------------------------------------------------------
-- (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 CommandLineData;
with CommandLineHandler;
with ExaminerConstants;
with E_Strings;
with FileSystem;
with ScreenEcho;
with SparkFormatCommandLineData;

use type CommandLineHandler.S_Typs;

package body SparkFormatCommandLineHandler is

   procedure Process is

      type Command_Line_Errors is (
                                   ES_InvalidOption,
                                   ES_NoComma,
                                   ES_Source,
                                   EW_TooMany,
                                   ES_Anno,
                                   ES_Contradict,
                                   ES_Duplicate,
                                   ES_Indent,
                                   ES_Default,
                                   ES_InvExclude);

      Command_String : CommandLineHandler.Command_Strings;

      procedure Read_Command_Line (Command_String : out CommandLineHandler.Command_Strings)
      --# global in out SPARK_IO.File_Sys;
      --# derives Command_String,
      --#         SPARK_IO.File_Sys from SPARK_IO.File_Sys;
      is
         Cmd_Line_Found         : Boolean;
         Command_String_Content : E_Strings.T;
      begin
         --# accept F, 10, Cmd_Line_Found, "We don't care if we found anything or not";
         FileSystem.Read_Cmd_Line (Cmd_Line_Found => Cmd_Line_Found,
                                   Cmd_Line       => Command_String_Content);
         --# end accept;

         Command_String := CommandLineHandler.Command_Strings'(Current_Position => 1,
                                                               Contents         => Command_String_Content);

         --# accept F, 33, Cmd_Line_Found, "And we don't tell anyone about it either";
      end Read_Command_Line;

      procedure Output_Error (E : in Command_Line_Errors)
      --# global in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                E;
      is
      begin
         case E is
            when ES_InvalidOption =>
               ScreenEcho.Put_String ("Invalid command line option");
            when ES_Source =>
               ScreenEcho.Put_String ("Source file incorrectly specified");
            when ES_NoComma =>
               ScreenEcho.Put_String ("Comma missing in line");
            when EW_TooMany =>
               ScreenEcho.Put_String ("Too many source files on command line ");
            when ES_Anno =>
               ScreenEcho.Put_String ("Annotation character option incorrect");
            when ES_Contradict =>
               ScreenEcho.Put_String ("Command line option duplicated or contradictory options specified");
            when ES_Duplicate =>
               ScreenEcho.Put_String ("Command line options may only be specified once");
            when ES_Indent =>
               ScreenEcho.Put_String ("Indentation option incorrect");
            when ES_Default =>
               ScreenEcho.Put_String ("Default function mode option incorrect");
            when ES_InvExclude =>
               ScreenEcho.Put_String ("Invalid syntax for excluded export");
         end case;
      end Output_Error;

      procedure Possible_Error (E : in Command_Line_Errors)
      --# global in     SparkFormatCommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                E,
      --#                                SparkFormatCommandLineData.Content;
      is
      begin
         if not SparkFormatCommandLineData.Content.Valid then
            Output_Error (E => E);
            ScreenEcho.New_Line (1);
         end if;
      end Possible_Error;

      procedure Possible_Error2 (E : in Command_Line_Errors;
                                 F : in E_Strings.T)
      --# global in     SparkFormatCommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                E,
      --#                                F,
      --#                                SparkFormatCommandLineData.Content;
      is
      begin
         if not SparkFormatCommandLineData.Content.Valid then
            Output_Error (E => E);
            ScreenEcho.Put_Char (' ');
            ScreenEcho.Put_ExaminerLine (F);
         end if;
      end Possible_Error2;

      procedure Parse_Command_Line (Command_String : in CommandLineHandler.Command_Strings)
      --# global in out CommandLineData.Content;
      --#        in out SparkFormatCommandLineData.Content;
      --#        in out SPARK_IO.File_Sys;
      --# derives CommandLineData.Content,
      --#         SparkFormatCommandLineData.Content,
      --#         SPARK_IO.File_Sys                  from *,
      --#                                                 Command_String,
      --#                                                 SparkFormatCommandLineData.Content;
      is

         Next_Symbol          : CommandLineHandler.Symbols;
         Local_Command_String : CommandLineHandler.Command_Strings;

         procedure Get_Next_Symbol
           (Command_String : in out CommandLineHandler.Command_Strings;
            Next_Symbol    :    out CommandLineHandler.Symbols)
         --# derives Command_String,
         --#         Next_Symbol    from Command_String;
         is
         begin
            -- This procedure is intended to return Next_Symbol; however, if the
            -- symbol is not a string then the string field is not set.  Although
            -- it is not used in these circcumstances its lack of definition
            -- causes so many flow errors its is better to use an aggregate to
            -- initialize Next_Symbol here, and then assign the final value
            -- to Next_Symbol (for compatibility with the Ada83 "out parameter" rule
            Next_Symbol := CommandLineHandler.Symbols'(Typ        => CommandLineHandler.S_Empty,
                                                       The_String => E_Strings.Empty_String);
            CommandLineHandler.Skip_Spaces (Command_String => Command_String);
            if Command_String.Current_Position <= E_Strings.Get_Length (E_Str => Command_String.Contents) then
               case E_Strings.Get_Element (E_Str => Command_String.Contents,
                                           Pos   => Command_String.Current_Position) is
                  when '=' =>
                     Next_Symbol.Typ                 := CommandLineHandler.S_Equal;
                     Command_String.Current_Position := Command_String.Current_Position + 1;
                  when ',' =>
                     --this condition is invariant for any particular system, we are actually
                     --simulating conditional compilation for different target platforms.
                     --Intended behaviour is correct despite flow error that will result.
                     --# accept Flow_Message, 22, "Simulation of conditional compilation";
                     if FileSystem.Use_Unix_Command_Line then
                        --# end accept;
                        Next_Symbol.Typ := CommandLineHandler.S_String;
                        CommandLineHandler.Read_The_String (Command_String => Command_String,
                                                            Next_Symbol    => Next_Symbol);
                     else -- FileSystem.UseWindowsCommandLine assumed
                        Next_Symbol.Typ                 := CommandLineHandler.S_Comma;
                        Command_String.Current_Position := Command_String.Current_Position + 1;
                     end if;
                  when '-' =>
                     -- We use '-' as a switch character on all platforms
                     Next_Symbol.Typ                 := CommandLineHandler.S_Switch_Character;
                     Command_String.Current_Position := Command_String.Current_Position + 1;
                  when others =>
                     Next_Symbol.Typ := CommandLineHandler.S_String;
                     CommandLineHandler.Read_The_String (Command_String => Command_String,
                                                         Next_Symbol    => Next_Symbol);
               end case;
            else
               -- Exceeded maximum command line length
               Next_Symbol.Typ := CommandLineHandler.S_Empty;
            end if;
         end Get_Next_Symbol;

         procedure Parse_Command_Options
           (Command_String : in out CommandLineHandler.Command_Strings;
            Next_Symbol    : in out CommandLineHandler.Symbols)
         --# global in out CommandLineData.Content;
         --#        in out SparkFormatCommandLineData.Content;
         --#        in out SPARK_IO.File_Sys;
         --# derives CommandLineData.Content,
         --#         Command_String,
         --#         Next_Symbol,
         --#         SparkFormatCommandLineData.Content,
         --#         SPARK_IO.File_Sys                  from *,
         --#                                                 Command_String,
         --#                                                 Next_Symbol,
         --#                                                 SparkFormatCommandLineData.Content;
         is
            Annotation_Character_Found  : Boolean;
            Compress_Found              : Boolean;
            Expand_Found                : Boolean;
            Add_Modes_Found             : Boolean;
            Global_Indent_Found         : Boolean;
            Export_Indent_Found         : Boolean;
            Import_Indent_Found         : Boolean;
            Inherit_Indent_Found        : Boolean;
            Own_Indent_Found            : Boolean;
            Refinement_Indent_Found     : Boolean;
            Constituent_Indent_Found    : Boolean;
            Initialization_Indent_Found : Boolean;
            Separator_Indent_Found      : Boolean;
            Properties_Indent_Found     : Boolean;
            Alphabetic_Ordering_Found   : Boolean;
            Help_Found                  : Boolean;
            Version_Found               : Boolean;
            Default_Function_Mode_Found : Boolean;
            Exclude_Export_Found        : Boolean;

            Opt_Name    : E_Strings.T;
            Opt_Val     : E_Strings.T;
            Opt_Name_OK : Boolean;
            Opt_Val_OK  : Boolean;
            OK          : Boolean;

            Indent_Val : Integer;

            -- reads in the option name and the value assigned to it
            procedure Read_Option
              (Opt_Name       :    out E_Strings.T;
               Opt_Name_OK    :    out Boolean;
               Opt_Val        :    out E_Strings.T;
               Opt_Val_OK     :    out Boolean;
               Command_String : in out CommandLineHandler.Command_Strings;
               Next_Symbol    :    out CommandLineHandler.Symbols)
            --# derives Command_String,
            --#         Next_Symbol,
            --#         Opt_Name,
            --#         Opt_Name_OK,
            --#         Opt_Val,
            --#         Opt_Val_OK     from Command_String;
            --  pre  Next_Symbol.Typ = CommandLineHandler.SSlash;
            is
            begin
               Opt_Val := E_Strings.Empty_String;
               Get_Next_Symbol (Command_String => Command_String,
                                Next_Symbol    => Next_Symbol);
               Opt_Name_OK := Next_Symbol.Typ = CommandLineHandler.S_String;
               Opt_Name    := Next_Symbol.The_String;
               Get_Next_Symbol (Command_String => Command_String,
                                Next_Symbol    => Next_Symbol);
               if Opt_Name_OK and Next_Symbol.Typ = CommandLineHandler.S_Equal then
                  Get_Next_Symbol (Command_String => Command_String,
                                   Next_Symbol    => Next_Symbol);
                  Opt_Val_OK := Next_Symbol.Typ = CommandLineHandler.S_String;
                  Opt_Val    := Next_Symbol.The_String;
                  Get_Next_Symbol (Command_String => Command_String,
                                   Next_Symbol    => Next_Symbol);
               else
                  Opt_Val_OK := False;
               end if;
            end Read_Option;

            -- converts an ExaminerString to a Natural. S.Content(1) is
            -- assumes to be a member of '0' .. '9'
            procedure Examiner_String_To_Natural (S  : in     E_Strings.T;
                                                  I  :    out Natural;
                                                  OK :    out Boolean)
            --# derives I,
            --#         OK from S;
            is
               Stop : Natural;
            begin
               if E_Strings.Get_Length (E_Str => S) >= 1 then
                  E_Strings.Get_Int_From_String (Source   => S,
                                                 Item     => I,
                                                 Start_Pt => 1,
                                                 Stop     => Stop);
                  if Stop = E_Strings.Get_Length (E_Str => S) then
                     OK := True;
                  else
                     I  := 0;
                     OK := False;
                  end if;
               else
                  OK := False;
                  I  := 0;
               end if;
            end Examiner_String_To_Natural;

            procedure Parse_Indent_Option
              (Opt_Value    : in     E_Strings.T;
               Opt_Value_OK : in     Boolean;
               Indent_Value :    out Natural;
               OK           :    out Boolean)
            --# derives Indent_Value,
            --#         OK           from Opt_Value,
            --#                           Opt_Value_OK;
            is
            begin
               if Opt_Value_OK and then E_Strings.Get_Length (E_Str => Opt_Value) >= 1 then
                  case E_Strings.Get_Element (E_Str => Opt_Value,
                                              Pos   => 1) is
                     -- if option is set to "inline"
                     when 'i' =>
                        OK           := CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Value,
                                                                              Str      => "inline");
                        Indent_Value := SparkFormatCommandLineData.Inline;
                        -- check if valid number
                     when '1' .. '9' =>
                        Examiner_String_To_Natural (S  => Opt_Value,
                                                    I  => Indent_Value,
                                                    OK => OK);
                     when others =>
                        OK := False;
                        -- assign default value
                        Indent_Value := SparkFormatCommandLineData.Inline;
                  end case;
               else
                  Indent_Value := SparkFormatCommandLineData.Inline;
                  OK           := False;
               end if;
            end Parse_Indent_Option;

            --  CFR 1753: A crude syntax check for a dotted simple name as
            --  required by the /exclude_export.
            --  The name must include at least one dot.
            function Check_For_Dotted_Name (S : E_Strings.T) return Boolean is
               type Symb_T is (Alpha, Digit, Dot, Under);
               Last_Symb : Symb_T;
               Dot_Found : Boolean;
               Error     : Boolean;
            begin
               Dot_Found := False;
               Error     := False;
               if E_Strings.Get_Length (E_Str => S) > 2 then  -- We require at least 3 chars, i.e., X.Y
                  if E_Strings.Get_Element (E_Str => S,
                                            Pos   => 1) in 'A' .. 'Z'
                    or else E_Strings.Get_Element (E_Str => S,
                                                   Pos   => 1) in 'a' .. 'z' then
                     Last_Symb := Alpha;
                     for I in E_Strings.Positions range 2 .. E_Strings.Get_Length (E_Str => S) loop
                        case E_Strings.Get_Element (E_Str => S,
                                                    Pos   => I) is
                           when '.' =>
                              if Last_Symb /= Dot and Last_Symb /= Under and E_Strings.Get_Length (E_Str => S) > I then
                                 Dot_Found := True;
                                 Last_Symb := Dot;
                              else
                                 Error := True;
                              end if;
                           when 'A' .. 'Z' | 'a' .. 'z' =>
                              Last_Symb := Alpha;
                           when '0' .. '9' =>
                              if Last_Symb /= Dot and Last_Symb /= Under then
                                 Last_Symb := Digit;
                              else
                                 Error := True;
                              end if;
                           when '_' =>
                              if Last_Symb /= Dot and Last_Symb /= Under and E_Strings.Get_Length (E_Str => S) > I then
                                 Last_Symb := Under;
                              else
                                 Error := True;
                              end if;
                           when others =>
                              Error := True;
                        end case;

                        exit when Error;
                     end loop;
                  end if;
               end if;

               return Dot_Found and not Error;
            end Check_For_Dotted_Name;

         begin -- Parse_Command_Options

            Annotation_Character_Found  := False;
            Compress_Found              := False;
            Expand_Found                := False;
            Add_Modes_Found             := False;
            Global_Indent_Found         := False;
            Export_Indent_Found         := False;
            Import_Indent_Found         := False;
            Inherit_Indent_Found        := False;
            Own_Indent_Found            := False;
            Refinement_Indent_Found     := False;
            Constituent_Indent_Found    := False;
            Initialization_Indent_Found := False;
            Separator_Indent_Found      := False;
            Properties_Indent_Found     := False;
            Alphabetic_Ordering_Found   := False;
            Help_Found                  := False;
            Version_Found               := False;
            Default_Function_Mode_Found := False;
            Exclude_Export_Found        := False;

            loop
               exit when Next_Symbol.Typ /= CommandLineHandler.S_Switch_Character;
               Read_Option
                 (Opt_Name       => Opt_Name,
                  Opt_Name_OK    => Opt_Name_OK,
                  Opt_Val        => Opt_Val,
                  Opt_Val_OK     => Opt_Val_OK,
                  Command_String => Command_String,
                  Next_Symbol    => Next_Symbol);
               if Opt_Name_OK then
                  case E_Strings.Get_Element (E_Str => Opt_Name,
                                              Pos   => 1) is
                     when 'a' | 'A' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 2) is
                           when 'd' | 'D' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "add_modes") then
                                 SparkFormatCommandLineData.Content.Valid := not Add_Modes_Found;
                                 Possible_Error (E => ES_Contradict);
                                 Add_Modes_Found                              := True;
                                 SparkFormatCommandLineData.Content.Add_Modes := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'n' | 'N' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "annotation_character") then
                                 SparkFormatCommandLineData.Content.Valid := Opt_Val_OK
                                   and then E_Strings.Get_Length (E_Str => Opt_Val) = 1
                                   and then not Annotation_Character_Found;
                                 Possible_Error (E => ES_Anno);
                                 Annotation_Character_Found        := True;
                                 CommandLineData.Content.Anno_Char := E_Strings.Get_Element (E_Str => Opt_Val,
                                                                                             Pos   => 1);
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'c' | 'C' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 3) is
                           when 'm' | 'M' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "compress") then
                                 SparkFormatCommandLineData.Content.Valid := not Compress_Found;
                                 Possible_Error (E => ES_Duplicate);
                                 Compress_Found                               := True;
                                 SparkFormatCommandLineData.Content.Operation := SparkFormatCommandLineData.Compress;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'n' | 'N' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "constituent_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Constituent_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid              := True;
                                    SparkFormatCommandLineData.Content.Constituent_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Constituent_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'd' | 'D' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "default_function_modes") then
                           SparkFormatCommandLineData.Content.Valid := Opt_Val_OK and then not Default_Function_Mode_Found;
                           Possible_Error (E => ES_Default);
                           if SparkFormatCommandLineData.Content.Valid then -- go on to check selection
                              Default_Function_Mode_Found := True;
                              case E_Strings.Get_Element (E_Str => Opt_Val,
                                                          Pos   => 1) is
                                 when 'i' | 'I' =>
                                    if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Val,
                                                                             Str      => "in_mode") then
                                       SparkFormatCommandLineData.Content.Default_Function_Mode := SparkFormatCommandLineData.In_Mode;
                                    else
                                       SparkFormatCommandLineData.Content.Valid := False;
                                       Possible_Error (E => ES_Default);
                                    end if;
                                 when 'u' | 'U' =>
                                    if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Val,
                                                                             Str      => "unmoded") then
                                       SparkFormatCommandLineData.Content.Default_Function_Mode := SparkFormatCommandLineData.Unmoded;
                                    else
                                       SparkFormatCommandLineData.Content.Valid := False;
                                       Possible_Error (E => ES_Default);
                                    end if;
                                 when others =>
                                    SparkFormatCommandLineData.Content.Valid := False;
                                    Possible_Error (E => ES_Default);
                              end case;
                           end if;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 'e' | 'E' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 4) is
                           when 'a' | 'A' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "expand") then
                                 SparkFormatCommandLineData.Content.Valid := not Expand_Found;
                                 Possible_Error (E => ES_Duplicate);
                                 Expand_Found                                 := True;
                                 SparkFormatCommandLineData.Content.Operation := SparkFormatCommandLineData.Expand;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'l' | 'L' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "exclude_export") then
                                 if not Exclude_Export_Found then
                                    if Opt_Val_OK and then Check_For_Dotted_Name (S => Opt_Val) then
                                       SparkFormatCommandLineData.Content.Valid          := True;
                                       SparkFormatCommandLineData.Content.Exclude_Export := Opt_Val;
                                    else
                                       SparkFormatCommandLineData.Content.Valid := False;
                                       Possible_Error (E => ES_InvExclude);
                                    end if;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                    Possible_Error (E => ES_Duplicate);
                                 end if;
                                 Exclude_Export_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'o' | 'O' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "export_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Export_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid         := True;
                                    SparkFormatCommandLineData.Content.Export_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Export_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'g' | 'G' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "global_indent") then
                           Parse_Indent_Option
                             (Opt_Value    => Opt_Val,
                              Opt_Value_OK => Opt_Val_OK,
                              Indent_Value => Indent_Val,
                              OK           => OK);
                           if OK and then not Global_Indent_Found then
                              SparkFormatCommandLineData.Content.Valid         := True;
                              SparkFormatCommandLineData.Content.Global_Indent := Indent_Val;
                           else
                              SparkFormatCommandLineData.Content.Valid := False;
                           end if;
                           Possible_Error (E => ES_Indent);
                           Global_Indent_Found := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 'h' | 'H' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "help") then
                           SparkFormatCommandLineData.Content.Valid := not Help_Found;
                           Possible_Error (E => ES_Duplicate);
                           Help_Found                              := True;
                           SparkFormatCommandLineData.Content.Help := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 'i' | 'I' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 3) is
                           when 'h' | 'H' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "inherit_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Inherit_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid          := True;
                                    SparkFormatCommandLineData.Content.Inherit_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Inherit_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'i' | 'I' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "initialization_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Initialization_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid                 := True;
                                    SparkFormatCommandLineData.Content.Initialization_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Initialization_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'p' | 'P' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "import_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Import_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid         := True;
                                    SparkFormatCommandLineData.Content.Import_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Import_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'n' | 'N' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 3) is
                           when 'a' | 'A' => -- 3rd letter
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "noadd_modes") then
                                 SparkFormatCommandLineData.Content.Valid := not Add_Modes_Found;
                                 Possible_Error (E => ES_Contradict);
                                 Add_Modes_Found                              := True;
                                 SparkFormatCommandLineData.Content.Add_Modes := False;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 's' | 'W' => -- 3rd letter
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "noswitch") then
                                 null; -- already dealt with, so ignore here
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'o' | 'O' =>
                        case E_Strings.Get_Element (E_Str => Opt_Name,
                                                    Pos   => 2) is
                           when 'r' | 'R' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "order") then
                                 if Opt_Val_OK then
                                    case E_Strings.Get_Element (E_Str => Opt_Val,
                                                                Pos   => 1) is
                                       when 'a' | 'A' =>
                                          if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Val,
                                                                                   Str      => "alphabetic") then
                                             SparkFormatCommandLineData.Content.Valid := not Alphabetic_Ordering_Found;
                                             Possible_Error (E => ES_Contradict);
                                             Alphabetic_Ordering_Found := True;
                                          else
                                             SparkFormatCommandLineData.Content.Valid := False;
                                             Possible_Error2 (E => ES_InvalidOption,
                                                              F => Opt_Val);
                                          end if;
                                       when others =>
                                          SparkFormatCommandLineData.Content.Valid := False;
                                          Possible_Error2 (E => ES_InvalidOption,
                                                           F => Opt_Val);
                                    end case;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                    Possible_Error2 (E => ES_InvalidOption,
                                                     F => Opt_Val);
                                 end if;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when 'w' | 'W' =>
                              if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                       Str      => "own_indent") then
                                 Parse_Indent_Option
                                   (Opt_Value    => Opt_Val,
                                    Opt_Value_OK => Opt_Val_OK,
                                    Indent_Value => Indent_Val,
                                    OK           => OK);
                                 if OK and then not Own_Indent_Found then
                                    SparkFormatCommandLineData.Content.Valid      := True;
                                    SparkFormatCommandLineData.Content.Own_Indent := Indent_Val;
                                 else
                                    SparkFormatCommandLineData.Content.Valid := False;
                                 end if;
                                 Possible_Error (E => ES_Indent);
                                 Own_Indent_Found := True;
                              else
                                 SparkFormatCommandLineData.Content.Valid := False;
                                 Possible_Error2 (E => ES_InvalidOption,
                                                  F => Opt_Name);
                              end if;
                           when others =>
                              SparkFormatCommandLineData.Content.Valid := False;
                              Possible_Error2 (E => ES_InvalidOption,
                                               F => Opt_Name);
                        end case;
                     when 'p' | 'P' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "properties_indent") then
                           Parse_Indent_Option
                             (Opt_Value    => Opt_Val,
                              Opt_Value_OK => Opt_Val_OK,
                              Indent_Value => Indent_Val,
                              OK           => OK);
                           if OK and then not Properties_Indent_Found then
                              SparkFormatCommandLineData.Content.Valid             := True;
                              SparkFormatCommandLineData.Content.Properties_Indent := Indent_Val;
                           else
                              SparkFormatCommandLineData.Content.Valid := False;
                           end if;
                           Possible_Error (E => ES_Indent);
                           Refinement_Indent_Found := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 'r' | 'R' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "refinement_indent") then
                           Parse_Indent_Option
                             (Opt_Value    => Opt_Val,
                              Opt_Value_OK => Opt_Val_OK,
                              Indent_Value => Indent_Val,
                              OK           => OK);
                           if OK and then not Refinement_Indent_Found then
                              SparkFormatCommandLineData.Content.Valid             := True;
                              SparkFormatCommandLineData.Content.Refinement_Indent := Indent_Val;
                           else
                              SparkFormatCommandLineData.Content.Valid := False;
                           end if;
                           Possible_Error (E => ES_Indent);
                           Refinement_Indent_Found := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 's' | 'S' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "separator_indent") then
                           Parse_Indent_Option
                             (Opt_Value    => Opt_Val,
                              Opt_Value_OK => Opt_Val_OK,
                              Indent_Value => Indent_Val,
                              OK           => OK);
                           if OK and then not Separator_Indent_Found then
                              SparkFormatCommandLineData.Content.Valid            := True;
                              SparkFormatCommandLineData.Content.Separator_Indent := Indent_Val;
                           else
                              SparkFormatCommandLineData.Content.Valid := False;
                           end if;
                           Possible_Error (E => ES_Indent);
                           Separator_Indent_Found := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when 'v' | 'V' =>
                        if CommandLineHandler.Check_Option_Name (Opt_Name => Opt_Name,
                                                                 Str      => "version") then
                           SparkFormatCommandLineData.Content.Valid := not Version_Found;
                           Possible_Error (E => ES_Duplicate);
                           Version_Found                              := True;
                           SparkFormatCommandLineData.Content.Version := True;
                        else
                           SparkFormatCommandLineData.Content.Valid := False;
                           Possible_Error2 (E => ES_InvalidOption,
                                            F => Opt_Name);
                        end if;
                     when others =>
                        SparkFormatCommandLineData.Content.Valid := False;
                        Possible_Error2 (E => ES_InvalidOption,
                                         F => Opt_Name);
                  end case;
               else
                  SparkFormatCommandLineData.Content.Valid := False;
                  Possible_Error2 (E => ES_InvalidOption,
                                   F => Opt_Name);
               end if;
               exit when not SparkFormatCommandLineData.Content.Valid;
            end loop;
         end Parse_Command_Options;

         procedure ParseArguments
           (Command_String : in CommandLineHandler.Command_Strings;
            Next_Symbol    : in CommandLineHandler.Symbols)
         --# global in out SparkFormatCommandLineData.Content;
         --#        in out SPARK_IO.File_Sys;
         --# derives SparkFormatCommandLineData.Content,
         --#         SPARK_IO.File_Sys                  from *,
         --#                                                 Command_String,
         --#                                                 Next_Symbol,
         --#                                                 SparkFormatCommandLineData.Content;
         --  pre Next_Symbol.Typ in {SString, SEmpty};
         is

            Local_Next_Symbol    : CommandLineHandler.Symbols;
            Local_Command_String : CommandLineHandler.Command_Strings;

            procedure ParseFileEntry
              (Command_String : in out CommandLineHandler.Command_Strings;
               Next_Symbol    : in out CommandLineHandler.Symbols)
            --# global in out SparkFormatCommandLineData.Content;
            --#        in out SPARK_IO.File_Sys;
            --# derives Command_String,
            --#         SparkFormatCommandLineData.Content from *,
            --#                                                 Next_Symbol &
            --#         Next_Symbol                        from *,
            --#                                                 Command_String &
            --#         SPARK_IO.File_Sys                  from *,
            --#                                                 Next_Symbol,
            --#                                                 SparkFormatCommandLineData.Content;
            --  pre  Next_Symbol.Typ = CommandLineHandler.SString;
            --  post Next_Symbol.Typ in {CommandLineHandler.SComma, CommandLineHandler.SEmpty};
            is
               FileName : E_Strings.T;
            begin
               case Next_Symbol.Typ is
                  when CommandLineHandler.S_String =>
                     SparkFormatCommandLineData.Content.Number_Source := SparkFormatCommandLineData.Content.Number_Source + 1;

                     FileName := Next_Symbol.The_String;

                     FileSystem.Check_Extension (Fn  => FileName,
                                                 Ext => SparkFormatCommandLineData.Content.Source_Extension);
                     SparkFormatCommandLineData.Content.Source_File_List (SparkFormatCommandLineData.Content.Number_Source).
                       Source_Filename := FileName;
                     Get_Next_Symbol (Command_String => Command_String,
                                      Next_Symbol    => Next_Symbol);
                     --  end if;
                  when CommandLineHandler.S_Empty =>
                     null;
                  when others =>
                     SparkFormatCommandLineData.Content.Valid := False;
                     Possible_Error (E => ES_Source);
               end case;
            end ParseFileEntry;

         begin
            Local_Next_Symbol                                := Next_Symbol;
            Local_Command_String                             := Command_String;
            SparkFormatCommandLineData.Content.Number_Source := 0;
            loop
               ParseFileEntry (Local_Command_String, Local_Next_Symbol);

               exit when Local_Next_Symbol.Typ = CommandLineHandler.S_Empty or not SparkFormatCommandLineData.Content.Valid;
               if SparkFormatCommandLineData.Content.Number_Source = ExaminerConstants.MaxFilesOnCommandLine then
                  SparkFormatCommandLineData.Content.Valid := False;
                  Output_Error (E => EW_TooMany);
                  exit;
               end if;
               if FileSystem.Use_Windows_Command_Line and Local_Next_Symbol.Typ = CommandLineHandler.S_Comma then
                  -- CFR 1824: Allow comma or space as separator on Windows
                  Get_Next_Symbol (Command_String => Local_Command_String,
                                   Next_Symbol    => Local_Next_Symbol);
               end if;
            end loop;
         end ParseArguments;

      begin -- Parse_Command_Line;
         Local_Command_String := Command_String;
         Get_Next_Symbol (Command_String => Local_Command_String,
                          Next_Symbol    => Next_Symbol);
         Parse_Command_Options (Command_String => Local_Command_String,
                                Next_Symbol    => Next_Symbol);
         if SparkFormatCommandLineData.Content.Valid then
            ParseArguments (Local_Command_String, Next_Symbol);
         end if;
      end Parse_Command_Line;

   begin -- Process
      CommandLineHandler.Process_Defaults_From_Switch_File;
      Read_Command_Line (Command_String => Command_String);
      Parse_Command_Line (Command_String => Command_String);
   end Process;

end SparkFormatCommandLineHandler;
