gnatcmd.adb   [plain text]


------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                              G N A T C M D                               --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                                                                          --
--          Copyright (C) 1996-2002 Free Software Foundation, Inc.          --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 GNAT;  see file COPYING.  If not, write --
-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with GNAT.Directory_Operations; use GNAT.Directory_Operations;

with Csets;
with MLib.Tgt;
with MLib.Utl;
with Namet;    use Namet;
with Opt;
with Osint;    use Osint;
with Output;
with Prj;      use Prj;
with Prj.Env;
with Prj.Ext;  use Prj.Ext;
with Prj.Pars;
with Prj.Util; use Prj.Util;
with Sdefault; use Sdefault;
with Snames;   use Snames;
with Stringt;  use Stringt;
with Table;
with Types;    use Types;
with Hostparm; use Hostparm;
--  Used to determine if we are in VMS or not for error message purposes

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

with Gnatvsn;
with GNAT.OS_Lib;             use GNAT.OS_Lib;

with Table;

procedure GNATCmd is

   Ada_Include_Path : constant String := "ADA_INCLUDE_PATH";
   Ada_Objects_Path : constant String := "ADA_OBJECTS_PATH";

   Project_File      : String_Access;
   Project           : Prj.Project_Id;
   Current_Verbosity : Prj.Verbosity := Prj.Default;
   Tool_Package_Name : Name_Id       := No_Name;

   --  This flag indicates a switch -p (for gnatxref and gnatfind) for
   --  an old fashioned project file. -p cannot be used in conjonction
   --  with -P.

   Old_Project_File_Used : Boolean := False;

   --  A table to keep the switches on the command line

   package Last_Switches is new Table.Table
     (Table_Component_Type => String_Access,
      Table_Index_Type     => Integer,
      Table_Low_Bound      => 1,
      Table_Initial        => 20,
      Table_Increment      => 100,
      Table_Name           => "Gnatcmd.Last_Switches");

   --  A table to keep the switches from the project file

   package First_Switches is new Table.Table
     (Table_Component_Type => String_Access,
      Table_Index_Type     => Integer,
      Table_Low_Bound      => 1,
      Table_Initial        => 20,
      Table_Increment      => 100,
      Table_Name           => "Gnatcmd.First_Switches");

   ------------------
   -- SWITCH TABLE --
   ------------------

   --  The switch tables contain an entry for each switch recognized by the
   --  command processor. The syntax of entries is as follows:

   --    SWITCH_STRING ::= "/ command-qualifier-name TRANSLATION"

   --    TRANSLATION ::=
   --      DIRECT_TRANSLATION
   --    | DIRECTORIES_TRANSLATION
   --    | FILE_TRANSLATION
   --    | NO_SPACE_FILE_TRANSL
   --    | NUMERIC_TRANSLATION
   --    | STRING_TRANSLATION
   --    | OPTIONS_TRANSLATION
   --    | COMMANDS_TRANSLATION
   --    | ALPHANUMPLUS_TRANSLATION
   --    | OTHER_TRANSLATION

   --    DIRECT_TRANSLATION       ::= space UNIX_SWITCHES
   --    DIRECTORIES_TRANSLATION  ::= =* UNIX_SWITCH *
   --    DIRECTORY_TRANSLATION    ::= =% UNIX_SWITCH %
   --    FILE_TRANSLATION         ::= =@ UNIX_SWITCH @
   --    NO_SPACE_FILE_TRANSL     ::= =< UNIX_SWITCH >
   --    NUMERIC_TRANSLATION      ::= =# UNIX_SWITCH # | # number #
   --    STRING_TRANSLATION       ::= =" UNIX_SWITCH "
   --    OPTIONS_TRANSLATION      ::= =OPTION {space OPTION}
   --    COMMANDS_TRANSLATION     ::= =? ARGS space command-name
   --    ALPHANUMPLUS_TRANSLATION ::= =| UNIX_SWITCH |

   --    UNIX_SWITCHES ::= UNIX_SWITCH {, UNIX_SWITCH}

   --    UNIX_SWITCH ::= unix-switch-string | !unix-switch-string | `string'

   --    OPTION ::= option-name space UNIX_SWITCHES

   --    ARGS ::= -cargs | -bargs | -largs

   --  Here command-qual is the name of the switch recognized by the GNATCmd.
   --  This is always given in upper case in the templates, although in the
   --  actual commands, either upper or lower case is allowed.

   --  The unix-switch-string always starts with a minus, and has no commas
   --  or spaces in it. Case is significant in the unix switch string. If a
   --  unix switch string is preceded by the not sign (!) it means that the
   --  effect of the corresponding command qualifer is to remove any previous
   --  occurrence of the given switch in the command line.

   --  The DIRECTORIES_TRANSLATION format is used where a list of directories
   --  is given. This possible corresponding formats recognized by GNATCmd are
   --  as shown by the following example for the case of PATH

   --    PATH=direc
   --    PATH=(direc,direc,direc,direc)

   --  When more than one directory is present for the DIRECTORIES case, then
   --  multiple instances of the corresponding unix switch are generated,
   --  with the file name being substituted for the occurrence of *.

   --  The FILE_TRANSLATION format is similar except that only a single
   --  file is allowed, not a list of files, and only one unix switch is
   --  generated as a result.

   --  the NO_SPACE_FILE_TRANSL is similar to FILE_TRANSLATION, except that
   --  no space is inserted between the switch and the file name.

   --  The NUMERIC_TRANSLATION format is similar to the FILE_TRANSLATION case
   --  except that the parameter is a decimal integer in the range 0 to 999.

   --  For the OPTIONS_TRANSLATION case, GNATCmd similarly permits one or
   --  more options to appear (although only in some cases does the use of
   --  multiple options make logical sense). For example, taking the
   --  case of ERRORS for GCC, the following are all allowed:

   --    /ERRORS=BRIEF
   --    /ERRORS=(FULL,VERBOSE)
   --    /ERRORS=(BRIEF IMMEDIATE)

   --  If no option is provided (e.g. just /ERRORS is written), then the
   --  first option in the list is the default option. For /ERRORS this
   --  is NORMAL, so /ERRORS with no option is equivalent to /ERRORS=NORMAL.

   --  The COMMANDS_TRANSLATION case is only used for gnatmake, to correspond
   --  to the use of -cargs, -bargs and -largs (the ARGS string as indicated
   --  is one of these three possibilities). The name given by COMMAND is the
   --  corresponding command name to be used to interprete the switches to be
   --  passed on. Switches of this type set modes, e.g. /COMPILER_QUALIFIERS
   --  sets the mode so that all subsequent switches, up to another switch
   --  with COMMANDS_TRANSLATION apply to the corresponding commands issued
   --  by the make utility. For example

   --    /COMPILER_QUALIFIERS /LIST /BINDER_QUALIFIERS /MAIN
   --    /COMPILER_QUALIFIERS /NOLIST /COMPILE_CHECKS=SYNTAX

   --  Clearly these switches must come at the end of the list of switches
   --  since all subsequent switches apply to an issued command.

   --  For the DIRECT_TRANSLATION case, an implicit additional entry is
   --  created by prepending NO to the name of the qualifer, and then
   --  inverting the sense of the UNIX_SWITCHES string. For example,
   --  given the entry:

   --     "/LIST -gnatl"

   --  An implicit entry is created:

   --     "/NOLIST !-gnatl"

   --  In the case where, a ! is already present, inverting the sense of the
   --  switch means removing it.

   subtype S is String;
   --  A synonym to shorten the table

   type String_Ptr is access constant String;
   --  String pointer type used throughout

   type Switches is array (Natural range <>) of String_Ptr;
   --  Type used for array of swtiches

   type Switches_Ptr is access constant Switches;

   --------------------------------
   -- Switches for project files --
   --------------------------------

   S_Ext_Ref      : aliased constant S := "/EXTERNAL_REFERENCE=" & '"'    &
                                            "-X" & '"';

   S_Project_File : aliased constant S := "/PROJECT_FILE=<"               &
                                            "-P>";
   S_Project_Verb : aliased constant S := "/PROJECT_FILE_VERBOSITY="      &
                                            "DEFAULT "                    &
                                               "-vP0 "                    &
                                            "MEDIUM "                     &
                                               "-vP1 "                    &
                                            "HIGH "                       &
                                               "-vP2";

   ----------------------------
   -- Switches for GNAT BIND --
   ----------------------------

   S_Bind_Bind    : aliased constant S := "/BIND_FILE="                    &
                                            "ADA "                         &
                                               "-A "                       &
                                            "C "                           &
                                               "-C";

   S_Bind_Build   : aliased constant S := "/BUILD_LIBRARY=|"               &
                                            "-L|";

   S_Bind_Current : aliased constant S := "/CURRENT_DIRECTORY "            &
                                            "!-I-";

   S_Bind_Debug   : aliased constant S := "/DEBUG="                        &
                                            "TRACEBACK "                   &
                                               "-g2 "                      &
                                            "ALL "                         &
                                               "-g3 "                      &
                                            "NONE "                        &
                                               "-g0 "                      &
                                            "SYMBOLS "                     &
                                               "-g1 "                      &
                                            "NOSYMBOLS "                   &
                                               "!-g1 "                     &
                                            "LINK "                        &
                                               "-g3 "                      &
                                            "NOTRACEBACK "                 &
                                               "!-g2";

   S_Bind_DebugX  : aliased constant S := "/NODEBUG "                      &
                                            "!-g";

   S_Bind_Elab    : aliased constant S := "/ELABORATION_DEPENDENCIES "     &
                                            "-e";

   S_Bind_Error   : aliased constant S := "/ERROR_LIMIT=#"                 &
                                            "-m#";

   S_Bind_Help    : aliased constant S := "/HELP "                         &
                                            "-h";

   S_Bind_Init    : aliased constant S := "/INITIALIZE_SCALARS="           &
                                            "INVALID "                     &
                                               "-Sin "                     &
                                            "LOW "                         &
                                               "-Slo "                     &
                                            "HIGH "                        &
                                               "-Shi";

   S_Bind_Library : aliased constant S := "/LIBRARY_SEARCH=*"              &
                                            "-aO*";

   S_Bind_Linker  : aliased constant S := "/LINKER_OPTION_LIST "           &
                                            "-K";

   S_Bind_List    : aliased constant S := "/LIST_RESTRICTIONS "            &
                                            "-r";

   S_Bind_Main    : aliased constant S := "/MAIN "                         &
                                            "!-n";

   S_Bind_Nostinc : aliased constant S := "/NOSTD_INCLUDES "               &
                                            "-nostdinc";

   S_Bind_Nostlib : aliased constant S := "/NOSTD_LIBRARIES "              &
                                            "-nostdlib";

   S_Bind_No_Time : aliased constant S := "/NO_TIME_STAMP_CHECK "          &
                                            "-t";

   S_Bind_Object  : aliased constant S := "/OBJECT_LIST "                  &
                                            "-O";

   S_Bind_Order   : aliased constant S := "/ORDER_OF_ELABORATION "         &
                                            "-l";

   S_Bind_Output  : aliased constant S := "/OUTPUT=@"                      &
                                            "-o@";

   S_Bind_OutputX : aliased constant S := "/NOOUTPUT "                     &
                                            "-c";

   S_Bind_Pess    : aliased constant S := "/PESSIMISTIC_ELABORATION "      &
                                            "-p";

   S_Bind_Read    : aliased constant S := "/READ_SOURCES="                 &
                                            "ALL "                         &
                                               "-s "                       &
                                            "NONE "                        &
                                               "-x "                       &
                                            "AVAILABLE "                   &
                                               "!-x,!-s";

   S_Bind_ReadX   : aliased constant S := "/NOREAD_SOURCES "               &
                                            "-x";

   S_Bind_Rename  : aliased constant S := "/RENAME_MAIN=<"                  &
                                            "-M>";

   S_Bind_Report  : aliased constant S := "/REPORT_ERRORS="                &
                                            "VERBOSE "                     &
                                               "-v "                       &
                                            "BRIEF "                       &
                                               "-b "                       &
                                            "DEFAULT "                     &
                                               "!-b,!-v";

   S_Bind_ReportX : aliased constant S := "/NOREPORT_ERRORS "              &
                                            "!-b,!-v";

   S_Bind_Restr   : aliased constant S := "/RESTRICTION_LIST "             &
                                            "-r";

   S_Bind_RTS     : aliased constant S := "/RUNTIME_SYSTEM=|"              &
                                            "--RTS=|";

   S_Bind_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_Bind_Shared  : aliased constant S := "/SHARED "                       &
                                            "-shared";

   S_Bind_Slice   : aliased constant S := "/TIME_SLICE=#"                  &
                                            "-T#";

   S_Bind_Source  : aliased constant S := "/SOURCE_SEARCH=*"               &
                                            "-aI*";

   S_Bind_Time    : aliased constant S := "/TIME_STAMP_CHECK "             &
                                            "!-t";

   S_Bind_Verbose : aliased constant S := "/VERBOSE "                      &
                                            "-v";

   S_Bind_Warn    : aliased constant S := "/WARNINGS="                     &
                                            "NORMAL "                      &
                                               "!-ws,!-we "                &
                                            "SUPPRESS "                    &
                                               "-ws "                      &
                                            "ERROR "                       &
                                               "-we";

   S_Bind_WarnX   : aliased constant S := "/NOWARNINGS "                   &
     "-ws";

   Bind_Switches : aliased constant Switches :=
     (S_Bind_Bind    'Access,
      S_Bind_Build   'Access,
      S_Bind_Current 'Access,
      S_Bind_Debug   'Access,
      S_Bind_DebugX  'Access,
      S_Bind_Elab    'Access,
      S_Bind_Error   'Access,
      S_Ext_Ref      'Access,
      S_Bind_Help    'Access,
      S_Bind_Init    'Access,
      S_Bind_Library 'Access,
      S_Bind_Linker  'Access,
      S_Bind_List    'Access,
      S_Bind_Main    'Access,
      S_Bind_Nostinc 'Access,
      S_Bind_Nostlib 'Access,
      S_Bind_No_Time 'Access,
      S_Bind_Object  'Access,
      S_Bind_Order   'Access,
      S_Bind_Output  'Access,
      S_Bind_OutputX 'Access,
      S_Bind_Pess    'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_Bind_Read    'Access,
      S_Bind_ReadX   'Access,
      S_Bind_Rename  'Access,
      S_Bind_Report  'Access,
      S_Bind_ReportX 'Access,
      S_Bind_Restr   'Access,
      S_Bind_RTS     'Access,
      S_Bind_Search  'Access,
      S_Bind_Shared  'Access,
      S_Bind_Slice   'Access,
      S_Bind_Source  'Access,
      S_Bind_Time    'Access,
      S_Bind_Verbose 'Access,
      S_Bind_Warn    'Access,
      S_Bind_WarnX   'Access);

   ----------------------------
   -- Switches for GNAT CHOP --
   ----------------------------

   S_Chop_Comp   : aliased constant S := "/COMPILATION "                   &
                                            "-c";

   S_Chop_File   : aliased constant S := "/FILE_NAME_MAX_LENGTH=#"         &
                                            "-k#";

   S_Chop_Help   : aliased constant S := "/HELP "                          &
                                            "-h";

   S_Chop_Over   : aliased constant S := "/OVERWRITE "                     &
                                            "-w";

   S_Chop_Pres   : aliased constant S := "/PRESERVE "                      &
                                            "-p";

   S_Chop_Quiet  : aliased constant S := "/QUIET "                         &
                                            "-q";

   S_Chop_Ref    : aliased constant S := "/REFERENCE "                     &
                                            "-r";

   S_Chop_Verb   : aliased constant S := "/VERBOSE "                       &
                                            "-v";

   Chop_Switches : aliased constant Switches :=
     (S_Chop_Comp   'Access,
      S_Chop_File   'Access,
      S_Chop_Help   'Access,
      S_Chop_Over   'Access,
      S_Chop_Pres   'Access,
      S_Chop_Quiet  'Access,
      S_Chop_Ref    'Access,
      S_Chop_Verb   'Access);

   -------------------------------
   -- Switches for GNAT COMPILE --
   -------------------------------

   S_GCC_Ada_83  : aliased constant S := "/83 "                            &
                                             "-gnat83";

   S_GCC_Ada_95  : aliased constant S := "/95 "                            &
                                             "!-gnat83";

   S_GCC_Asm     : aliased constant S := "/ASM "                           &
                                             "-S,!-c";

   S_GCC_Checks  : aliased constant S := "/CHECKS="                        &
                                             "FULL "                       &
                                                "-gnato,!-gnatE,!-gnatp "  &
                                             "OVERFLOW "                   &
                                                "-gnato "                  &
                                             "ELABORATION "                &
                                                "-gnatE "                  &
                                             "ASSERTIONS "                 &
                                                "-gnata "                  &
                                             "DEFAULT "                    &
                                                "!-gnato,!-gnatp "         &
                                             "SUPPRESS_ALL "               &
                                                "-gnatp";

   S_GCC_ChecksX : aliased constant S := "/NOCHECKS "                      &
                                             "-gnatp,!-gnato,!-gnatE";

   S_GCC_Compres : aliased constant S := "/COMPRESS_NAMES "                &
                                             "-gnatC";

   S_GCC_Config  : aliased constant S := "/CONFIGURATION_PRAGMAS_FILE=<"   &
                                             "-gnatec>";

   S_GCC_Current : aliased constant S := "/CURRENT_DIRECTORY "             &
                                             "!-I-";

   S_GCC_Debug   : aliased constant S := "/DEBUG="                         &
                                            "SYMBOLS "                     &
                                               "-g2 "                      &
                                            "NOSYMBOLS "                   &
                                               "!-g2 "                     &
                                            "TRACEBACK "                   &
                                               "-g1 "                      &
                                            "ALL "                         &
                                               "-g3 "                      &
                                            "NONE "                        &
                                               "-g0 "                      &
                                            "NOTRACEBACK "                 &
                                               "-g0";

   S_GCC_DebugX  : aliased constant S := "/NODEBUG "                       &
                                             "!-g";

   S_GCC_Dist    : aliased constant S := "/DISTRIBUTION_STUBS="            &
                                            "RECEIVER "                    &
                                               "-gnatzr "                  &
                                            "CALLER "                      &
                                              "-gnatzc";

   S_GCC_DistX   : aliased constant S := "/NODISTRIBUTION_STUBS "          &
                                            "!-gnatzr,!-gnatzc";

   S_GCC_Error   : aliased constant S := "/ERROR_LIMIT=#"                  &
                                            "-gnatm#";

   S_GCC_ErrorX  : aliased constant S := "/NOERROR_LIMIT "                 &
                                            "-gnatm999";

   S_GCC_Expand  : aliased constant S := "/EXPAND_SOURCE "                 &
                                            "-gnatG";

   S_GCC_Extend  : aliased constant S := "/EXTENSIONS_ALLOWED "            &
                                            "-gnatX";

   S_GCC_File    : aliased constant S := "/FILE_NAME_MAX_LENGTH=#"         &
                                            "-gnatk#";

   S_GCC_Force   : aliased constant S := "/FORCE_ALI "                     &
                                            "-gnatQ";

   S_GCC_Help    : aliased constant S := "/HELP "                     &
                                            "-gnath";

   S_GCC_Ident   : aliased constant S := "/IDENTIFIER_CHARACTER_SET="      &
                                             "DEFAULT "                    &
                                                "-gnati1 "                 &
                                             "1 "                          &
                                                "-gnati1 "                 &
                                             "2 "                          &
                                                "-gnati2 "                 &
                                             "3 "                          &
                                                "-gnati3 "                 &
                                             "4 "                          &
                                                "-gnati4 "                 &
                                             "5 "                          &
                                                "-gnati5 "                 &
                                             "PC "                         &
                                                "-gnatip "                 &
                                             "PC850 "                      &
                                                "-gnati8 "                 &
                                             "FULL_UPPER "                 &
                                                "-gnatif "                 &
                                             "NO_UPPER "                   &
                                                "-gnatin "                 &
                                             "WIDE "                       &
                                                "-gnatiw";

   S_GCC_IdentX  : aliased constant S := "/NOIDENTIFIER_CHARACTER_SET "    &
                                             "-gnati1";

   S_GCC_Immed   : aliased constant S := "/IMMEDIATE_ERRORS "    &
                                             "-gnatdO";

   S_GCC_Inline  : aliased constant S := "/INLINE="                        &
                                            "PRAGMA "                      &
                                              "-gnatn "                    &
                                            "FULL "                        &
                                              "-gnatN "                    &
                                            "SUPPRESS "                    &
                                              "-fno-inline";

   S_GCC_InlineX : aliased constant S := "/NOINLINE "                      &
                                             "!-gnatn";

   S_GCC_Jumps   : aliased constant S := "/LONGJMP_SETJMP "                &
                                             "-gnatL";

   S_GCC_Length  : aliased constant S := "/MAX_LINE_LENGTH=#"              &
                                             "-gnatyM#";

   S_GCC_List    : aliased constant S := "/LIST "                          &
                                             "-gnatl";

   S_GCC_Noadc   : aliased constant S := "/NO_GNAT_ADC "                   &
                                             "-gnatA";

   S_GCC_Noload  : aliased constant S := "/NOLOAD "                        &
                                             "-gnatc";

   S_GCC_Nostinc : aliased constant S := "/NOSTD_INCLUDES "                &
                                             "-nostdinc";

   S_GCC_Opt     : aliased constant S := "/OPTIMIZE="                      &
                                            "ALL "                         &
                                               "-O2,!-O0,!-O1,!-O3 "       &
                                            "NONE "                        &
                                               "-O0,!-O1,!-O2,!-O3 "       &
                                            "SOME "                        &
                                               "-O1,!-O0,!-O2,!-O3 "       &
                                            "DEVELOPMENT "                 &
                                               "-O1,!-O0,!-O2,!-O3 "       &
                                            "UNROLL_LOOPS "                &
                                               "-funroll-loops "           &
                                            "INLINING "                    &
                                               "-O3,!-O0,!-O1,!-O2";

   S_GCC_OptX    : aliased constant S := "/NOOPTIMIZE "                    &
                                            "-O0,!-O1,!-O2,!-O3";

   S_GCC_Polling : aliased constant S := "/POLLING "                       &
                                            "-gnatP";

   S_GCC_Report  : aliased constant S := "/REPORT_ERRORS="                 &
                                            "VERBOSE "                     &
                                               "-gnatv "                   &
                                            "BRIEF "                       &
                                               "-gnatb "                   &
                                            "FULL "                        &
                                               "-gnatf "                   &
                                            "IMMEDIATE "                   &
                                               "-gnate "                   &
                                            "DEFAULT "                     &
                                               "!-gnatb,!-gnatv";

   S_GCC_ReportX : aliased constant S := "/NOREPORT_ERRORS "               &
                                            "!-gnatb,!-gnatv";

   S_GCC_Repinfo : aliased constant S := "/REPRESENTATION_INFO="           &
                                            "ARRAYS "                      &
                                               "-gnatR1 "                  &
                                            "NONE "                        &
                                               "-gnatR0 "                  &
                                            "OBJECTS "                     &
                                               "-gnatR2 "                  &
                                            "SYMBOLIC "                    &
                                               "-gnatR3 "                  &
                                            "DEFAULT "                     &
                                               "-gnatR";

   S_GCC_RepinfX : aliased constant S := "/NOREPRESENTATION_INFO "         &
                                            "!-gnatR";

   S_GCC_Search  : aliased constant S := "/SEARCH=*"                       &
                                            "-I*";

   S_GCC_Style   : aliased constant S := "/STYLE_CHECKS="                  &
                                            "ALL_BUILTIN "                 &
                                               "-gnaty "                   &
                                            "1 "                           &
                                               "-gnaty1 "                  &
                                            "2 "                           &
                                               "-gnaty2 "                  &
                                            "3 "                           &
                                               "-gnaty3 "                  &
                                            "4 "                           &
                                               "-gnaty4 "                  &
                                            "5 "                           &
                                               "-gnaty5 "                  &
                                            "6 "                           &
                                               "-gnaty6 "                  &
                                            "7 "                           &
                                               "-gnaty7 "                  &
                                            "8 "                           &
                                               "-gnaty8 "                  &
                                            "9 "                           &
                                               "-gnaty9 "                  &
                                            "ATTRIBUTE "                   &
                                               "-gnatya "                  &
                                            "BLANKS "                      &
                                               "-gnatyb "                  &
                                            "COMMENTS "                    &
                                               "-gnatyc "                  &
                                            "END "                         &
                                               "-gnatye "                  &
                                            "VTABS "                       &
                                               "-gnatyf "                  &
                                            "GNAT "                        &
                                               "-gnatg "                   &
                                            "HTABS "                       &
                                               "-gnatyh "                  &
                                            "IF_THEN "                     &
                                               "-gnatyi "                  &
                                            "KEYWORD "                     &
                                               "-gnatyk "                  &
                                            "LAYOUT "                      &
                                               "-gnatyl "                  &
                                            "LINE_LENGTH "                 &
                                               "-gnatym "                  &
                                            "STANDARD_CASING "             &
                                               "-gnatyn "                  &
                                            "ORDERED_SUBPROGRAMS "         &
                                               "-gnatyo "                  &
                                            "NONE "                        &
                                               "!-gnatg,!-gnatr "          &
                                            "PRAGMA "                      &
                                               "-gnatyp "                  &
                                            "RM_COLUMN_LAYOUT "            &
                                               "-gnatr "                   &
                                            "SPECS "                       &
                                               "-gnatys "                  &
                                            "TOKEN "                       &
                                               "-gnatyt ";

   S_GCC_StyleX  : aliased constant S := "/NOSTYLE_CHECKS "                &
                                            "!-gnatg,!-gnatr";

   S_GCC_Syntax  : aliased constant S := "/SYNTAX_ONLY "                   &
                                            "-gnats";

   S_GCC_Trace   : aliased constant S := "/TRACE_UNITS "                   &
                                            "-gnatdc";

   S_GCC_Tree    : aliased constant S := "/TREE_OUTPUT "                   &
                                            "-gnatt";

   S_GCC_Trys    : aliased constant S := "/TRY_SEMANTICS "                 &
                                            "-gnatq";

   S_GCC_Units   : aliased constant S := "/UNITS_LIST "                    &
                                            "-gnatu";

   S_GCC_Unique  : aliased constant S := "/UNIQUE_ERROR_TAG "              &
                                            "-gnatU";

   S_GCC_Upcase  : aliased constant S := "/UPPERCASE_EXTERNALS "           &
                                            "-gnatF";

   S_GCC_Valid   : aliased constant S := "/VALIDITY_CHECKING="             &
                                            "DEFAULT "                     &
                                               "-gnatVd "                  &
                                            "NODEFAULT "                   &
                                               "-gnatVD "                  &
                                            "COPIES "                      &
                                               "-gnatVc "                  &
                                            "NOCOPIES "                    &
                                               "-gnatVC "                  &
                                            "FLOATS "                      &
                                               "-gnatVf "                  &
                                            "NOFLOATS "                    &
                                               "-gnatVF "                  &
                                            "IN_PARAMS "                   &
                                               "-gnatVi "                  &
                                            "NOIN_PARAMS "                 &
                                               "-gnatVI "                  &
                                            "MOD_PARAMS "                  &
                                               "-gnatVm "                  &
                                            "NOMOD_PARAMS "                &
                                               "-gnatVM "                  &
                                            "OPERANDS "                    &
                                               "-gnatVo "                  &
                                            "NOOPERANDS "                  &
                                               "-gnatVO "                  &
                                            "RETURNS "                     &
                                               "-gnatVr "                  &
                                            "NORETURNS "                   &
                                               "-gnatVR "                  &
                                            "SUBSCRIPTS "                  &
                                               "-gnatVs "                  &
                                            "NOSUBSCRIPTS "                &
                                               "-gnatVS "                  &
                                            "TESTS "                       &
                                               "-gnatVt "                  &
                                            "NOTESTS "                     &
                                               "-gnatVT "                  &
                                            "ALL "                         &
                                               "-gnatVa "                  &
                                            "NONE "                        &
                                               "-gnatVn";

   S_GCC_Verbose : aliased constant S := "/VERBOSE "                       &
                                            "-v";

   S_GCC_Warn    : aliased constant S := "/WARNINGS="                      &
                                            "DEFAULT "                     &
                                               "!-gnatws,!-gnatwe "        &
                                            "ALL_GCC "                     &
                                               "-Wall "                    &
                                            "BIASED_ROUNDING "             &
                                               "-gnatwb "                  &
                                            "NOBIASED_ROUNDING "           &
                                               "-gnatwB "                  &
                                            "CONDITIONALS "                &
                                               "-gnatwc "                  &
                                            "NOCONDITIONALS "              &
                                               "-gnatwC "                  &
                                            "IMPLICIT_DEREFERENCE "        &
                                               "-gnatwd "                  &
                                            "NO_IMPLICIT_DEREFERENCE "     &
                                               "-gnatwD "                  &
                                            "ELABORATION "                 &
                                               "-gnatwl "                  &
                                            "NOELABORATION "               &
                                               "-gnatwL "                  &
                                            "ERRORS "                      &
                                               "-gnatwe "                  &
                                            "HIDING "                      &
                                               "-gnatwh "                  &
                                            "NOHIDING "                    &
                                               "-gnatwH "                  &
                                            "IMPLEMENTATION "              &
                                               "-gnatwi "                  &
                                            "NOIMPLEMENTATION "            &
                                               "-gnatwI "                  &
                                            "INEFFECTIVE_INLINE "          &
                                               "-gnatwp "                  &
                                            "NOINEFFECTIVE_INLINE "        &
                                               "-gnatwP "                  &
                                            "OPTIONAL "                    &
                                               "-gnatwa "                  &
                                            "NOOPTIONAL "                  &
                                               "-gnatwA "                  &
                                            "OVERLAYS "                    &
                                               "-gnatwo "                  &
                                            "NOOVERLAYS "                  &
                                               "-gnatwO "                  &
                                            "REDUNDANT "                   &
                                               "-gnatwr "                  &
                                            "NOREDUNDANT "                 &
                                               "-gnatwR "                  &
                                            "SUPPRESS "                    &
                                               "-gnatws "                  &
                                            "UNINITIALIZED "               &
                                               "-Wuninitialized "          &
                                            "UNREFERENCED_FORMALS "        &
                                               "-gnatwf "                  &
                                            "NOUNREFERENCED_FORMALS "      &
                                               "-gnatwF "                  &
                                            "UNUSED "                      &
                                               "-gnatwu "                  &
                                            "NOUNUSED "                    &
                                               "-gnatwU";

   S_GCC_WarnX   : aliased constant S := "/NOWARNINGS "                    &
                                            "-gnatws";

   S_GCC_Wide    : aliased constant S := "/WIDE_CHARACTER_ENCODING="       &
                                             "BRACKETS "                   &
                                                "-gnatWb "                 &
                                             "NONE "                       &
                                                "-gnatWn "                 &
                                             "HEX "                        &
                                                "-gnatWh "                 &
                                             "UPPER "                      &
                                                "-gnatWu "                 &
                                             "SHIFT_JIS "                  &
                                                "-gnatWs "                 &
                                             "UTF8 "                       &
                                                "-gnatW8 "                 &
                                             "EUC "                        &
                                                "-gnatWe";

   S_GCC_WideX   : aliased constant S := "/NOWIDE_CHARACTER_ENCODING "     &
                                             "-gnatWn";

   S_GCC_Xdebug  : aliased constant S := "/XDEBUG "                        &
                                             "-gnatD";

   S_GCC_Xref    : aliased constant S := "/XREF="                          &
                                            "GENERATE "                    &
                                               "!-gnatx "                  &
                                            "SUPPRESS "                    &
                                               "-gnatx";

   GCC_Switches : aliased constant Switches :=
     (S_GCC_Ada_83  'Access,
      S_GCC_Ada_95  'Access,
      S_GCC_Asm     'Access,
      S_GCC_Checks  'Access,
      S_GCC_ChecksX 'Access,
      S_GCC_Compres 'Access,
      S_GCC_Config  'Access,
      S_GCC_Current 'Access,
      S_GCC_Debug   'Access,
      S_GCC_DebugX  'Access,
      S_GCC_Dist    'Access,
      S_GCC_DistX   'Access,
      S_GCC_Error   'Access,
      S_GCC_ErrorX  'Access,
      S_GCC_Expand  'Access,
      S_GCC_Extend  'Access,
      S_Ext_Ref     'Access,
      S_GCC_File    'Access,
      S_GCC_Force   'Access,
      S_GCC_Help    'Access,
      S_GCC_Ident   'Access,
      S_GCC_IdentX  'Access,
      S_GCC_Immed   'Access,
      S_GCC_Inline  'Access,
      S_GCC_InlineX 'Access,
      S_GCC_Jumps   'Access,
      S_GCC_Length  'Access,
      S_GCC_List    'Access,
      S_GCC_Noadc   'Access,
      S_GCC_Noload  'Access,
      S_GCC_Nostinc 'Access,
      S_GCC_Opt     'Access,
      S_GCC_OptX    'Access,
      S_GCC_Polling 'Access,
      S_Project_File'Access,
      S_Project_Verb'Access,
      S_GCC_Report  'Access,
      S_GCC_ReportX 'Access,
      S_GCC_Repinfo 'Access,
      S_GCC_RepinfX 'Access,
      S_GCC_Search  'Access,
      S_GCC_Style   'Access,
      S_GCC_StyleX  'Access,
      S_GCC_Syntax  'Access,
      S_GCC_Trace   'Access,
      S_GCC_Tree    'Access,
      S_GCC_Trys    'Access,
      S_GCC_Units   'Access,
      S_GCC_Unique  'Access,
      S_GCC_Upcase  'Access,
      S_GCC_Valid   'Access,
      S_GCC_Verbose 'Access,
      S_GCC_Warn    'Access,
      S_GCC_WarnX   'Access,
      S_GCC_Wide    'Access,
      S_GCC_WideX   'Access,
      S_GCC_Xdebug  'Access,
      S_GCC_Xref    'Access);

   ----------------------------
   -- Switches for GNAT ELIM --
   ----------------------------

   S_Elim_All    : aliased constant S := "/ALL "                           &
                                            "-a";

   S_Elim_Bind   : aliased constant S := "/BIND_FILE=<"                    &
                                            "-b>";

   S_Elim_Miss   : aliased constant S := "/MISSED "                        &
                                            "-m";

   S_Elim_Quiet  : aliased constant S := "/QUIET "                         &
                                            "-q";

   S_Elim_Tree   : aliased constant S := "/TREE_DIRS=*"                    &
                                            "-T*";

   S_Elim_Verb   : aliased constant S := "/VERBOSE "                       &
                                            "-v";

   Elim_Switches : aliased constant Switches :=
     (S_Elim_All    'Access,
      S_Elim_Bind   'Access,
      S_Elim_Miss   'Access,
      S_Elim_Quiet  'Access,
      S_Elim_Tree   'Access,
      S_Elim_Verb   'Access);

   ----------------------------
   -- Switches for GNAT FIND --
   ----------------------------

   S_Find_All     : aliased constant S := "/ALL_FILES "                    &
                                            "-a";

   S_Find_Deriv   : aliased constant S := "/DERIVED_TYPE_INFORMATION "     &
                                            "-d";

   S_Find_Expr    : aliased constant S := "/EXPRESSIONS "                  &
                                            "-e";

   S_Find_Full    : aliased constant S := "/FULL_PATHNAME "                &
                                            "-f";

   S_Find_Ignore  : aliased constant S := "/IGNORE_LOCALS "                &
                                            "-g";

   S_Find_Nostinc : aliased constant S := "/NOSTD_INCLUDES "               &
                                            "-nostdinc";

   S_Find_Nostlib : aliased constant S := "/NOSTD_LIBRARIES "              &
                                            "-nostdlib";

   S_Find_Object  : aliased constant S := "/OBJECT_SEARCH=*"               &
                                            "-aO*";

   S_Find_Print   : aliased constant S := "/PRINT_LINES "                  &
                                            "-s";

   S_Find_Project : aliased constant S := "/PROJECT=@"                     &
                                            "-p@";

   S_Find_Ref     : aliased constant S := "/REFERENCES "                   &
                                            "-r";

   S_Find_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_Find_Source  : aliased constant S := "/SOURCE_SEARCH=*"               &
                                            "-aI*";

   S_Find_Types   : aliased constant S := "/TYPE_HIERARCHY "               &
                                            "-t";

   Find_Switches : aliased constant Switches :=
     (S_Find_All     'Access,
      S_Find_Deriv   'Access,
      S_Find_Expr    'Access,
      S_Ext_Ref      'Access,
      S_Find_Full    'Access,
      S_Find_Ignore  'Access,
      S_Find_Nostinc 'Access,
      S_Find_Nostlib 'Access,
      S_Find_Object  'Access,
      S_Find_Print   'Access,
      S_Find_Project 'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_Find_Ref     'Access,
      S_Find_Search  'Access,
      S_Find_Source  'Access,
      S_Find_Types   'Access);

   ------------------------------
   -- Switches for GNAT KRUNCH --
   ------------------------------

   S_Krunch_Count  : aliased constant S := "/COUNT=#"                      &
                                            "`#";

   Krunch_Switches : aliased constant Switches  :=
     (1 .. 1 => S_Krunch_Count  'Access);

   -------------------------------
   -- Switches for GNAT LIBRARY --
   -------------------------------

   S_Lbr_Config    : aliased constant S := "/CONFIG=@"                     &
                                            "--config=@";

   S_Lbr_Create    : aliased constant S := "/CREATE=%"                     &
                                            "--create=%";

   S_Lbr_Delete    : aliased constant S := "/DELETE=%"                     &
                                            "--delete=%";

   S_Lbr_Set       : aliased constant S := "/SET=%"                        &
                                            "--set=%";

   Lbr_Switches : aliased constant Switches  :=
     (S_Lbr_Config 'Access,
      S_Lbr_Create 'Access,
      S_Lbr_Delete 'Access,
      S_Lbr_Set    'Access);

   ----------------------------
   -- Switches for GNAT LINK --
   ----------------------------

   S_Link_Bind    : aliased constant S := "/BIND_FILE="                    &
                                            "ADA "                         &
                                               "-A "                       &
                                            "C "                           &
                                               "-C";

   S_Link_Debug   : aliased constant S := "/DEBUG="                        &
                                            "ALL "                         &
                                               "-g3 "                      &
                                            "NONE "                        &
                                               "-g0 "                      &
                                            "TRACEBACK "                   &
                                               "-g1 "                      &
                                            "NOTRACEBACK "                 &
                                               "-g0";

   S_Link_Execut  : aliased constant S := "/EXECUTABLE=@"                  &
                                            "-o@";

   S_Link_Force   : aliased constant S := "/FORCE_OBJECT_FILE_LIST "       &
                                            "-f";

   S_Link_Ident   : aliased constant S := "/IDENTIFICATION=" & '"'         &
                                            "--for-linker=IDENT="          &
                                            '"';

   S_Link_Nocomp  : aliased constant S := "/NOCOMPILE "                    &
                                            "-n";

   S_Link_Nofiles : aliased constant S := "/NOSTART_FILES "                &
                                            "-nostartfiles";

   S_Link_Noinhib : aliased constant S := "/NOINHIBIT-EXEC "               &
                                            "--for-linker=--noinhibit-exec";

   S_Link_Static  : aliased constant S := "/STATIC "                       &
                                            "--for-linker=-static";

   S_Link_Verb    : aliased constant S := "/VERBOSE "                      &
                                            "-v";

   S_Link_ZZZZZ   : aliased constant S := "/<other> "                      &
                                            "--for-linker=";

   Link_Switches : aliased constant Switches :=
     (S_Link_Bind    'Access,
      S_Link_Debug   'Access,
      S_Link_Execut  'Access,
      S_Ext_Ref      'Access,
      S_Link_Force   'Access,
      S_Link_Ident   'Access,
      S_Link_Nocomp  'Access,
      S_Link_Nofiles 'Access,
      S_Link_Noinhib 'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_Link_Static  'Access,
      S_Link_Verb    'Access,
      S_Link_ZZZZZ   'Access);

   ----------------------------
   -- Switches for GNAT LIST --
   ----------------------------

   S_List_All     : aliased constant S := "/ALL_UNITS "                    &
                                            "-a";

   S_List_Current : aliased constant S := "/CURRENT_DIRECTORY "            &
                                            "!-I-";

   S_List_Nostinc : aliased constant S := "/NOSTD_INCLUDES "               &
                                            "-nostdinc";

   S_List_Object  : aliased constant S := "/OBJECT_SEARCH=*"               &
                                            "-aO*";

   S_List_Output  : aliased constant S := "/OUTPUT="                       &
                                            "SOURCES "                     &
                                               "-s "                       &
                                            "DEPEND "                      &
                                               "-d "                       &
                                            "OBJECTS "                     &
                                               "-o "                       &
                                            "UNITS "                       &
                                               "-u "                       &
                                            "OPTIONS "                     &
                                               "-h "                       &
                                            "VERBOSE "                     &
                                               "-v ";

   S_List_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_List_Source  : aliased constant S := "/SOURCE_SEARCH=*"               &
                                            "-aI*";

   List_Switches : aliased constant Switches :=
     (S_List_All     'Access,
      S_List_Current 'Access,
      S_Ext_Ref      'Access,
      S_List_Nostinc 'Access,
      S_List_Object  'Access,
      S_List_Output  'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_List_Search  'Access,
      S_List_Source  'Access);

   ----------------------------
   -- Switches for GNAT MAKE --
   ----------------------------

   S_Make_Actions : aliased constant S := "/ACTIONS="                      &
                                            "COMPILE "                     &
                                               "-c "                       &
                                            "BIND "                        &
                                               "-b "                       &
                                            "LINK "                        &
                                               "-l ";

   S_Make_All     : aliased constant S := "/ALL_FILES "                    &
                                            "-a";

   S_Make_Bind    : aliased constant S := "/BINDER_QUALIFIERS=?"           &
                                            "-bargs BIND";

   S_Make_Comp    : aliased constant S := "/COMPILER_QUALIFIERS=?"         &
                                            "-cargs COMPILE";

   S_Make_Cond    : aliased constant S := "/CONDITIONAL_SOURCE_SEARCH=*"   &
                                            "-A*";

   S_Make_Cont    : aliased constant S := "/CONTINUE_ON_ERROR "            &
                                            "-k";

   S_Make_Current : aliased constant S := "/CURRENT_DIRECTORY "            &
                                            "!-I-";

   S_Make_Dep     : aliased constant S := "/DEPENDENCIES_LIST "            &
                                            "-M";

   S_Make_Doobj   : aliased constant S := "/DO_OBJECT_CHECK "              &
                                            "-n";

   S_Make_Execut  : aliased constant S := "/EXECUTABLE=@"                  &
                                            "-o@";

   S_Make_Force   : aliased constant S := "/FORCE_COMPILE "                &
                                            "-f";

   S_Make_Inplace : aliased constant S := "/IN_PLACE "                     &
                                            "-i";

   S_Make_Library : aliased constant S := "/LIBRARY_SEARCH=*"              &
                                            "-L*";

   S_Make_Link    : aliased constant S := "/LINKER_QUALIFIERS=?"           &
                                            "-largs LINK";

   S_Make_Mapping : aliased constant S := "/MAPPING "                      &
                                            "-C";

   S_Make_Minimal : aliased constant S := "/MINIMAL_RECOMPILATION "        &
                                            "-m";

   S_Make_Nolink  : aliased constant S := "/NOLINK "                       &
                                            "-c";

   S_Make_Nomain  : aliased constant S := "/NOMAIN "                       &
                                            "-z";

   S_Make_Nostinc : aliased constant S := "/NOSTD_INCLUDES "               &
                                            "-nostdinc";

   S_Make_Nostlib : aliased constant S := "/NOSTD_LIBRARIES "              &
                                            "-nostdlib";

   S_Make_Object  : aliased constant S := "/OBJECT_SEARCH=*"               &
                                            "-aO*";

   S_Make_Proc    : aliased constant S := "/PROCESSES=#"                   &
                                            "-j#";

   S_Make_Nojobs  : aliased constant S := "/NOPROCESSES "                  &
                                            "-j1";

   S_Make_Quiet   : aliased constant S := "/QUIET "                        &
                                            "-q";

   S_Make_Reason  : aliased constant S := "/REASONS "                      &
                                            "-v";

   S_Make_RTS     : aliased constant S := "/RUNTIME_SYSTEM=|"              &
                                            "--RTS=|";

   S_Make_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_Make_Skip    : aliased constant S := "/SKIP_MISSING=*"                &
                                            "-aL*";

   S_Make_Source  : aliased constant S := "/SOURCE_SEARCH=*"               &
                                            "-aI*";

   S_Make_Switch  : aliased constant S := "/SWITCH_CHECK "               &
                                            "-s";

   S_Make_Unique  : aliased constant S := "/UNIQUE "                       &
                                            "-u";

   S_Make_Verbose : aliased constant S := "/VERBOSE "                      &
                                            "-v";

   Make_Switches : aliased constant Switches :=
     (S_Make_Actions 'Access,
      S_Make_All     'Access,
      S_Make_Bind    'Access,
      S_Make_Comp    'Access,
      S_Make_Cond    'Access,
      S_Make_Cont    'Access,
      S_Make_Current 'Access,
      S_Make_Dep     'Access,
      S_Make_Doobj   'Access,
      S_Make_Execut  'Access,
      S_Ext_Ref      'Access,
      S_Make_Force   'Access,
      S_Make_Inplace 'Access,
      S_Make_Library 'Access,
      S_Make_Link    'Access,
      S_Make_Mapping 'Access,
      S_Make_Minimal 'Access,
      S_Make_Nolink  'Access,
      S_Make_Nomain  'Access,
      S_Make_Nostinc 'Access,
      S_Make_Nostlib 'Access,
      S_Make_Object  'Access,
      S_Make_Proc    'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_Make_Nojobs  'Access,
      S_Make_Quiet   'Access,
      S_Make_Reason  'Access,
      S_Make_RTS     'Access,
      S_Make_Search  'Access,
      S_Make_Skip    'Access,
      S_Make_Source  'Access,
      S_Make_Switch  'Access,
      S_Make_Unique  'Access,
      S_Make_Verbose 'Access);

   ----------------------------
   -- Switches for GNAT Name --
   ----------------------------

   S_Name_Conf    : aliased constant S := "/CONFIG_FILE=<"                   &
                                            "-c>";

   S_Name_Dirs    : aliased constant S := "/SOURCE_DIRS=*"                   &
                                            "-d*";

   S_Name_Dfile   : aliased constant S := "/DIRS_FILE=<"                     &
                                            "-D>";

   S_Name_Help    : aliased constant S := "/HELP"                            &
                                            " -h";

   S_Name_Proj    : aliased constant S := "/PROJECT_FILE=<"                  &
                                            "-P>";

   S_Name_Verbose : aliased constant S := "/VERBOSE"                         &
                                            " -v";

   Name_Switches : aliased constant Switches :=
     (S_Name_Conf         'Access,
      S_Name_Dirs         'Access,
      S_Name_Dfile        'Access,
      S_Name_Help         'Access,
      S_Name_Proj         'Access,
      S_Name_Verbose      'Access);

   ----------------------------------
   -- Switches for GNAT PREPROCESS --
   ----------------------------------

   S_Prep_Assoc   : aliased constant S := "/ASSOCIATE=" & '"'               &
                                            "-D" & '"';

   S_Prep_Blank   : aliased constant S := "/BLANK_LINES "                   &
                                            "-b";

   S_Prep_Com     : aliased constant S := "/COMMENTS "                      &
                                            "-c";

   S_Prep_Ref     : aliased constant S := "/REFERENCE "                     &
                                            "-r";

   S_Prep_Remove  : aliased constant S := "/REMOVE "                        &
                                            "!-b,!-c";

   S_Prep_Symbols : aliased constant S := "/SYMBOLS "                       &
                                            "-s";

   S_Prep_Undef   : aliased constant S := "/UNDEFINED "                     &
                                            "-u";

   Prep_Switches : aliased constant Switches :=
     (S_Prep_Assoc   'Access,
      S_Prep_Blank   'Access,
      S_Prep_Com     'Access,
      S_Prep_Ref     'Access,
      S_Prep_Remove  'Access,
      S_Prep_Symbols 'Access,
      S_Prep_Undef   'Access);

   ------------------------------
   -- Switches for GNAT SHARED --
   ------------------------------

   S_Shared_Debug   : aliased constant S := "/DEBUG="                      &
                                            "ALL "                         &
                                               "-g3 "                      &
                                            "NONE "                        &
                                               "-g0 "                      &
                                            "TRACEBACK "                   &
                                               "-g1 "                      &
                                            "NOTRACEBACK "                 &
                                               "-g0";

   S_Shared_Image  : aliased constant S := "/IMAGE=@"                      &
                                            "-o@";

   S_Shared_Ident   : aliased constant S := "/IDENTIFICATION=" & '"'       &
                                            "--for-linker=IDENT="          &
                                            '"';

   S_Shared_Nofiles : aliased constant S := "/NOSTART_FILES "              &
                                            "-nostartfiles";

   S_Shared_Noinhib : aliased constant S := "/NOINHIBIT-IMAGE "            &
                                            "--for-linker=--noinhibit-exec";

   S_Shared_Verb    : aliased constant S := "/VERBOSE "                    &
                                            "-v";

   S_Shared_ZZZZZ   : aliased constant S := "/<other> "                    &
                                            "--for-linker=";

   Shared_Switches : aliased constant Switches :=
     (S_Shared_Debug   'Access,
      S_Shared_Image   'Access,
      S_Shared_Ident   'Access,
      S_Shared_Nofiles 'Access,
      S_Shared_Noinhib 'Access,
      S_Shared_Verb    'Access,
      S_Shared_ZZZZZ   'Access);

   --------------------------------
   -- Switches for GNAT STANDARD --
   --------------------------------

   Standard_Switches : aliased constant Switches := (1 .. 0 => null);

   ----------------------------
   -- Switches for GNAT STUB --
   ----------------------------

   S_Stub_Current : aliased constant S := "/CURRENT_DIRECTORY "            &
                                            "!-I-";

   S_Stub_Full    : aliased constant S := "/FULL "                         &
                                            "-f";

   S_Stub_Header  : aliased constant S := "/HEADER="                       &
                                            "GENERAL "                     &
                                               "-hg "                      &
                                            "SPEC "                        &
                                               "-hs";

   S_Stub_Indent  : aliased constant S := "/INDENTATION=#"                 &
                                            "-i#";

   S_Stub_Length  : aliased constant S := "/LINE_LENGTH=#"                 &
                                            "-l#";

   S_Stub_Quiet   : aliased constant S := "/QUIET "                        &
                                            "-q";

   S_Stub_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_Stub_Tree    : aliased constant S := "/TREE_FILE="                    &
                                            "OVERWRITE "                   &
                                               "-t "                       &
                                            "SAVE "                        &
                                               "-k "                       &
                                            "REUSE "                       &
                                               "-r";

   S_Stub_Verbose : aliased constant S := "/VERBOSE "                      &
                                            "-v";

   Stub_Switches : aliased constant Switches :=
     (S_Stub_Current 'Access,
      S_Stub_Full    'Access,
      S_Stub_Header  'Access,
      S_Stub_Indent  'Access,
      S_Stub_Length  'Access,
      S_Stub_Quiet   'Access,
      S_Stub_Search  'Access,
      S_Stub_Tree    'Access,
      S_Stub_Verbose 'Access);

   ----------------------------
   -- Switches for GNAT XREF --
   ----------------------------

   S_Xref_All     : aliased constant S := "/ALL_FILES "                    &
                                            "-a";

   S_Xref_Deriv   : aliased constant S := "/DERIVED_TYPES "                &
                                            "-d";

   S_Xref_Full    : aliased constant S := "/FULL_PATHNAME "                &
                                            "-f";

   S_Xref_Global  : aliased constant S := "/IGNORE_LOCALS "                &
                                            "-g";

   S_Xref_Nostinc : aliased constant S := "/NOSTD_INCLUDES "               &
                                            "-nostdinc";

   S_Xref_Nostlib : aliased constant S := "/NOSTD_LIBRARIES "              &
                                            "-nostdlib";

   S_Xref_Object  : aliased constant S := "/OBJECT_SEARCH=*"               &
                                            "-aO*";

   S_Xref_Project : aliased constant S := "/PROJECT=@"                     &
                                            "-p@";

   S_Xref_Search  : aliased constant S := "/SEARCH=*"                      &
                                            "-I*";

   S_Xref_Source  : aliased constant S := "/SOURCE_SEARCH=*"               &
                                            "-aI*";

   S_Xref_Output  : aliased constant S := "/UNUSED "                       &
                                            "-u";

   S_Xref_Tags    : aliased constant S := "/TAGS "                         &
                                            "-v";

   Xref_Switches : aliased constant Switches :=
     (S_Xref_All     'Access,
      S_Xref_Deriv   'Access,
      S_Ext_Ref      'Access,
      S_Xref_Full    'Access,
      S_Xref_Global  'Access,
      S_Xref_Nostinc 'Access,
      S_Xref_Nostlib 'Access,
      S_Xref_Object  'Access,
      S_Xref_Project 'Access,
      S_Project_File 'Access,
      S_Project_Verb 'Access,
      S_Xref_Search  'Access,
      S_Xref_Source  'Access,
      S_Xref_Output  'Access,
      S_Xref_Tags    'Access);

   -------------------
   -- COMMAND TABLE --
   -------------------

   --  The command table contains an entry for each command recognized by
   --  GNATCmd. The entries are represented by an array of records.

   type Parameter_Type is
   --  A parameter is defined as a whitespace bounded string, not begining
   --   with a slash. (But see note under FILES_OR_WILDCARD).
     (File,
      --  A required file or directory parameter.

      Optional_File,
      --  An optional file or directory parameter.

      Other_As_Is,
      --  A parameter that's passed through as is (not canonicalized)

      Unlimited_Files,
      --  An unlimited number of whitespace separate file or directory
      --  parameters including wildcard specifications.

      Unlimited_As_Is,
      --  Un unlimited number of whitespace separated paameters that are
      --  passed through as is (not canonicalized).

      Files_Or_Wildcard);
      --  A comma separated list of files and/or wildcard file specifications.
      --  A comma preceded by or followed by whitespace is considered as a
      --  single comma character w/o whitespace.

   type Parameter_Array is array (Natural range <>) of Parameter_Type;
   type Parameter_Ref is access all Parameter_Array;

   type Command_Type is
     (Bind, Chop, Compile, Elim, Find, Krunch, Library, Link, List,
      Make, Name, Preprocess, Shared, Standard, Stub, Xref, Undefined);

   type Alternate_Command is (Comp, Ls, Kr, Prep, Psta);
   --  Alternate command libel for non VMS system

   Corresponding_To : constant array (Alternate_Command) of Command_Type :=
     (Comp  => Compile,
      Ls    => List,
      Kr    => Krunch,
      Prep  => Preprocess,
      Psta  => Standard);
   --  Mapping of alternate commands to commands

   subtype Real_Command_Type is Command_Type range Bind .. Xref;

   type Command_Entry is record
      Cname : String_Ptr;
      --  Command name for GNAT xxx command

      Usage : String_Ptr;
      --  A usage string, used for error messages

      Unixcmd : String_Ptr;
      --  Corresponding Unix command

      Unixsws : Argument_List_Access;
      --  Switches for the Unix command

      VMS_Only : Boolean;
      --  When True, the command can only be used on VMS

      Switches : Switches_Ptr;
      --  Pointer to array of switch strings

      Params : Parameter_Ref;
      --  Describes the allowable types of parameters.
      --  Params (1) is the type of the first parameter, etc.
      --  An empty parameter array means this command takes no parameters.

      Defext : String (1 .. 3);
      --  Default extension. If non-blank, then this extension is supplied by
      --  default as the extension for any file parameter which does not have
      --  an extension already.
   end record;

   -------------------------
   -- INTERNAL STRUCTURES --
   -------------------------

   --  The switches and commands are defined by strings in the previous
   --  section so that they are easy to modify, but internally, they are
   --  kept in a more conveniently accessible form described in this
   --  section.

   --  Commands, command qualifers and options have a similar common format
   --  so that searching for matching names can be done in a common manner.

   type Item_Id is (Id_Command, Id_Switch, Id_Option);

   type Translation_Type is
     (
      T_Direct,
      --  A qualifier with no options.
      --  Example: GNAT MAKE /VERBOSE

      T_Directories,
      --  A qualifier followed by a list of directories
      --  Example: GNAT COMPILE /SEARCH=([], [.FOO], [.BAR])

      T_Directory,
      --  A qualifier followed by one directory
      --  Example: GNAT LIBRARY /SET=[.VAXFLOATLIB]

      T_File,
      --  A qualifier followed by a filename
      --  Example: GNAT LINK /EXECUTABLE=FOO.EXE

      T_No_Space_File,
      --  A qualifier followed by a filename
      --  Example: GNAT MAKE /PROJECT_FILE=PRJ.GPR

      T_Numeric,
      --  A qualifier followed by a numeric value.
      --  Example: GNAT CHOP /FILE_NAME_MAX_LENGTH=39

      T_String,
      --  A qualifier followed by a quoted string. Only used by
      --  /IDENTIFICATION qualfier.
      --  Example: GNAT LINK /IDENTIFICATION="3.14a1 version"

      T_Options,
      --  A qualifier followed by a list of options.
      --  Example: GNAT COMPILE /REPRESENTATION_INFO=(ARRAYS,OBJECTS)

      T_Commands,
      --  A qualifier followed by a list. Only used for
      --  MAKE /COMPILER_QUALIFIERS /BINDER_QUALIFIERS /LINKER_QUALIFIERS
      --  (gnatmake -cargs -bargs -largs )
      --  Example: GNAT MAKE ... /LINKER_QUALIFIERS /VERBOSE FOOBAR.OBJ

      T_Other,
      --  A qualifier passed directly to the linker. Only used
      --  for LINK and SHARED if no other match is found.
      --  Example: GNAT LINK FOO.ALI /SYSSHR

      T_Alphanumplus
      --  A qualifier followed by a legal linker symbol prefix. Only used
      --  for BIND /BUILD_LIBRARY (gnatbind -Lxyz).
      --  Example: GNAT BIND /BUILD_LIBRARY=foobar
      );

   type Item (Id : Item_Id);
   type Item_Ptr is access all Item;

   type Item (Id : Item_Id) is record
      Name : String_Ptr;
      --  Name of the command, switch (with slash) or option

      Next : Item_Ptr;
      --  Pointer to next item on list, always has the same Id value

      Command : Command_Type := Undefined;

      Unix_String : String_Ptr := null;
      --  Corresponding Unix string. For a command, this is the unix command
      --  name and possible default switches. For a switch or option it is
      --  the unix switch string.

      case Id is

         when Id_Command =>

            Switches : Item_Ptr;
            --  Pointer to list of switch items for the command, linked
            --  through the Next fields with null terminating the list.

            Usage : String_Ptr;
            --  Usage information, used only for errors and the default
            --  list of commands output.

            Params : Parameter_Ref;
            --  Array of parameters

            Defext : String (1 .. 3);
            --  Default extension. If non-blank, then this extension is
            --  supplied by default as the extension for any file parameter
            --  which does not have an extension already.

         when Id_Switch =>

            Translation : Translation_Type;
            --  Type of switch translation. For all cases, except Options,
            --  this is the only field needed, since the Unix translation
            --  is found in Unix_String.

            Options : Item_Ptr;
            --  For the Options case, this field is set to point to a list
            --  of options item (for this case Unix_String is null in the
            --  main switch item). The end of the list is marked by null.

         when Id_Option =>

            null;
            --  No special fields needed, since Name and Unix_String are
            --  sufficient to completely described an option.

      end case;
   end record;

   subtype Command_Item is Item (Id_Command);
   subtype Switch_Item  is Item (Id_Switch);
   subtype Option_Item  is Item (Id_Option);

   ----------------------------------
   -- Declarations for GNATCMD use --
   ----------------------------------

   Commands : Item_Ptr;
   --  Pointer to head of list of command items, one for each command, with
   --  the end of the list marked by a null pointer.

   Last_Command : Item_Ptr;
   --  Pointer to last item in Commands list

   Normal_Exit : exception;
   --  Raise this exception for normal program termination

   Error_Exit : exception;
   --  Raise this exception if error detected

   Errors : Natural := 0;
   --  Count errors detected

   Command_Arg : Positive := 1;

   Command : Item_Ptr;
   --  Pointer to command item for current command

   Make_Commands_Active : Item_Ptr := null;
   --  Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
   --  if a COMMANDS_TRANSLATION switch has been encountered while processing
   --  a MAKE Command.

   My_Exit_Status : Exit_Status := Success;

   package Buffer is new Table.Table
     (Table_Component_Type => Character,
      Table_Index_Type     => Integer,
      Table_Low_Bound      => 1,
      Table_Initial        => 4096,
      Table_Increment      => 2,
      Table_Name           => "Buffer");

   Param_Count : Natural := 0;
   --  Number of parameter arguments so far

   Arg_Num : Natural;
   --  Argument number

   Display_Command : Boolean := False;
   --  Set true if /? switch causes display of generated command (on VMS)

   The_Command : Command_Type;
   --  The command used

   -----------------------
   -- Local Subprograms --
   -----------------------

   function Index (Char : Character; Str : String) return Natural;
   --  Returns the first occurrence of Char in Str.
   --  Returns 0 if Char is not in Str.

   function Init_Object_Dirs return Argument_List;

   function Invert_Sense (S : String) return String_Ptr;
   --  Given a unix switch string S, computes the inverse (adding or
   --  removing ! characters as required), and returns a pointer to
   --  the allocated result on the heap.

   function Is_Extensionless (F : String) return Boolean;
   --  Returns true if the filename has no extension.

   function Match (S1, S2 : String) return Boolean;
   --  Determines whether S1 and S2 match. This is a case insensitive match.

   function Match_Prefix (S1, S2 : String) return Boolean;
   --  Determines whether S1 matches a prefix of S2. This is also a case
   --  insensitive match (for example Match ("AB","abc") is True).

   function Matching_Name
     (S     : String;
      Itm   : Item_Ptr;
      Quiet : Boolean := False)
      return  Item_Ptr;
   --  Determines if the item list headed by Itm and threaded through the
   --  Next fields (with null marking the end of the list), contains an
   --  entry that uniquely matches the given string. The match is case
   --  insensitive and permits unique abbreviation. If the match succeeds,
   --  then a pointer to the matching item is returned. Otherwise, an
   --  appropriate error message is written. Note that the discriminant
   --  of Itm is used to determine the appropriate form of this message.
   --  Quiet is normally False as shown, if it is set to True, then no
   --  error message is generated in a not found situation (null is still
   --  returned to indicate the not-found situation).

   procedure Non_VMS_Usage;
   --  Display usage for platforms other than VMS

   function OK_Alphanumerplus (S : String) return Boolean;
   --  Checks that S is a string of alphanumeric characters,
   --  returning True if all alphanumeric characters,
   --  False if empty or a non-alphanumeric character is present.

   function OK_Integer (S : String) return Boolean;
   --  Checks that S is a string of digits, returning True if all digits,
   --  False if empty or a non-digit is present.

   procedure Output_Version;
   --  Output the version of this program

   procedure Place (C : Character);
   --  Place a single character in the buffer, updating Ptr

   procedure Place (S : String);
   --  Place a string character in the buffer, updating Ptr

   procedure Place_Lower (S : String);
   --  Place string in buffer, forcing letters to lower case, updating Ptr

   procedure Place_Unix_Switches (S : String_Ptr);
   --  Given a unix switch string, place corresponding switches in Buffer,
   --  updating Ptr appropriatelly. Note that in the case of use of ! the
   --  result may be to remove a previously placed switch.

   procedure Set_Library_For
     (Project             : Project_Id;
      There_Are_Libraries : in out Boolean);
   --  If Project is a library project, add the correct
   --  -L and -l switches to the linker invocation.

   procedure Set_Libraries is
      new For_Every_Project_Imported (Boolean, Set_Library_For);
   --  Add the -L and -l switches to the linker for all
   --  of the library projects.

   procedure Validate_Command_Or_Option (N : String_Ptr);
   --  Check that N is a valid command or option name, i.e. that it is of the
   --  form of an Ada identifier with upper case letters and underscores.

   procedure Validate_Unix_Switch (S : String_Ptr);
   --  Check that S is a valid switch string as described in the syntax for
   --  the switch table item UNIX_SWITCH or else begins with a backquote.

   procedure VMS_Conversion (The_Command : out Command_Type);
   --  Converts VMS command line to equivalent Unix command line

   -----------
   -- Index --
   -----------

   function Index (Char : Character; Str : String) return Natural is
   begin
      for Index in Str'Range loop
         if Str (Index) = Char then
            return Index;
         end if;
      end loop;

      return 0;
   end Index;

   ----------------------
   -- Init_Object_Dirs --
   ----------------------

   function Init_Object_Dirs return Argument_List is
      Object_Dirs     : Integer;
      Object_Dir      : Argument_List (1 .. 256);
      Object_Dir_Name : String_Access;

   begin
      Object_Dirs := 0;
      Object_Dir_Name := String_Access (Object_Dir_Default_Name);
      Get_Next_Dir_In_Path_Init (Object_Dir_Name);

      loop
         declare
            Dir : String_Access := String_Access
              (Get_Next_Dir_In_Path (Object_Dir_Name));
         begin
            exit when Dir = null;
            Object_Dirs := Object_Dirs + 1;
            Object_Dir (Object_Dirs) :=
              new String'("-L" &
                          To_Canonical_Dir_Spec
                          (To_Host_Dir_Spec
                           (Normalize_Directory_Name (Dir.all).all,
                            True).all, True).all);
         end;
      end loop;

      Object_Dirs := Object_Dirs + 1;
      Object_Dir (Object_Dirs) := new String'("-lgnat");

      if Hostparm.OpenVMS then
         Object_Dirs := Object_Dirs + 1;
         Object_Dir (Object_Dirs) := new String'("-ldecgnat");
      end if;

      return Object_Dir (1 .. Object_Dirs);
   end Init_Object_Dirs;

   ------------------
   -- Invert_Sense --
   ------------------

   function Invert_Sense (S : String) return String_Ptr is
      Sinv : String (1 .. S'Length * 2);
      --  Result (for sure long enough)

      Sinvp : Natural := 0;
      --  Pointer to output string

   begin
      for Sp in S'Range loop
         if Sp = S'First or else S (Sp - 1) = ',' then
            if S (Sp) = '!' then
               null;
            else
               Sinv (Sinvp + 1) := '!';
               Sinv (Sinvp + 2) := S (Sp);
               Sinvp := Sinvp + 2;
            end if;

         else
            Sinv (Sinvp + 1) := S (Sp);
            Sinvp := Sinvp + 1;
         end if;
      end loop;

      return new String'(Sinv (1 .. Sinvp));
   end Invert_Sense;

   ----------------------
   -- Is_Extensionless --
   ----------------------

   function Is_Extensionless (F : String) return Boolean is
   begin
      for J in reverse F'Range loop
         if F (J) = '.' then
            return False;
         elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
            return True;
         end if;
      end loop;

      return True;
   end Is_Extensionless;

   -----------
   -- Match --
   -----------

   function Match (S1, S2 : String) return Boolean is
      Dif : constant Integer := S2'First - S1'First;

   begin

      if S1'Length /= S2'Length then
         return False;

      else
         for J in S1'Range loop
            if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
               return False;
            end if;
         end loop;

         return True;
      end if;
   end Match;

   ------------------
   -- Match_Prefix --
   ------------------

   function Match_Prefix (S1, S2 : String) return Boolean is
   begin
      if S1'Length > S2'Length then
         return False;
      else
         return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
      end if;
   end Match_Prefix;

   -------------------
   -- Matching_Name --
   -------------------

   function Matching_Name
     (S     : String;
      Itm   : Item_Ptr;
      Quiet : Boolean := False)
     return  Item_Ptr
   is
      P1, P2 : Item_Ptr;

      procedure Err;
      --  Little procedure to output command/qualifier/option as appropriate
      --  and bump error count.

      ---------
      -- Err --
      ---------

      procedure Err is
      begin
         if Quiet then
            return;
         end if;

         Errors := Errors + 1;

         if Itm /= null then
            case Itm.Id is
               when Id_Command =>
                  Put (Standard_Error, "command");

               when Id_Switch =>
                  if OpenVMS then
                     Put (Standard_Error, "qualifier");
                  else
                     Put (Standard_Error, "switch");
                  end if;

               when Id_Option =>
                  Put (Standard_Error, "option");

            end case;
         else
            Put (Standard_Error, "input");

         end if;

         Put (Standard_Error, ": ");
         Put (Standard_Error, S);
      end Err;

   --  Start of processing for Matching_Name

   begin
      --  If exact match, that's the one we want

      P1 := Itm;
      while P1 /= null loop
         if Match (S, P1.Name.all) then
            return P1;
         else
            P1 := P1.Next;
         end if;
      end loop;

      --  Now check for prefix matches

      P1 := Itm;
      while P1 /= null loop
         if P1.Name.all = "/<other>" then
            return P1;

         elsif not Match_Prefix (S, P1.Name.all) then
            P1 := P1.Next;

         else
            --  Here we have found one matching prefix, so see if there is
            --  another one (which is an ambiguity)

            P2 := P1.Next;
            while P2 /= null loop
               if Match_Prefix (S, P2.Name.all) then
                  if not Quiet then
                     Put (Standard_Error, "ambiguous ");
                     Err;
                     Put (Standard_Error, " (matches ");
                     Put (Standard_Error, P1.Name.all);

                     while P2 /= null loop
                        if Match_Prefix (S, P2.Name.all) then
                           Put (Standard_Error, ',');
                           Put (Standard_Error, P2.Name.all);
                        end if;

                        P2 := P2.Next;
                     end loop;

                     Put_Line (Standard_Error, ")");
                  end if;

                  return null;
               end if;

               P2 := P2.Next;
            end loop;

            --  If we fall through that loop, then there was only one match

            return P1;
         end if;
      end loop;

      --  If we fall through outer loop, there was no match

      if not Quiet then
         Put (Standard_Error, "unrecognized ");
         Err;
         New_Line (Standard_Error);
      end if;

      return null;
   end Matching_Name;

   -----------------------
   -- OK_Alphanumerplus --
   -----------------------

   function OK_Alphanumerplus (S : String) return Boolean is
   begin
      if S'Length = 0 then
         return False;

      else
         for J in S'Range loop
            if not (Is_Alphanumeric (S (J)) or else
                    S (J) = '_' or else S (J) = '$')
            then
               return False;
            end if;
         end loop;

         return True;
      end if;
   end OK_Alphanumerplus;

   ----------------
   -- OK_Integer --
   ----------------

   function OK_Integer (S : String) return Boolean is
   begin
      if S'Length = 0 then
         return False;

      else
         for J in S'Range loop
            if not Is_Digit (S (J)) then
               return False;
            end if;
         end loop;

         return True;
      end if;
   end OK_Integer;

   --------------------
   -- Output_Version --
   --------------------

   procedure Output_Version is
   begin
      Put ("GNAT ");
      Put (Gnatvsn.Gnat_Version_String);
      Put_Line (" Copyright 1996-2002 Free Software Foundation, Inc.");
   end Output_Version;

   -----------
   -- Place --
   -----------

   procedure Place (C : Character) is
   begin
      Buffer.Increment_Last;
      Buffer.Table (Buffer.Last) := C;

      --  Do not put a space as the first character in the buffer
      if C = ' ' and then Buffer.Last = 1 then
         Buffer.Decrement_Last;
      end if;
   end Place;

   procedure Place (S : String) is
   begin
      for J in S'Range loop
         Place (S (J));
      end loop;
   end Place;

   -----------------
   -- Place_Lower --
   -----------------

   procedure Place_Lower (S : String) is
   begin
      for J in S'Range loop
         Place (To_Lower (S (J)));
      end loop;
   end Place_Lower;

   -------------------------
   -- Place_Unix_Switches --
   -------------------------

   procedure Place_Unix_Switches (S : String_Ptr) is
      P1, P2, P3 : Natural;
      Remove     : Boolean;
      Slen       : Natural;

   begin
      P1 := S'First;
      while P1 <= S'Last loop
         if S (P1) = '!' then
            P1 := P1 + 1;
            Remove := True;
         else
            Remove := False;
         end if;

         P2 := P1;
         pragma Assert (S (P1) = '-' or else S (P1) = '`');

         while P2 < S'Last and then S (P2 + 1) /= ',' loop
            P2 := P2 + 1;
         end loop;

         --  Switch is now in S (P1 .. P2)

         Slen := P2 - P1 + 1;

         if Remove then
            P3 := 2;
            while P3 <= Buffer.Last - Slen loop
               if Buffer.Table (P3) = ' '
                 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
                                                             S (P1 .. P2)
                 and then (P3 + Slen = Buffer.Last
                             or else
                           Buffer.Table (P3 + Slen + 1) = ' ')
               then
                  Buffer.Table (P3 .. Buffer.Last - Slen - 1) :=
                    Buffer.Table (P3 + Slen + 1 .. Buffer.Last);
                  Buffer.Set_Last (Buffer.Last - Slen - 1);

               else
                  P3 := P3 + 1;
               end if;
            end loop;

         else
            Place (' ');

            if S (P1) = '`' then
               P1 := P1 + 1;
            end if;

            Place (S (P1 .. P2));
         end if;

         P1 := P2 + 2;
      end loop;
   end Place_Unix_Switches;

   ---------------------
   -- Set_Library_For --
   ---------------------

   procedure Set_Library_For
     (Project             : Project_Id;
      There_Are_Libraries : in out Boolean)
   is
   begin
      --  Case of library project

      if Projects.Table (Project).Library then
         There_Are_Libraries := True;

         --  Add the -L switch

         Last_Switches.Increment_Last;
         Last_Switches.Table (Last_Switches.Last) :=
           new String'("-L" &
                       Get_Name_String
                       (Projects.Table (Project).Library_Dir));

         --  Add the -l switch

         Last_Switches.Increment_Last;
         Last_Switches.Table (Last_Switches.Last) :=
           new String'("-l" &
                       Get_Name_String
                       (Projects.Table (Project).Library_Name));

         --  Add the Wl,-rpath switch if library non static

         if Projects.Table (Project).Library_Kind /= Static then
            declare
               Option : constant String_Access :=
                          MLib.Tgt.Linker_Library_Path_Option
                            (Get_Name_String
                              (Projects.Table (Project).Library_Dir));

            begin
               if Option /= null then
                  Last_Switches.Increment_Last;
                  Last_Switches.Table (Last_Switches.Last) :=
                    Option;
               end if;

            end;

         end if;

      end if;
   end Set_Library_For;

   --------------------------------
   -- Validate_Command_Or_Option --
   --------------------------------

   procedure Validate_Command_Or_Option (N : String_Ptr) is
   begin
      pragma Assert (N'Length > 0);

      for J in N'Range loop
         if N (J) = '_' then
            pragma Assert (N (J - 1) /= '_');
            null;
         else
            pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
            null;
         end if;
      end loop;
   end Validate_Command_Or_Option;

   --------------------------
   -- Validate_Unix_Switch --
   --------------------------

   procedure Validate_Unix_Switch (S : String_Ptr) is
   begin
      if S (S'First) = '`' then
         return;
      end if;

      pragma Assert (S (S'First) = '-' or else S (S'First) = '!');

      for J in S'First + 1 .. S'Last loop
         pragma Assert (S (J) /= ' ');

         if S (J) = '!' then
            pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
            null;
         end if;
      end loop;
   end Validate_Unix_Switch;

   ----------------------
   -- List of Commands --
   ----------------------

   --  Note that we put this after all the local bodies (except Non_VMS_Usage
   --  and VMS_Conversion that use Command_List) to avoid some access before
   --  elaboration problems.

   Command_List : constant array (Real_Command_Type) of Command_Entry :=
     (Bind =>
       (Cname    => new S'("BIND"),
        Usage    => new S'("GNAT BIND file[.ali] /qualifiers"),
        VMS_Only => False,
        Unixcmd  => new S'("gnatbind"),
        Unixsws  => null,
        Switches => Bind_Switches'Access,
        Params   => new Parameter_Array'(1 => File),
        Defext   => "ali"),

      Chop =>
        (Cname    => new S'("CHOP"),
         Usage    => new S'("GNAT CHOP file [directory] /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatchop"),
         Unixsws  => null,
         Switches => Chop_Switches'Access,
         Params   => new Parameter_Array'(1 => File, 2 => Optional_File),
         Defext   => "   "),

      Compile =>
        (Cname    => new S'("COMPILE"),
         Usage    => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatmake"),
         Unixsws  => new Argument_List' (1 => new String'("-f"),
                                         2 => new String'("-u"),
                                         3 => new String'("-c")),
         Switches => GCC_Switches'Access,
         Params   => new Parameter_Array'(1 => Files_Or_Wildcard),
         Defext   => "   "),

      Elim =>
        (Cname    => new S'("ELIM"),
         Usage    => new S'("GNAT ELIM name /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatelim"),
         Unixsws  => null,
         Switches => Elim_Switches'Access,
         Params   => new Parameter_Array'(1 => Other_As_Is),
         Defext   => "ali"),

      Find =>
        (Cname    => new S'("FIND"),
         Usage    => new S'("GNAT FIND pattern[:sourcefile[:line"
                            & "[:column]]] filespec[,...] /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatfind"),
         Unixsws  => null,
         Switches => Find_Switches'Access,
         Params   => new Parameter_Array'(1 => Other_As_Is,
                                          2 => Files_Or_Wildcard),
         Defext   => "ali"),

      Krunch =>
        (Cname    => new S'("KRUNCH"),
         Usage    => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatkr"),
         Unixsws  => null,
         Switches => Krunch_Switches'Access,
         Params   => new Parameter_Array'(1 => File),
         Defext   => "   "),

      Library =>
        (Cname    => new S'("LIBRARY"),
         Usage    => new S'("GNAT LIBRARY /[CREATE | SET | DELETE]"
                            & "=directory [/CONFIG=file]"),
         VMS_Only => True,
         Unixcmd  => new S'("gnatlbr"),
         Unixsws  => null,
         Switches => Lbr_Switches'Access,
         Params   => new Parameter_Array'(1 .. 0 => File),
         Defext   => "   "),

      Link =>
        (Cname    => new S'("LINK"),
         Usage    => new S'("GNAT LINK file[.ali]"
                            & " [extra obj_&_lib_&_exe_&_opt files]"
                            & " /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatlink"),
         Unixsws  => null,
         Switches => Link_Switches'Access,
         Params   => new Parameter_Array'(1 => Unlimited_Files),
         Defext   => "ali"),

      List =>
        (Cname    => new S'("LIST"),
         Usage    => new S'("GNAT LIST /qualifiers object_or_ali_file"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatls"),
         Unixsws  => null,
         Switches => List_Switches'Access,
         Params   => new Parameter_Array'(1 => File),
         Defext   => "ali"),

      Make =>
        (Cname    => new S'("MAKE"),
         Usage    => new S'("GNAT MAKE file /qualifiers (includes "
                            & "COMPILE /qualifiers)"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatmake"),
         Unixsws  => null,
         Switches => Make_Switches'Access,
         Params   => new Parameter_Array'(1 => File),
         Defext   => "   "),

      Name =>
        (Cname    => new S'("NAME"),
         Usage    => new S'("GNAT NAME /qualifiers naming-pattern "
                            & "[naming-patterns]"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatname"),
         Unixsws  => null,
         Switches => Name_Switches'Access,
         Params   => new Parameter_Array'(1 => Unlimited_As_Is),
         Defext   => "   "),

      Preprocess =>
        (Cname    => new S'("PREPROCESS"),
         Usage    => new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatprep"),
         Unixsws  => null,
         Switches => Prep_Switches'Access,
         Params   => new Parameter_Array'(1 .. 3 => File),
         Defext   => "   "),

      Shared =>
        (Cname    => new S'("SHARED"),
         Usage    => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
                            & "files] /qualifiers"),
         VMS_Only => True,
         Unixcmd  => new S'("gcc"),
         Unixsws  => new Argument_List'(new String'("-shared")
                                        & Init_Object_Dirs),
         Switches => Shared_Switches'Access,
         Params   => new Parameter_Array'(1 => Unlimited_Files),
         Defext   => "   "),

      Standard =>
        (Cname    => new S'("STANDARD"),
         Usage    => new S'("GNAT STANDARD"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatpsta"),
         Unixsws  => null,
         Switches => Standard_Switches'Access,
         Params   => new Parameter_Array'(1 .. 0 => File),
         Defext   => "   "),

      Stub =>
        (Cname    => new S'("STUB"),
         Usage    => new S'("GNAT STUB file [directory]/qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatstub"),
         Unixsws  => null,
         Switches => Stub_Switches'Access,
         Params   => new Parameter_Array'(1 => File, 2 => Optional_File),
         Defext   => "   "),

      Xref =>
        (Cname    => new S'("XREF"),
         Usage    => new S'("GNAT XREF filespec[,...] /qualifiers"),
         VMS_Only => False,
         Unixcmd  => new S'("gnatxref"),
         Unixsws  => null,
         Switches => Xref_Switches'Access,
         Params   => new Parameter_Array'(1 => Files_Or_Wildcard),
         Defext   => "ali")
      );

   -------------------
   -- Non_VMS_Usage --
   -------------------

   procedure Non_VMS_Usage is
   begin
      Output_Version;
      New_Line;
      Put_Line ("List of available commands");
      New_Line;

      for C in Command_List'Range loop
         if not Command_List (C).VMS_Only then
            Put ("GNAT " & Command_List (C).Cname.all);
            Set_Col (25);
            Put (Command_List (C).Unixcmd.all);

            declare
               Sws : Argument_List_Access renames Command_List (C).Unixsws;
            begin
               if Sws /= null then
                  for J in Sws'Range loop
                     Put (' ');
                     Put (Sws (J).all);
                  end loop;
               end if;
            end;

            New_Line;
         end if;
      end loop;

      New_Line;
      Put_Line ("Commands FIND, LIST and XREF accept project file " &
                "switches -vPx, -Pprj and -Xnam=val");
      New_Line;
   end Non_VMS_Usage;

   --------------------
   -- VMS_Conversion --
   --------------------

   procedure VMS_Conversion (The_Command : out Command_Type) is
   begin
      Buffer.Init;

      --  First we must preprocess the string form of the command and options
      --  list into the internal form that we use.

      for C in Real_Command_Type loop

         declare
            Command : Item_Ptr := new Command_Item;

            Last_Switch : Item_Ptr;
            --  Last switch in list

         begin
            --  Link new command item into list of commands

            if Last_Command = null then
               Commands := Command;
            else
               Last_Command.Next := Command;
            end if;

            Last_Command := Command;

            --  Fill in fields of new command item

            Command.Name    := Command_List (C).Cname;
            Command.Usage   := Command_List (C).Usage;
            Command.Command := C;

            if Command_List (C).Unixsws = null then
               Command.Unix_String := Command_List (C).Unixcmd;
            else
               declare
                  Cmd  : String (1 .. 5_000);
                  Last : Natural := 0;
                  Sws  : Argument_List_Access := Command_List (C).Unixsws;

               begin
                  Cmd (1 .. Command_List (C).Unixcmd'Length) :=
                    Command_List (C).Unixcmd.all;
                  Last := Command_List (C).Unixcmd'Length;

                  for J in Sws'Range loop
                     Last := Last + 1;
                     Cmd (Last) := ' ';
                     Cmd (Last + 1 .. Last + Sws (J)'Length) :=
                       Sws (J).all;
                     Last := Last + Sws (J)'Length;
                  end loop;

                  Command.Unix_String := new String'(Cmd (1 .. Last));
               end;
            end if;

            Command.Params := Command_List (C).Params;
            Command.Defext := Command_List (C).Defext;

            Validate_Command_Or_Option (Command.Name);

            --  Process the switch list

            for S in Command_List (C).Switches'Range loop
               declare
                  SS : constant String_Ptr := Command_List (C).Switches (S);

                  P  : Natural := SS'First;
                  Sw : Item_Ptr := new Switch_Item;

                  Last_Opt : Item_Ptr;
                  --  Pointer to last option

               begin
                  --  Link new switch item into list of switches

                  if Last_Switch = null then
                     Command.Switches := Sw;
                  else
                     Last_Switch.Next := Sw;
                  end if;

                  Last_Switch := Sw;

                  --  Process switch string, first get name

                  while SS (P) /= ' ' and SS (P) /= '=' loop
                     P := P + 1;
                  end loop;

                  Sw.Name := new String'(SS (SS'First .. P - 1));

                  --  Direct translation case

                  if SS (P) = ' ' then
                     Sw.Translation := T_Direct;
                     Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
                     Validate_Unix_Switch (Sw.Unix_String);

                     if SS (P - 1) = '>' then
                        Sw.Translation := T_Other;

                     elsif SS (P + 1) = '`' then
                        null;

                        --  Create the inverted case (/NO ..)

                     elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
                        Sw := new Switch_Item;
                        Last_Switch.Next := Sw;
                        Last_Switch := Sw;

                        Sw.Name :=
                          new String'("/NO" & SS (SS'First + 1 .. P - 1));
                        Sw.Translation := T_Direct;
                        Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
                        Validate_Unix_Switch (Sw.Unix_String);
                     end if;

                     --  Directories translation case

                  elsif SS (P + 1) = '*' then
                     pragma Assert (SS (SS'Last) = '*');
                     Sw.Translation := T_Directories;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  Directory translation case

                  elsif SS (P + 1) = '%' then
                     pragma Assert (SS (SS'Last) = '%');
                     Sw.Translation := T_Directory;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  File translation case

                  elsif SS (P + 1) = '@' then
                     pragma Assert (SS (SS'Last) = '@');
                     Sw.Translation := T_File;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  No space file translation case

                  elsif SS (P + 1) = '<' then
                     pragma Assert (SS (SS'Last) = '>');
                     Sw.Translation := T_No_Space_File;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  Numeric translation case

                  elsif SS (P + 1) = '#' then
                     pragma Assert (SS (SS'Last) = '#');
                     Sw.Translation := T_Numeric;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  Alphanumerplus translation case

                  elsif SS (P + 1) = '|' then
                     pragma Assert (SS (SS'Last) = '|');
                     Sw.Translation := T_Alphanumplus;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  String translation case

                  elsif SS (P + 1) = '"' then
                     pragma Assert (SS (SS'Last) = '"');
                     Sw.Translation := T_String;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
                     Validate_Unix_Switch (Sw.Unix_String);

                     --  Commands translation case

                  elsif SS (P + 1) = '?' then
                     Sw.Translation := T_Commands;
                     Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));

                     --  Options translation case

                  else
                     Sw.Translation := T_Options;
                     Sw.Unix_String := new String'("");

                     P := P + 1; -- bump past =
                     while P <= SS'Last loop
                        declare
                           Opt : Item_Ptr := new Option_Item;
                           Q   : Natural;

                        begin
                           --  Link new option item into options list

                           if Last_Opt = null then
                              Sw.Options := Opt;
                           else
                              Last_Opt.Next := Opt;
                           end if;

                           Last_Opt := Opt;

                           --  Fill in fields of new option item

                           Q := P;
                           while SS (Q) /= ' ' loop
                              Q := Q + 1;
                           end loop;

                           Opt.Name := new String'(SS (P .. Q - 1));
                           Validate_Command_Or_Option (Opt.Name);

                           P := Q + 1;
                           Q := P;

                           while Q <= SS'Last and then SS (Q) /= ' ' loop
                              Q := Q + 1;
                           end loop;

                           Opt.Unix_String := new String'(SS (P .. Q - 1));
                           Validate_Unix_Switch (Opt.Unix_String);
                           P := Q + 1;
                        end;
                     end loop;
                  end if;
               end;
            end loop;
         end;
      end loop;

      --  If no parameters, give complete list of commands

      if Argument_Count = 0 then
         Output_Version;
         New_Line;
         Put_Line ("List of available commands");
         New_Line;

         while Commands /= null loop
            Put (Commands.Usage.all);
            Set_Col (53);
            Put_Line (Commands.Unix_String.all);
            Commands := Commands.Next;
         end loop;

         raise Normal_Exit;
      end if;

      Arg_Num := 1;

      --  Loop through arguments

      while Arg_Num <= Argument_Count loop

         Process_Argument : declare
            Argv    : String_Access;
            Arg_Idx : Integer;

            function Get_Arg_End
              (Argv    : String;
               Arg_Idx : Integer)
               return    Integer;
            --  Begins looking at Arg_Idx + 1 and returns the index of the
            --  last character before a slash or else the index of the last
            --  character in the string Argv.

            -----------------
            -- Get_Arg_End --
            -----------------

            function Get_Arg_End
              (Argv    : String;
               Arg_Idx : Integer)
              return    Integer
            is
            begin
               for J in Arg_Idx + 1 .. Argv'Last loop
                  if Argv (J) = '/' then
                     return J - 1;
                  end if;
               end loop;

               return Argv'Last;
            end Get_Arg_End;

         --  Start of processing for Process_Argument

         begin
            Argv := new String'(Argument (Arg_Num));
            Arg_Idx := Argv'First;

            <<Tryagain_After_Coalesce>>
               loop
                  declare
                     Next_Arg_Idx : Integer;
                     Arg          : String_Access;

                  begin
                     Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
                     Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));

                     --  The first one must be a command name

                     if Arg_Num = 1 and then Arg_Idx = Argv'First then

                        Command := Matching_Name (Arg.all, Commands);

                        if Command = null then
                           raise Error_Exit;
                        end if;

                        The_Command := Command.Command;

                        --  Give usage information if only command given

                        if Argument_Count = 1 and then Next_Arg_Idx = Argv'Last
                          and then Command.Command /= Standard
                        then
                           Output_Version;
                           New_Line;
                           Put_Line
                             ("List of available qualifiers and options");
                           New_Line;

                           Put (Command.Usage.all);
                           Set_Col (53);
                           Put_Line (Command.Unix_String.all);

                           declare
                              Sw : Item_Ptr := Command.Switches;

                           begin
                              while Sw /= null loop
                                 Put ("   ");
                                 Put (Sw.Name.all);

                                 case Sw.Translation is

                                    when T_Other =>
                                       Set_Col (53);
                                       Put_Line (Sw.Unix_String.all &
                                                 "/<other>");

                                    when T_Direct =>
                                       Set_Col (53);
                                       Put_Line (Sw.Unix_String.all);

                                    when T_Directories =>
                                       Put ("=(direc,direc,..direc)");
                                       Set_Col (53);
                                       Put (Sw.Unix_String.all);
                                       Put (" direc ");
                                       Put (Sw.Unix_String.all);
                                       Put_Line (" direc ...");

                                    when T_Directory =>
                                       Put ("=directory");
                                       Set_Col (53);
                                       Put (Sw.Unix_String.all);

                                       if Sw.Unix_String (Sw.Unix_String'Last)
                                         /= '='
                                       then
                                          Put (' ');
                                       end if;

                                       Put_Line ("directory ");

                                    when T_File | T_No_Space_File =>
                                       Put ("=file");
                                       Set_Col (53);
                                       Put (Sw.Unix_String.all);

                                       if Sw.Translation = T_File
                                         and then Sw.Unix_String
                                                   (Sw.Unix_String'Last)
                                                     /= '='
                                       then
                                          Put (' ');
                                       end if;

                                       Put_Line ("file ");

                                    when T_Numeric =>
                                       Put ("=nnn");
                                       Set_Col (53);

                                       if Sw.Unix_String (Sw.Unix_String'First)
                                         = '`'
                                       then
                                          Put (Sw.Unix_String
                                               (Sw.Unix_String'First + 1
                                                .. Sw.Unix_String'Last));
                                       else
                                          Put (Sw.Unix_String.all);
                                       end if;

                                       Put_Line ("nnn");

                                    when T_Alphanumplus =>
                                       Put ("=xyz");
                                       Set_Col (53);

                                       if Sw.Unix_String (Sw.Unix_String'First)
                                         = '`'
                                       then
                                          Put (Sw.Unix_String
                                               (Sw.Unix_String'First + 1
                                                .. Sw.Unix_String'Last));
                                       else
                                          Put (Sw.Unix_String.all);
                                       end if;

                                       Put_Line ("xyz");

                                    when T_String =>
                                       Put ("=");
                                       Put ('"');
                                       Put ("<string>");
                                       Put ('"');
                                       Set_Col (53);

                                       Put (Sw.Unix_String.all);

                                       if Sw.Unix_String (Sw.Unix_String'Last)
                                         /= '='
                                       then
                                          Put (' ');
                                       end if;

                                       Put ("<string>");
                                       New_Line;

                                    when T_Commands =>
                                       Put (" (switches for ");
                                       Put (Sw.Unix_String
                                            (Sw.Unix_String'First + 7
                                             .. Sw.Unix_String'Last));
                                       Put (')');
                                       Set_Col (53);
                                       Put (Sw.Unix_String
                                            (Sw.Unix_String'First
                                             .. Sw.Unix_String'First + 5));
                                       Put_Line (" switches");

                                    when T_Options =>
                                       declare
                                          Opt : Item_Ptr := Sw.Options;

                                       begin
                                          Put_Line ("=(option,option..)");

                                          while Opt /= null loop
                                             Put ("      ");
                                             Put (Opt.Name.all);

                                             if Opt = Sw.Options then
                                                Put (" (D)");
                                             end if;

                                             Set_Col (53);
                                             Put_Line (Opt.Unix_String.all);
                                             Opt := Opt.Next;
                                          end loop;
                                       end;

                                 end case;

                                 Sw := Sw.Next;
                              end loop;
                           end;

                           raise Normal_Exit;
                        end if;

                        --  Place (Command.Unix_String.all);

                        --  Special handling for internal debugging switch /?

                     elsif Arg.all = "/?" then
                        Display_Command := True;

                        --  Copy -switch unchanged

                     elsif Arg (Arg'First) = '-' then
                        Place (' ');
                        Place (Arg.all);

                        --  Copy quoted switch with quotes stripped

                     elsif Arg (Arg'First) = '"' then
                        if Arg (Arg'Last) /= '"' then
                           Put (Standard_Error, "misquoted argument: ");
                           Put_Line (Standard_Error, Arg.all);
                           Errors := Errors + 1;

                        else
                           Place (' ');
                           Place (Arg (Arg'First + 1 .. Arg'Last - 1));
                        end if;

                        --  Parameter Argument

                     elsif Arg (Arg'First) /= '/'
                       and then Make_Commands_Active = null
                     then
                        Param_Count := Param_Count + 1;

                        if Param_Count <= Command.Params'Length then

                           case Command.Params (Param_Count) is

                              when File | Optional_File =>
                                 declare
                                    Normal_File : String_Access
                                      := To_Canonical_File_Spec (Arg.all);
                                 begin
                                    Place (' ');
                                    Place_Lower (Normal_File.all);

                                    if Is_Extensionless (Normal_File.all)
                                      and then Command.Defext /= "   "
                                    then
                                       Place ('.');
                                       Place (Command.Defext);
                                    end if;
                                 end;

                              when Unlimited_Files =>
                                 declare
                                    Normal_File : String_Access
                                      := To_Canonical_File_Spec (Arg.all);

                                    File_Is_Wild  : Boolean := False;
                                    File_List     : String_Access_List_Access;
                                 begin
                                    for I in Arg'Range loop
                                       if Arg (I) = '*'
                                         or else Arg (I) = '%'
                                       then
                                          File_Is_Wild := True;
                                       end if;
                                    end loop;

                                    if File_Is_Wild then
                                       File_List := To_Canonical_File_List
                                         (Arg.all, False);

                                       for I in File_List.all'Range loop
                                          Place (' ');
                                          Place_Lower (File_List.all (I).all);
                                       end loop;
                                    else
                                       Place (' ');
                                       Place_Lower (Normal_File.all);

                                       if Is_Extensionless (Normal_File.all)
                                         and then Command.Defext /= "   "
                                       then
                                          Place ('.');
                                          Place (Command.Defext);
                                       end if;
                                    end if;

                                    Param_Count := Param_Count - 1;
                                 end;

                              when Other_As_Is =>
                                 Place (' ');
                                 Place (Arg.all);

                              when Unlimited_As_Is =>
                                 Place (' ');
                                 Place (Arg.all);
                                 Param_Count := Param_Count - 1;

                              when Files_Or_Wildcard =>

                                 --  Remove spaces from a comma separated list
                                 --  of file names and adjust control variables
                                 --  accordingly.

                                 while Arg_Num < Argument_Count and then
                                   (Argv (Argv'Last) = ',' xor
                                    Argument (Arg_Num + 1)
                                      (Argument (Arg_Num + 1)'First) = ',')
                                 loop
                                    Argv := new String'
                                           (Argv.all & Argument (Arg_Num + 1));
                                    Arg_Num := Arg_Num + 1;
                                    Arg_Idx := Argv'First;
                                    Next_Arg_Idx :=
                                      Get_Arg_End (Argv.all, Arg_Idx);
                                    Arg := new String'
                                            (Argv (Arg_Idx .. Next_Arg_Idx));
                                 end loop;

                                 --  Parse the comma separated list of VMS
                                 --  filenames and place them on the command
                                 --  line as space separated Unix style
                                 --  filenames. Lower case and add default
                                 --  extension as appropriate.

                                 declare
                                    Arg1_Idx : Integer := Arg'First;

                                    function Get_Arg1_End
                                      (Arg : String; Arg_Idx : Integer)
                                      return Integer;
                                    --  Begins looking at Arg_Idx + 1 and
                                    --  returns the index of the last character
                                    --  before a comma or else the index of the
                                    --  last character in the string Arg.

                                    function Get_Arg1_End
                                      (Arg : String; Arg_Idx : Integer)
                                      return Integer
                                    is
                                    begin
                                       for I in Arg_Idx + 1 .. Arg'Last loop
                                          if Arg (I) = ',' then
                                             return I - 1;
                                          end if;
                                       end loop;

                                       return Arg'Last;
                                    end Get_Arg1_End;

                                 begin
                                    loop
                                       declare
                                          Next_Arg1_Idx : Integer :=
                                            Get_Arg1_End (Arg.all, Arg1_Idx);

                                          Arg1 : String :=
                                            Arg (Arg1_Idx .. Next_Arg1_Idx);

                                          Normal_File : String_Access :=
                                            To_Canonical_File_Spec (Arg1);

                                       begin
                                          Place (' ');
                                          Place_Lower (Normal_File.all);

                                          if Is_Extensionless (Normal_File.all)
                                            and then Command.Defext /= "   "
                                          then
                                             Place ('.');
                                             Place (Command.Defext);
                                          end if;

                                          Arg1_Idx := Next_Arg1_Idx + 1;
                                       end;

                                       exit when Arg1_Idx > Arg'Last;

                                       --  Don't allow two or more commas in
                                       --  a row

                                       if Arg (Arg1_Idx) = ',' then
                                          Arg1_Idx := Arg1_Idx + 1;
                                          if Arg1_Idx > Arg'Last or else
                                            Arg (Arg1_Idx) = ','
                                          then
                                             Put_Line
                                               (Standard_Error,
                                                "Malformed Parameter: " &
                                                Arg.all);
                                             Put (Standard_Error, "usage: ");
                                             Put_Line (Standard_Error,
                                                       Command.Usage.all);
                                             raise Error_Exit;
                                          end if;
                                       end if;

                                    end loop;
                                 end;
                           end case;
                        end if;

                        --  Qualifier argument

                     else
                        declare
                           Sw   : Item_Ptr;
                           SwP  : Natural;
                           P2   : Natural;
                           Endp : Natural := 0; -- avoid warning!
                           Opt  : Item_Ptr;

                        begin
                           SwP := Arg'First;
                           while SwP < Arg'Last
                             and then Arg (SwP + 1) /= '='
                           loop
                              SwP := SwP + 1;
                           end loop;

                           --  At this point, the switch name is in
                           --  Arg (Arg'First..SwP) and if that is not the
                           --  whole switch, then there is an equal sign at
                           --  Arg (SwP + 1) and the rest of Arg is what comes
                           --  after the equal sign.

                           --  If make commands are active, see if we have
                           --  another COMMANDS_TRANSLATION switch belonging
                           --  to gnatmake.

                           if Make_Commands_Active /= null then
                              Sw :=
                                Matching_Name
                                (Arg (Arg'First .. SwP),
                                 Command.Switches,
                                 Quiet => True);

                              if Sw /= null
                                and then Sw.Translation = T_Commands
                              then
                                 null;

                              else
                                 Sw :=
                                   Matching_Name
                                   (Arg (Arg'First .. SwP),
                                    Make_Commands_Active.Switches,
                                    Quiet => False);
                              end if;

                              --  For case of GNAT MAKE or CHOP, if we cannot
                              --  find the switch, then see if it is a
                              --  recognized compiler switch instead, and if
                              --  so process the compiler switch.

                           elsif Command.Name.all = "MAKE"
                             or else Command.Name.all = "CHOP" then
                              Sw :=
                                Matching_Name
                                (Arg (Arg'First .. SwP),
                                 Command.Switches,
                                 Quiet => True);

                              if Sw = null then
                                 Sw :=
                                   Matching_Name
                                   (Arg (Arg'First .. SwP),
                                    Matching_Name
                                      ("COMPILE", Commands).Switches,
                                    Quiet => False);
                              end if;

                              --  For all other cases, just search the relevant
                              --  command.

                           else
                              Sw :=
                                Matching_Name
                                (Arg (Arg'First .. SwP),
                                 Command.Switches,
                                 Quiet => False);
                           end if;

                           if Sw /= null then
                              case Sw.Translation is

                                 when T_Direct =>
                                    Place_Unix_Switches (Sw.Unix_String);
                                    if SwP < Arg'Last
                                      and then Arg (SwP + 1) = '='
                                    then
                                       Put (Standard_Error,
                                            "qualifier options ignored: ");
                                       Put_Line (Standard_Error, Arg.all);
                                    end if;

                                 when T_Directories =>
                                    if SwP + 1 > Arg'Last then
                                       Put (Standard_Error,
                                            "missing directories for: ");
                                       Put_Line (Standard_Error, Arg.all);
                                       Errors := Errors + 1;

                                    elsif Arg (SwP + 2) /= '(' then
                                       SwP := SwP + 2;
                                       Endp := Arg'Last;

                                    elsif Arg (Arg'Last) /= ')' then

                                       --  Remove spaces from a comma separated
                                       --  list of file names and adjust
                                       --  control variables accordingly.

                                       if Arg_Num < Argument_Count and then
                                         (Argv (Argv'Last) = ',' xor
                                          Argument (Arg_Num + 1)
                                          (Argument (Arg_Num + 1)'First) = ',')
                                       then
                                          Argv :=
                                            new String'(Argv.all
                                                        & Argument
                                                           (Arg_Num + 1));
                                          Arg_Num := Arg_Num + 1;
                                          Arg_Idx := Argv'First;
                                          Next_Arg_Idx
                                            := Get_Arg_End (Argv.all, Arg_Idx);
                                          Arg := new String'
                                            (Argv (Arg_Idx .. Next_Arg_Idx));
                                          goto Tryagain_After_Coalesce;
                                       end if;

                                       Put (Standard_Error,
                                            "incorrectly parenthesized " &
                                            "or malformed argument: ");
                                       Put_Line (Standard_Error, Arg.all);
                                       Errors := Errors + 1;

                                    else
                                       SwP := SwP + 3;
                                       Endp := Arg'Last - 1;
                                    end if;

                                    while SwP <= Endp loop
                                       declare
                                          Dir_Is_Wild       : Boolean := False;
                                          Dir_Maybe_Is_Wild : Boolean := False;
                                          Dir_List : String_Access_List_Access;
                                       begin
                                          P2 := SwP;

                                          while P2 < Endp
                                            and then Arg (P2 + 1) /= ','
                                          loop

                                             --  A wildcard directory spec on
                                             --  VMS will contain either * or
                                             --  % or ...

                                             if Arg (P2) = '*' then
                                                Dir_Is_Wild := True;

                                             elsif Arg (P2) = '%' then
                                                Dir_Is_Wild := True;

                                             elsif Dir_Maybe_Is_Wild
                                               and then Arg (P2) = '.'
                                               and then Arg (P2 + 1) = '.'
                                             then
                                                Dir_Is_Wild := True;
                                                Dir_Maybe_Is_Wild := False;

                                             elsif Dir_Maybe_Is_Wild then
                                                Dir_Maybe_Is_Wild := False;

                                             elsif Arg (P2) = '.'
                                               and then Arg (P2 + 1) = '.'
                                             then
                                                Dir_Maybe_Is_Wild := True;

                                             end if;

                                             P2 := P2 + 1;
                                          end loop;

                                          if (Dir_Is_Wild) then
                                             Dir_List := To_Canonical_File_List
                                               (Arg (SwP .. P2), True);

                                             for I in Dir_List.all'Range loop
                                                Place_Unix_Switches
                                                  (Sw.Unix_String);
                                                Place_Lower
                                                  (Dir_List.all (I).all);
                                             end loop;
                                          else
                                             Place_Unix_Switches
                                               (Sw.Unix_String);
                                             Place_Lower
                                               (To_Canonical_Dir_Spec
                                                (Arg (SwP .. P2), False).all);
                                          end if;

                                          SwP := P2 + 2;
                                       end;
                                    end loop;

                                 when T_Directory =>
                                    if SwP + 1 > Arg'Last then
                                       Put (Standard_Error,
                                            "missing directory for: ");
                                       Put_Line (Standard_Error, Arg.all);
                                       Errors := Errors + 1;

                                    else
                                       Place_Unix_Switches (Sw.Unix_String);

                                       --  Some switches end in "=". No space
                                       --  here

                                       if Sw.Unix_String
                                         (Sw.Unix_String'Last) /= '='
                                       then
                                          Place (' ');
                                       end if;

                                       Place_Lower
                                         (To_Canonical_Dir_Spec
                                          (Arg (SwP + 2 .. Arg'Last),
                                           False).all);
                                    end if;

                                 when T_File | T_No_Space_File =>
                                    if SwP + 1 > Arg'Last then
                                       Put (Standard_Error,
                                            "missing file for: ");
                                       Put_Line (Standard_Error, Arg.all);
                                       Errors := Errors + 1;

                                    else
                                       Place_Unix_Switches (Sw.Unix_String);

                                       --  Some switches end in "=". No space
                                       --  here.

                                       if Sw.Translation = T_File
                                         and then Sw.Unix_String
                                                   (Sw.Unix_String'Last) /= '='
                                       then
                                          Place (' ');
                                       end if;

                                       Place_Lower
                                         (To_Canonical_File_Spec
                                          (Arg (SwP + 2 .. Arg'Last)).all);
                                    end if;

                                 when T_Numeric =>
                                    if
                                      OK_Integer (Arg (SwP + 2 .. Arg'Last))
                                    then
                                       Place_Unix_Switches (Sw.Unix_String);
                                       Place (Arg (SwP + 2 .. Arg'Last));

                                    else
                                       Put (Standard_Error, "argument for ");
                                       Put (Standard_Error, Sw.Name.all);
                                       Put_Line
                                         (Standard_Error, " must be numeric");
                                       Errors := Errors + 1;
                                    end if;

                                 when T_Alphanumplus =>
                                    if
                                      OK_Alphanumerplus
                                        (Arg (SwP + 2 .. Arg'Last))
                                    then
                                       Place_Unix_Switches (Sw.Unix_String);
                                       Place (Arg (SwP + 2 .. Arg'Last));

                                    else
                                       Put (Standard_Error, "argument for ");
                                       Put (Standard_Error, Sw.Name.all);
                                       Put_Line (Standard_Error,
                                                 " must be alphanumeric");
                                       Errors := Errors + 1;
                                    end if;

                                 when T_String =>

                                    --  A String value must be extended to the
                                    --  end of the Argv, otherwise strings like
                                    --  "foo/bar" get split at the slash.
                                    --
                                    --  The begining and ending of the string
                                    --  are flagged with embedded nulls which
                                    --  are removed when building the Spawn
                                    --  call. Nulls are use because they won't
                                    --  show up in a /? output. Quotes aren't
                                    --  used because that would make it
                                    --  difficult to embed them.

                                    Place_Unix_Switches (Sw.Unix_String);
                                    if Next_Arg_Idx /= Argv'Last then
                                       Next_Arg_Idx := Argv'Last;
                                       Arg := new String'
                                         (Argv (Arg_Idx .. Next_Arg_Idx));

                                       SwP := Arg'First;
                                       while SwP < Arg'Last and then
                                         Arg (SwP + 1) /= '=' loop
                                          SwP := SwP + 1;
                                       end loop;
                                    end if;
                                    Place (ASCII.NUL);
                                    Place (Arg (SwP + 2 .. Arg'Last));
                                    Place (ASCII.NUL);

                                 when T_Commands =>

                                    --  Output -largs/-bargs/-cargs

                                    Place (' ');
                                    Place (Sw.Unix_String
                                           (Sw.Unix_String'First ..
                                            Sw.Unix_String'First + 5));

                                    --  Set source of new commands, also
                                    --  setting this non-null indicates that
                                    --  we are in the special commands mode
                                    --  for processing the -xargs case.

                                    Make_Commands_Active :=
                                      Matching_Name
                                      (Sw.Unix_String
                                       (Sw.Unix_String'First + 7 ..
                                        Sw.Unix_String'Last),
                                       Commands);

                                 when T_Options =>
                                    if SwP + 1 > Arg'Last then
                                       Place_Unix_Switches
                                         (Sw.Options.Unix_String);
                                       SwP := Endp + 1;

                                    elsif Arg (SwP + 2) /= '(' then
                                       SwP := SwP + 2;
                                       Endp := Arg'Last;

                                    elsif Arg (Arg'Last) /= ')' then
                                       Put
                                         (Standard_Error,
                                          "incorrectly parenthesized " &
                                          "argument: ");
                                       Put_Line (Standard_Error, Arg.all);
                                       Errors := Errors + 1;
                                       SwP := Endp + 1;

                                    else
                                       SwP := SwP + 3;
                                       Endp := Arg'Last - 1;
                                    end if;

                                    while SwP <= Endp loop
                                       P2 := SwP;

                                       while P2 < Endp
                                         and then Arg (P2 + 1) /= ','
                                       loop
                                          P2 := P2 + 1;
                                       end loop;

                                       --  Option name is in Arg (SwP .. P2)

                                       Opt := Matching_Name (Arg (SwP .. P2),
                                                             Sw.Options);

                                       if Opt /= null then
                                          Place_Unix_Switches
                                            (Opt.Unix_String);
                                       end if;

                                       SwP := P2 + 2;
                                    end loop;

                                 when T_Other =>
                                    Place_Unix_Switches
                                      (new String'(Sw.Unix_String.all &
                                                   Arg.all));

                              end case;
                           end if;
                        end;
                     end if;

                     Arg_Idx := Next_Arg_Idx + 1;
                  end;

                  exit when Arg_Idx > Argv'Last;

               end loop;
         end Process_Argument;

         Arg_Num := Arg_Num + 1;
      end loop;

      if Display_Command then
         Put (Standard_Error, "generated command -->");
         Put (Standard_Error, Command_List (The_Command).Unixcmd.all);

         if Command_List (The_Command).Unixsws /= null then
            for J in Command_List (The_Command).Unixsws'Range loop
               Put (Standard_Error, " ");
               Put (Standard_Error,
                    Command_List (The_Command).Unixsws (J).all);
            end loop;
         end if;

         Put (Standard_Error, " ");
         Put (Standard_Error, String (Buffer.Table (1 .. Buffer.Last)));
         Put (Standard_Error, "<--");
         New_Line (Standard_Error);
         raise Normal_Exit;
      end if;

      --  Gross error checking that the number of parameters is correct.
      --  Not applicable to Unlimited_Files parameters.

      if (Param_Count = Command.Params'Length - 1
            and then Command.Params (Param_Count + 1) = Unlimited_Files)
        or else Param_Count <= Command.Params'Length
      then
         null;

      else
         Put_Line (Standard_Error,
                   "Parameter count of "
                   & Integer'Image (Param_Count)
                   & " not equal to expected "
                   & Integer'Image (Command.Params'Length));
         Put (Standard_Error, "usage: ");
         Put_Line (Standard_Error, Command.Usage.all);
         Errors := Errors + 1;
      end if;

      if Errors > 0 then
         raise Error_Exit;
      else
         --  Prepare arguments for a call to spawn, filtering out
         --  embedded nulls place there to delineate strings.

         declare
            P1, P2     : Natural;
            Inside_Nul : Boolean := False;
            Arg        : String (1 .. 1024);
            Arg_Ctr    : Natural;

         begin
            P1 := 1;

            while P1 <= Buffer.Last and then Buffer.Table (P1) = ' ' loop
               P1 := P1 + 1;
            end loop;

            Arg_Ctr := 1;
            Arg (Arg_Ctr) := Buffer.Table (P1);

            while P1 <= Buffer.Last loop

               if Buffer.Table (P1) = ASCII.NUL then
                  if Inside_Nul then
                     Inside_Nul := False;
                  else
                     Inside_Nul := True;
                  end if;
               end if;

               if Buffer.Table (P1) = ' ' and then not Inside_Nul then
                  P1 := P1 + 1;
                  Arg_Ctr := Arg_Ctr + 1;
                  Arg (Arg_Ctr) := Buffer.Table (P1);

               else
                  Last_Switches.Increment_Last;
                  P2 := P1;

                  while P2 < Buffer.Last
                    and then (Buffer.Table (P2 + 1) /= ' ' or else
                              Inside_Nul)
                  loop
                     P2 := P2 + 1;
                     Arg_Ctr := Arg_Ctr + 1;
                     Arg (Arg_Ctr) := Buffer.Table (P2);
                     if Buffer.Table (P2) = ASCII.NUL then
                        Arg_Ctr := Arg_Ctr - 1;
                        if Inside_Nul then
                           Inside_Nul := False;
                        else
                           Inside_Nul := True;
                        end if;
                     end if;
                  end loop;

                  Last_Switches.Table (Last_Switches.Last) :=
                    new String'(String (Arg (1 .. Arg_Ctr)));
                  P1 := P2 + 2;
                  Arg_Ctr := 1;
                  Arg (Arg_Ctr) := Buffer.Table (P1);
               end if;
            end loop;
         end;
      end if;
   end VMS_Conversion;

   -------------------------------------
   -- Start of processing for GNATCmd --
   -------------------------------------

begin
   --  Initializations

   Namet.Initialize;
   Csets.Initialize;

   Snames.Initialize;

   Prj.Initialize;

   Last_Switches.Init;
   Last_Switches.Set_Last (0);

   First_Switches.Init;
   First_Switches.Set_Last (0);

   --  If on VMS, or if VMS emulation is on, convert VMS style /qualifiers,
   --  filenames and pathnames to Unix style.

   if Hostparm.OpenVMS
     or else To_Lower (Getenv ("EMULATE_VMS").all) = "true"
   then
      VMS_Conversion (The_Command);

   --  If not on VMS, scan the command line directly

   else
      if Argument_Count = 0 then
         Non_VMS_Usage;
         return;
      else
         begin
            if Argument_Count > 1 and then Argument (1) = "-v" then
               Opt.Verbose_Mode := True;
               Command_Arg := 2;
            end if;

            The_Command := Real_Command_Type'Value (Argument (Command_Arg));

            if Command_List (The_Command).VMS_Only then
               Non_VMS_Usage;
               Fail ("Command """ & Command_List (The_Command).Cname.all &
                     """ can only be used on VMS");
            end if;
         exception
            when Constraint_Error =>

               --  Check if it is an alternate command
               declare
                  Alternate : Alternate_Command;

               begin
                  Alternate := Alternate_Command'Value
                                              (Argument (Command_Arg));
                  The_Command := Corresponding_To (Alternate);

               exception
                  when Constraint_Error =>
                     Non_VMS_Usage;
                     Fail ("Unknown command: " & Argument (Command_Arg));
               end;
         end;

         for Arg in Command_Arg + 1 .. Argument_Count loop
            Last_Switches.Increment_Last;
            Last_Switches.Table (Last_Switches.Last) :=
              new String'(Argument (Arg));
         end loop;
      end if;
   end if;

   declare
      Program : constant String :=
        Program_Name (Command_List (The_Command).Unixcmd.all).all;

      Exec_Path : String_Access;

   begin
      --  Locate the executable for the command

      Exec_Path := Locate_Exec_On_Path (Program);

      if Exec_Path = null then
         Put_Line (Standard_Error, "Couldn't locate " & Program);
         raise Error_Exit;
      end if;

      --  If there are switches for the executable, put them as first switches

      if Command_List (The_Command).Unixsws /= null then
         for J in Command_List (The_Command).Unixsws'Range loop
            First_Switches.Increment_Last;
            First_Switches.Table (First_Switches.Last) :=
              Command_List (The_Command).Unixsws (J);
         end loop;
      end if;

      --  For BIND, FIND, LINK, LIST and XREF, look for project file related
      --  switches.

      if The_Command = Bind
        or else The_Command = Find
        or else The_Command = Link
        or else The_Command = List
        or else The_Command = Xref
      then
         case The_Command is
            when Bind =>
               Tool_Package_Name := Name_Binder;
            when Find =>
               Tool_Package_Name := Name_Finder;
            when Link =>
               Tool_Package_Name := Name_Linker;
            when List =>
               Tool_Package_Name := Name_Gnatls;
            when Xref =>
               Tool_Package_Name := Name_Cross_Reference;
            when others =>
               null;
         end case;

         declare
            Arg_Num : Positive := 1;
            Argv    : String_Access;

            procedure Remove_Switch (Num : Positive);
            --  Remove a project related switch from table Last_Switches

            -------------------
            -- Remove_Switch --
            -------------------

            procedure Remove_Switch (Num : Positive) is
            begin
               Last_Switches.Table (Num .. Last_Switches.Last - 1) :=
                 Last_Switches.Table (Num + 1 .. Last_Switches.Last);
               Last_Switches.Decrement_Last;
            end Remove_Switch;

         --  Start of processing for ??? (need block name here)

         begin
            while Arg_Num <= Last_Switches.Last loop
               Argv := Last_Switches.Table (Arg_Num);

               if Argv (Argv'First) = '-' then
                  if Argv'Length = 1 then
                     Fail ("switch character cannot be followed by a blank");
                  end if;

                  --  The two style project files (-p and -P) cannot be used
                  --  together

                  if (The_Command = Find or else The_Command = Xref)
                    and then Argv (2) = 'p'
                  then
                     Old_Project_File_Used := True;
                     if Project_File /= null then
                        Fail ("-P and -p cannot be used together");
                     end if;
                  end if;

                  --  -vPx  Specify verbosity while parsing project files

                  if Argv'Length = 4
                    and then Argv (Argv'First + 1 .. Argv'First + 2) = "vP"
                  then
                     case Argv (Argv'Last) is
                        when '0' =>
                           Current_Verbosity := Prj.Default;
                        when '1' =>
                           Current_Verbosity := Prj.Medium;
                        when '2' =>
                           Current_Verbosity := Prj.High;
                        when others =>
                           Fail ("Invalid switch: " & Argv.all);
                     end case;

                     Remove_Switch (Arg_Num);

                  --  -Pproject_file  Specify project file to be used

                  elsif Argv'Length >= 3
                    and then Argv (Argv'First + 1) = 'P'
                  then

                     --  Only one -P switch can be used

                     if Project_File /= null then
                        Fail (Argv.all &
                              ": second project file forbidden (first is """ &
                              Project_File.all & """)");

                     --  The two style project files (-p and -P) cannot be
                     --  used together.

                     elsif Old_Project_File_Used then
                        Fail ("-p and -P cannot be used together");

                     else
                        Project_File :=
                          new String'(Argv (Argv'First + 2 .. Argv'Last));
                     end if;

                     Remove_Switch (Arg_Num);

                  --  -Xexternal=value Specify an external reference to be
                  --                   used in project files

                  elsif Argv'Length >= 5
                    and then Argv (Argv'First + 1) = 'X'
                  then
                     declare
                        Equal_Pos : constant Natural :=
                          Index ('=', Argv (Argv'First + 2 .. Argv'Last));
                     begin
                        if Equal_Pos >= Argv'First + 3 and then
                          Equal_Pos /= Argv'Last then
                           Add (External_Name =>
                                  Argv (Argv'First + 2 .. Equal_Pos - 1),
                                Value => Argv (Equal_Pos + 1 .. Argv'Last));
                        else
                           Fail (Argv.all &
                                 " is not a valid external assignment.");
                        end if;
                     end;

                     Remove_Switch (Arg_Num);

                  else
                     Arg_Num := Arg_Num + 1;
                  end if;

               else
                  Arg_Num := Arg_Num + 1;
               end if;
            end loop;
         end;
      end if;

      --  If there is a project file specified, parse it, get the switches
      --  for the tool and setup PATH environment variables.

      if Project_File /= null then
         Prj.Pars.Set_Verbosity (To => Current_Verbosity);

         Prj.Pars.Parse
           (Project           => Project,
            Project_File_Name => Project_File.all);

         if Project = Prj.No_Project then
            Fail ("""" & Project_File.all & """ processing failed");
         end if;

         --  Check if a package with the name of the tool is in the project
         --  file and if there is one, get the switches, if any, and scan them.

         declare
            Data : Prj.Project_Data := Prj.Projects.Table (Project);
            Pkg  : Prj.Package_Id :=
                     Prj.Util.Value_Of
                       (Name        => Tool_Package_Name,
                        In_Packages => Data.Decl.Packages);

            Element : Package_Element;

            Default_Switches_Array : Array_Element_Id;

            The_Switches : Prj.Variable_Value;
            Current      : Prj.String_List_Id;
            The_String   : String_Element;

         begin
            if Pkg /= No_Package then
               Element := Packages.Table (Pkg);

               --  Packages Gnatls has a single attribute Switches, that is
               --  not an associative array.

               if The_Command = List then
                  The_Switches :=
                    Prj.Util.Value_Of
                    (Variable_Name => Snames.Name_Switches,
                     In_Variables => Element.Decl.Attributes);

               --  Packages Binder (for gnatbind), Cross_Reference (for
               --  gnatxref), Linker (for gnatlink) and Finder
               --  (for gnatfind) have an attributed Default_Switches,
               --  an associative array, indexed by the name of the
               --  programming language.
               else
                  Default_Switches_Array :=
                    Prj.Util.Value_Of
                    (Name => Name_Default_Switches,
                     In_Arrays => Packages.Table (Pkg).Decl.Arrays);
                  The_Switches := Prj.Util.Value_Of
                    (Index => Name_Ada,
                     In_Array => Default_Switches_Array);

               end if;

               --  If there are switches specified in the package of the
               --  project file corresponding to the tool, scan them.

               case The_Switches.Kind is
                  when Prj.Undefined =>
                     null;

                  when Prj.Single =>
                     if String_Length (The_Switches.Value) > 0 then
                        String_To_Name_Buffer (The_Switches.Value);
                        First_Switches.Increment_Last;
                        First_Switches.Table (First_Switches.Last) :=
                          new String'(Name_Buffer (1 .. Name_Len));
                     end if;

                  when Prj.List =>
                     Current := The_Switches.Values;
                     while Current /= Prj.Nil_String loop
                        The_String := String_Elements.Table (Current);

                        if String_Length (The_String.Value) > 0 then
                           String_To_Name_Buffer (The_String.Value);
                           First_Switches.Increment_Last;
                           First_Switches.Table (First_Switches.Last) :=
                             new String'(Name_Buffer (1 .. Name_Len));
                        end if;

                        Current := The_String.Next;
                     end loop;
               end case;
            end if;
         end;

         --  Set up the environment variables ADA_INCLUDE_PATH and
         --  ADA_OBJECTS_PATH.

         Setenv
           (Name  => Ada_Include_Path,
            Value => Prj.Env.Ada_Include_Path (Project).all);
         Setenv
           (Name  => Ada_Objects_Path,
            Value => Prj.Env.Ada_Objects_Path
            (Project, Including_Libraries => False).all);

         if The_Command = Bind or else The_Command = Link then
            Change_Dir
              (Get_Name_String
                 (Projects.Table (Project).Object_Directory));
         end if;

         if The_Command = Link then

            --  Add the default search directories, to be able to find
            --  libgnat in call to MLib.Utl.Lib_Directory.

            Add_Default_Search_Dirs;

            declare
               There_Are_Libraries  : Boolean := False;

            begin
               --  Check if there are library project files

               if MLib.Tgt.Libraries_Are_Supported then
                  Set_Libraries (Project, There_Are_Libraries);
               end if;

               --  If there are, add the necessary additional switches

               if There_Are_Libraries then

                  --  Add -L<lib_dir> -lgnarl -lgnat -Wl,-rpath,<lib_dir>

                  Last_Switches.Increment_Last;
                  Last_Switches.Table (Last_Switches.Last) :=
                    new String'("-L" & MLib.Utl.Lib_Directory);
                  Last_Switches.Increment_Last;
                  Last_Switches.Table (Last_Switches.Last) :=
                    new String'("-lgnarl");
                  Last_Switches.Increment_Last;
                  Last_Switches.Table (Last_Switches.Last) :=
                    new String'("-lgnat");

                  declare
                     Option : constant String_Access :=
                       MLib.Tgt.Linker_Library_Path_Option
                       (MLib.Utl.Lib_Directory);

                  begin
                     if Option /= null then
                        Last_Switches.Increment_Last;
                        Last_Switches.Table (Last_Switches.Last) :=
                          Option;
                     end if;
                  end;
               end if;
            end;
         end if;
      end if;

      --  Gather all the arguments and invoke the executable

      declare
         The_Args : Argument_List
           (1 .. First_Switches.Last + Last_Switches.Last);
         Arg_Num : Natural := 0;
      begin
         for J in 1 .. First_Switches.Last loop
            Arg_Num := Arg_Num + 1;
            The_Args (Arg_Num) := First_Switches.Table (J);
         end loop;

         for J in 1 .. Last_Switches.Last loop
            Arg_Num := Arg_Num + 1;
            The_Args (Arg_Num) := Last_Switches.Table (J);
         end loop;

         if Opt.Verbose_Mode then
            Output.Write_Str (Exec_Path.all);

            for Arg in The_Args'Range loop
               Output.Write_Char (' ');
               Output.Write_Str (The_Args (Arg).all);
            end loop;

            Output.Write_Eol;
         end if;

         My_Exit_Status
           := Exit_Status (Spawn (Exec_Path.all, The_Args));
         raise Normal_Exit;
      end;
   end;

exception
   when Error_Exit =>
      Set_Exit_Status (Failure);

   when Normal_Exit =>
      Set_Exit_Status (My_Exit_Status);

end GNATCmd;