------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                             P R J . N M S C                              --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                            $Revision: 1.7.10.1 $
--                                                                          --
--          Copyright (C) 2000-2001 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 Ada.Characters.Handling;    use Ada.Characters.Handling;
with Ada.Strings;                use Ada.Strings;
with Ada.Strings.Fixed;          use Ada.Strings.Fixed;
with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants;
with Errout;                     use Errout;
with GNAT.Case_Util;             use GNAT.Case_Util;
with GNAT.Directory_Operations;  use GNAT.Directory_Operations;
with GNAT.OS_Lib;                use GNAT.OS_Lib;
with MLib.Tgt;
with Namet;                      use Namet;
with Osint;                      use Osint;
with Output;                     use Output;
with Prj.Com;                    use Prj.Com;
with Prj.Util;                   use Prj.Util;
with Snames;                     use Snames;
with Stringt;                    use Stringt;
with Types;                      use Types;

package body Prj.Nmsc is

   Dir_Sep      : Character renames GNAT.OS_Lib.Directory_Separator;

   Error_Report : Put_Line_Access := null;

   procedure Check_Ada_Naming_Scheme (Naming : Naming_Data);
   --  Check that the package Naming is correct.

   procedure Check_Ada_Name
     (Name : Name_Id;
      Unit : out Name_Id);
   --  Check that a name is a valid Ada unit name.

   procedure Error_Msg (Msg : String; Flag_Location : Source_Ptr);
   --  Output an error message. If Error_Report is null, simply call
   --  Errout.Error_Msg. Otherwise, disregard Flag_Location and use
   --  Error_Report.

   function Get_Name_String (S : String_Id) return String;
   --  Get the string from a String_Id

   procedure Get_Unit
     (File_Name    : Name_Id;
      Naming       : Naming_Data;
      Unit_Name    : out Name_Id;
      Unit_Kind    : out Spec_Or_Body;
      Needs_Pragma : out Boolean);
   --  Find out, from a file name, the unit name, the unit kind and if a
   --  specific SFN pragma is needed. If the file name corresponds to no
   --  unit, then Unit_Name will be No_Name.

   function Is_Illegal_Append (This : String) return Boolean;
   --  Returns True if the string This cannot be used as
   --  a Specification_Append, a Body_Append or a Separate_Append.

   procedure Record_Source
     (File_Name        : Name_Id;
      Path_Name        : Name_Id;
      Project          : Project_Id;
      Data             : in out Project_Data;
      Location         : Source_Ptr;
      Current_Source   : in out String_List_Id);
   --  Put a unit in the list of units of a project, if the file name
   --  corresponds to a valid unit name.

   procedure Show_Source_Dirs (Project : Project_Id);
   --  List all the source directories of a project.

   function Locate_Directory
     (Name   : Name_Id;
      Parent : Name_Id)
      return   Name_Id;
   --  Locate a directory.
   --  Returns No_Name if directory does not exist.

   function Path_Name_Of
     (File_Name : String_Id;
      Directory : Name_Id)
      return      String;
   --  Returns the path name of a (non project) file.
   --  Returns an empty string if file cannot be found.

   function Path_Name_Of
     (File_Name : String_Id;
      Directory : String_Id)
      return      String;
   --  Same as above except that Directory is a String_Id instead
   --  of a Name_Id.

   ---------------
   -- Ada_Check --
   ---------------

   procedure Ada_Check
     (Project      : Project_Id;
      Report_Error : Put_Line_Access)
   is
      Data         : Project_Data;
      Languages    : Variable_Value := Nil_Variable_Value;

      procedure Check_Unit_Names (List : Array_Element_Id);
      --  Check that a list of unit names contains only valid names.

      procedure Find_Sources;
      --  Find all the sources in all of the source directories
      --  of a project.

      procedure Get_Path_Name_And_Record_Source
        (File_Name        : String;
         Location         : Source_Ptr;
         Current_Source   : in out String_List_Id);
      --  Find the path name of a source in the source directories and
      --  record the source, if found.

      procedure Get_Sources_From_File
        (Path     : String;
         Location : Source_Ptr);
      --  Get the sources of a project from a text file

      ----------------------
      -- Check_Unit_Names --
      ----------------------

      procedure Check_Unit_Names (List : Array_Element_Id) is
         Current   : Array_Element_Id := List;
         Element   : Array_Element;
         Unit_Name : Name_Id;

      begin
         --  Loop through elements of the string list

         while Current /= No_Array_Element loop
            Element := Array_Elements.Table (Current);

            --  Check that it contains a valid unit name

            Check_Ada_Name (Element.Index, Unit_Name);

            if Unit_Name = No_Name then
               Error_Msg_Name_1 := Element.Index;
               Error_Msg
                 ("{ is not a valid unit name.",
                  Element.Value.Location);

            else
               if Current_Verbosity = High then
                  Write_Str ("   Body_Part (""");
                  Write_Str (Get_Name_String (Unit_Name));
                  Write_Line (""")");
               end if;

               Element.Index := Unit_Name;
               Array_Elements.Table (Current) := Element;
            end if;

            Current := Element.Next;
         end loop;
      end Check_Unit_Names;

      ------------------
      -- Find_Sources --
      ------------------

      procedure Find_Sources is
         Source_Dir     : String_List_Id := Data.Source_Dirs;
         Element        : String_Element;
         Dir            : Dir_Type;
         Current_Source : String_List_Id := Nil_String;

      begin
         if Current_Verbosity = High then
            Write_Line ("Looking for sources:");
         end if;

         --  For each subdirectory

         while Source_Dir /= Nil_String loop
            begin
               Element := String_Elements.Table (Source_Dir);
               if Element.Value /= No_String then
                  declare
                     Source_Directory : String
                       (1 .. Integer (String_Length (Element.Value)));
                  begin
                     String_To_Name_Buffer (Element.Value);
                     Source_Directory := Name_Buffer (1 .. Name_Len);
                     if Current_Verbosity = High then
                        Write_Str ("Source_Dir = ");
                        Write_Line (Source_Directory);
                     end if;

                     --  We look to every entry in the source directory

                     Open (Dir, Source_Directory);

                     loop
                        Read (Dir, Name_Buffer, Name_Len);

                        if Current_Verbosity = High then
                           Write_Str  ("   Checking ");
                           Write_Line (Name_Buffer (1 .. Name_Len));
                        end if;

                        exit when Name_Len = 0;

                        declare
                           Path_Access : constant GNAT.OS_Lib.String_Access :=
                                           Locate_Regular_File
                                             (Name_Buffer (1 .. Name_Len),
                                              Source_Directory);

                           File_Name : Name_Id;
                           Path_Name : Name_Id;

                        begin
                           --  If it is a regular file

                           if Path_Access /= null then
                              File_Name := Name_Find;
                              Name_Len := Path_Access'Length;
                              Name_Buffer (1 .. Name_Len) := Path_Access.all;
                              Path_Name := Name_Find;

                              --  We attempt to register it as a source.
                              --  However, there is no error if the file
                              --  does not contain a valid source.
                              --  But there is an error if we have a
                              --  duplicate unit name.

                              Record_Source
                                (File_Name        => File_Name,
                                 Path_Name        => Path_Name,
                                 Project          => Project,
                                 Data             => Data,
                                 Location         => No_Location,
                                 Current_Source   => Current_Source);

                           else
                              if Current_Verbosity = High then
                                 Write_Line
                                   ("      Not a regular file.");
                              end if;
                           end if;
                        end;
                     end loop;

                     Close (Dir);
                  end;
               end if;

            exception
               when Directory_Error =>
                  null;
            end;

            Source_Dir := Element.Next;
         end loop;

         if Current_Verbosity = High then
            Write_Line ("end Looking for sources.");
         end if;

         --  If we have looked for sources and found none, then
         --  it is an error. If a project is not supposed to contain
         --  any source, then we never call Find_Sources.

         if Current_Source = Nil_String then
            Error_Msg ("there are no sources in this project",
                       Data.Location);
         end if;
      end Find_Sources;

      -------------------------------------
      -- Get_Path_Name_And_Record_Source --
      -------------------------------------

      procedure Get_Path_Name_And_Record_Source
        (File_Name        : String;
         Location         : Source_Ptr;
         Current_Source   : in out String_List_Id)
      is
         Source_Dir : String_List_Id := Data.Source_Dirs;
         Element    : String_Element;
         Path_Name  : GNAT.OS_Lib.String_Access;
         Found      : Boolean := False;
         File       : Name_Id;

      begin
         if Current_Verbosity = High then
            Write_Str  ("   Checking """);
            Write_Str  (File_Name);
            Write_Line (""".");
         end if;

         --  We look in all source directories for this file name

         while Source_Dir /= Nil_String loop
            Element := String_Elements.Table (Source_Dir);

            if Current_Verbosity = High then
               Write_Str ("      """);
               Write_Str (Get_Name_String (Element.Value));
               Write_Str (""": ");
            end if;

            Path_Name :=
              Locate_Regular_File
              (File_Name,
               Get_Name_String (Element.Value));

            if Path_Name /= null then
               if Current_Verbosity = High then
                  Write_Line ("OK");
               end if;

               Name_Len := File_Name'Length;
               Name_Buffer (1 .. Name_Len) := File_Name;
               File := Name_Find;
               Name_Len := Path_Name'Length;
               Name_Buffer (1 .. Name_Len) := Path_Name.all;

               --  Register the source. Report an error if the file does not
               --  correspond to a source.

               Record_Source
                 (File_Name        => File,
                  Path_Name        => Name_Find,
                  Project          => Project,
                  Data             => Data,
                  Location         => Location,
                  Current_Source   => Current_Source);
               Found := True;
               exit;

            else
               if Current_Verbosity = High then
                  Write_Line ("No");
               end if;

               Source_Dir := Element.Next;
            end if;
         end loop;

      end Get_Path_Name_And_Record_Source;

      ---------------------------
      -- Get_Sources_From_File --
      ---------------------------

      procedure Get_Sources_From_File
        (Path     : String;
         Location : Source_Ptr)
      is
         File           : Prj.Util.Text_File;
         Line           : String (1 .. 250);
         Last           : Natural;
         Current_Source : String_List_Id := Nil_String;

         Nmb_Errors : constant Nat := Errors_Detected;

      begin
         if Current_Verbosity = High then
            Write_Str  ("Opening """);
            Write_Str  (Path);
            Write_Line (""".");
         end if;

         --  We open the file

         Prj.Util.Open (File, Path);

         if not Prj.Util.Is_Valid (File) then
            Error_Msg ("file does not exist", Location);
         else
            while not Prj.Util.End_Of_File (File) loop
               Prj.Util.Get_Line (File, Line, Last);

               --  If the line is not empty and does not start with "--",
               --  then it must contains a file name.

               if Last /= 0
                 and then (Last = 1 or else Line (1 .. 2) /= "--")
               then
                  Get_Path_Name_And_Record_Source
                    (File_Name => Line (1 .. Last),
                     Location => Location,
                     Current_Source => Current_Source);
                  exit when Nmb_Errors /= Errors_Detected;
               end if;
            end loop;

            Prj.Util.Close (File);

         end if;

         --  We should have found at least one source.
         --  If not, report an error.

         if Current_Source = Nil_String then
            Error_Msg ("this project has no source", Location);
         end if;
      end Get_Sources_From_File;

      --  Start of processing for Ada_Check

   begin
      Language_Independent_Check (Project, Report_Error);

      Error_Report := Report_Error;

      Data      := Projects.Table (Project);
      Languages := Prj.Util.Value_Of (Name_Languages, Data.Decl.Attributes);

      Data.Naming.Current_Language := Name_Ada;
      Data.Sources_Present         := Data.Source_Dirs /= Nil_String;

      if not Languages.Default then
         declare
            Current   : String_List_Id := Languages.Values;
            Element   : String_Element;
            Ada_Found : Boolean := False;

         begin
            Look_For_Ada : while Current /= Nil_String loop
               Element := String_Elements.Table (Current);
               String_To_Name_Buffer (Element.Value);
               To_Lower (Name_Buffer (1 .. Name_Len));

               if Name_Buffer (1 .. Name_Len) = "ada" then
                  Ada_Found := True;
                  exit Look_For_Ada;
               end if;

               Current := Element.Next;
            end loop Look_For_Ada;

            if not Ada_Found then

               --  Mark the project file as having no sources for Ada

               Data.Sources_Present := False;
            end if;
         end;
      end if;

      declare
         Naming_Id : constant Package_Id :=
                       Util.Value_Of (Name_Naming, Data.Decl.Packages);

         Naming : Package_Element;

      begin
         --  If there is a package Naming, we will put in Data.Naming
         --  what is in this package Naming.

         if Naming_Id /= No_Package then
            Naming := Packages.Table (Naming_Id);

            if Current_Verbosity = High then
               Write_Line ("Checking ""Naming"" for Ada.");
            end if;

            declare
               Bodies : constant Array_Element_Id :=
                                  Util.Value_Of
                                    (Name_Implementation, Naming.Decl.Arrays);

               Specifications : constant Array_Element_Id :=
                                  Util.Value_Of
                                    (Name_Specification, Naming.Decl.Arrays);

            begin
               if Bodies /= No_Array_Element then

                  --  We have elements in the array Body_Part

                  if Current_Verbosity = High then
                     Write_Line ("Found Bodies.");
                  end if;

                  Data.Naming.Bodies := Bodies;
                  Check_Unit_Names (Bodies);

               else
                  if Current_Verbosity = High then
                     Write_Line ("No Bodies.");
                  end if;
               end if;

               if Specifications /= No_Array_Element then

                  --  We have elements in the array Specification

                  if Current_Verbosity = High then
                     Write_Line ("Found Specifications.");
                  end if;

                  Data.Naming.Specifications := Specifications;
                  Check_Unit_Names (Specifications);

               else
                  if Current_Verbosity = High then
                     Write_Line ("No Specifications.");
                  end if;
               end if;
            end;

            --  We are now checking if variables Dot_Replacement, Casing,
            --  Specification_Append, Body_Append and/or Separate_Append
            --  exist.

            --  For each variable, if it does not exist, we do nothing,
            --  because we already have the default.

            --  Check Dot_Replacement

            declare
               Dot_Replacement : constant Variable_Value :=
                                   Util.Value_Of
                                     (Name_Dot_Replacement,
                                      Naming.Decl.Attributes);

            begin
               pragma Assert (Dot_Replacement.Kind = Single,
                              "Dot_Replacement is not a single string");

               if not Dot_Replacement.Default then

                  String_To_Name_Buffer (Dot_Replacement.Value);

                  if Name_Len = 0 then
                     Error_Msg ("Dot_Replacement cannot be empty",
                                Dot_Replacement.Location);

                  else
                     Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
                     Data.Naming.Dot_Replacement := Name_Find;
                     Data.Naming.Dot_Repl_Loc := Dot_Replacement.Location;
                  end if;

               end if;

            end;

            if Current_Verbosity = High then
               Write_Str  ("  Dot_Replacement = """);
               Write_Str  (Get_Name_String (Data.Naming.Dot_Replacement));
               Write_Char ('"');
               Write_Eol;
            end if;

            --  Check Casing

            declare
               Casing_String : constant Variable_Value :=
                 Util.Value_Of (Name_Casing, Naming.Decl.Attributes);

            begin
               pragma Assert (Casing_String.Kind = Single,
                              "Casing is not a single string");

               if not Casing_String.Default then
                  declare
                     Casing_Image : constant String :=
                                      Get_Name_String (Casing_String.Value);

                  begin
                     declare
                        Casing : constant Casing_Type :=
                          Value (Casing_Image);

                     begin
                        Data.Naming.Casing := Casing;
                     end;

                  exception
                     when Constraint_Error =>
                        if Casing_Image'Length = 0 then
                           Error_Msg ("Casing cannot be an empty string",
                                      Casing_String.Location);

                        else
                           Name_Len := Casing_Image'Length;
                           Name_Buffer (1 .. Name_Len) := Casing_Image;
                           Error_Msg_Name_1 := Name_Find;
                           Error_Msg
                             ("{ is not a correct Casing",
                              Casing_String.Location);
                        end if;
                  end;
               end if;
            end;

            if Current_Verbosity = High then
               Write_Str  ("  Casing = ");
               Write_Str  (Image (Data.Naming.Casing));
               Write_Char ('.');
               Write_Eol;
            end if;

            --  Check Specification_Suffix

            declare
               Ada_Spec_Suffix : constant Variable_Value :=
                 Prj.Util.Value_Of
                   (Index => Name_Ada,
                    In_Array => Data.Naming.Specification_Suffix);

            begin
               if Ada_Spec_Suffix.Kind = Single
                 and then String_Length (Ada_Spec_Suffix.Value) /= 0
               then
                  String_To_Name_Buffer (Ada_Spec_Suffix.Value);
                  Data.Naming.Current_Spec_Suffix := Name_Find;
                  Data.Naming.Spec_Suffix_Loc := Ada_Spec_Suffix.Location;

               else
                  Data.Naming.Current_Spec_Suffix := Default_Ada_Spec_Suffix;
               end if;
            end;

            if Current_Verbosity = High then
               Write_Str  ("  Specification_Suffix = """);
               Write_Str  (Get_Name_String (Data.Naming.Current_Spec_Suffix));
               Write_Char ('"');
               Write_Eol;
            end if;

            --  Check Implementation_Suffix

            declare
               Ada_Impl_Suffix : constant Variable_Value :=
                 Prj.Util.Value_Of
                   (Index => Name_Ada,
                    In_Array => Data.Naming.Implementation_Suffix);

            begin
               if Ada_Impl_Suffix.Kind = Single
                 and then String_Length (Ada_Impl_Suffix.Value) /= 0
               then
                  String_To_Name_Buffer (Ada_Impl_Suffix.Value);
                  Data.Naming.Current_Impl_Suffix := Name_Find;
                  Data.Naming.Impl_Suffix_Loc := Ada_Impl_Suffix.Location;

               else
                  Data.Naming.Current_Impl_Suffix := Default_Ada_Impl_Suffix;
               end if;
            end;

            if Current_Verbosity = High then
               Write_Str  ("  Implementation_Suffix = """);
               Write_Str  (Get_Name_String (Data.Naming.Current_Impl_Suffix));
               Write_Char ('"');
               Write_Eol;
            end if;

            --  Check Separate_Suffix

            declare
               Ada_Sep_Suffix : constant Variable_Value :=
                 Prj.Util.Value_Of
                 (Variable_Name => Name_Separate_Suffix,
                  In_Variables  => Naming.Decl.Attributes);
            begin
               if Ada_Sep_Suffix.Default then
                  Data.Naming.Separate_Suffix :=
                    Data.Naming.Current_Impl_Suffix;

               else
                  String_To_Name_Buffer (Ada_Sep_Suffix.Value);

                  if Name_Len = 0 then
                     Error_Msg ("Separate_Suffix cannot be empty",
                                Ada_Sep_Suffix.Location);

                  else
                     Data.Naming.Separate_Suffix := Name_Find;
                     Data.Naming.Sep_Suffix_Loc  := Ada_Sep_Suffix.Location;
                  end if;

               end if;

            end;

            if Current_Verbosity = High then
               Write_Str  ("  Separate_Suffix = """);
               Write_Str  (Get_Name_String (Data.Naming.Separate_Suffix));
               Write_Char ('"');
               Write_Eol;
            end if;

            --  Check if Data.Naming is valid

            Check_Ada_Naming_Scheme (Data.Naming);

         else
            Data.Naming.Current_Spec_Suffix := Default_Ada_Spec_Suffix;
            Data.Naming.Current_Impl_Suffix := Default_Ada_Impl_Suffix;
            Data.Naming.Separate_Suffix     := Default_Ada_Impl_Suffix;
         end if;
      end;

      --  If we have source directories, then find the sources

      if Data.Sources_Present then
         if Data.Source_Dirs = Nil_String then
            Data.Sources_Present := False;

         else
            declare
               Sources : constant Variable_Value :=
                 Util.Value_Of
                 (Name_Source_Files,
                  Data.Decl.Attributes);

               Source_List_File : constant Variable_Value :=
                 Util.Value_Of
                 (Name_Source_List_File,
                  Data.Decl.Attributes);

            begin
               pragma Assert
                 (Sources.Kind = List,
                    "Source_Files is not a list");
               pragma Assert
                 (Source_List_File.Kind = Single,
                    "Source_List_File is not a single string");

               if not Sources.Default then
                  if not Source_List_File.Default then
                     Error_Msg
                       ("?both variables source_files and " &
                        "source_list_file are present",
                        Source_List_File.Location);
                  end if;

                  --  Sources is a list of file names

                  declare
                     Current_Source : String_List_Id := Nil_String;
                     Current        : String_List_Id := Sources.Values;
                     Element        : String_Element;

                  begin
                     Data.Sources_Present := Current /= Nil_String;

                     while Current /= Nil_String loop
                        Element := String_Elements.Table (Current);
                        String_To_Name_Buffer (Element.Value);

                        declare
                           File_Name : constant String :=
                             Name_Buffer (1 .. Name_Len);

                        begin
                           Get_Path_Name_And_Record_Source
                             (File_Name        => File_Name,
                              Location         => Element.Location,
                              Current_Source   => Current_Source);
                           Current := Element.Next;
                        end;
                     end loop;
                  end;

                  --  No source_files specified.
                  --  We check Source_List_File has been specified.

               elsif not Source_List_File.Default then

                  --  Source_List_File is the name of the file
                  --  that contains the source file names

                  declare
                     Source_File_Path_Name : constant String :=
                       Path_Name_Of
                       (Source_List_File.Value,
                        Data.Directory);

                  begin
                     if Source_File_Path_Name'Length = 0 then
                        String_To_Name_Buffer (Source_List_File.Value);
                        Error_Msg_Name_1 := Name_Find;
                        Error_Msg
                          ("file with sources { does not exist",
                           Source_List_File.Location);

                     else
                        Get_Sources_From_File
                          (Source_File_Path_Name,
                           Source_List_File.Location);
                     end if;
                  end;

               else
                  --  Neither Source_Files nor Source_List_File has been
                  --  specified.
                  --  Find all the files that satisfy
                  --  the naming scheme in all the source directories.

                  Find_Sources;
               end if;
            end;
         end if;
      end if;

      Projects.Table (Project) := Data;
   end Ada_Check;

   --------------------
   -- Check_Ada_Name --
   --------------------

   procedure Check_Ada_Name
     (Name : Name_Id;
      Unit : out Name_Id)
   is
      The_Name        : String := Get_Name_String (Name);
      Need_Letter     : Boolean := True;
      Last_Underscore : Boolean := False;
      OK              : Boolean := The_Name'Length > 0;

   begin
      for Index in The_Name'Range loop
         if Need_Letter then

            --  We need a letter (at the beginning, and following a dot),
            --  but we don't have one.

            if Is_Letter (The_Name (Index)) then
               Need_Letter := False;

            else
               OK := False;

               if Current_Verbosity = High then
                  Write_Int  (Types.Int (Index));
                  Write_Str  (": '");
                  Write_Char (The_Name (Index));
                  Write_Line ("' is not a letter.");
               end if;

               exit;
            end if;

         elsif Last_Underscore
           and then (The_Name (Index) = '_' or else The_Name (Index) = '.')
         then
            --  Two underscores are illegal, and a dot cannot follow
            --  an underscore.

            OK := False;

            if Current_Verbosity = High then
               Write_Int  (Types.Int (Index));
               Write_Str  (": '");
               Write_Char (The_Name (Index));
               Write_Line ("' is illegal here.");
            end if;

            exit;

         elsif The_Name (Index) = '.' then

            --  We need a letter after a dot

            Need_Letter := True;

         elsif The_Name (Index) = '_' then
            Last_Underscore := True;

         else
            --  We need an letter or a digit

            Last_Underscore := False;

            if not Is_Alphanumeric (The_Name (Index)) then
               OK := False;

               if Current_Verbosity = High then
                  Write_Int  (Types.Int (Index));
                  Write_Str  (": '");
                  Write_Char (The_Name (Index));
                  Write_Line ("' is not alphanumeric.");
               end if;

               exit;
            end if;
         end if;
      end loop;

      --  Cannot end with an underscore or a dot

      OK := OK and then not Need_Letter and then not Last_Underscore;

      if OK then
         Unit := Name;
      else
         --  Signal a problem with No_Name

         Unit := No_Name;
      end if;
   end Check_Ada_Name;

   -----------------------------
   -- Check_Ada_Naming_Scheme --
   -----------------------------

   procedure Check_Ada_Naming_Scheme (Naming : Naming_Data) is
   begin
      --  Only check if we are not using the standard naming scheme

      if Naming /= Standard_Naming_Data then
         declare
            Dot_Replacement       : constant String :=
                                     Get_Name_String
                                       (Naming.Dot_Replacement);

            Specification_Suffix : constant String :=
                                     Get_Name_String
                                       (Naming.Current_Spec_Suffix);

            Implementation_Suffix : constant String :=
                                     Get_Name_String
                                       (Naming.Current_Impl_Suffix);

            Separate_Suffix       : constant String :=
                                     Get_Name_String
                                       (Naming.Separate_Suffix);

         begin
            --  Dot_Replacement cannot
            --   - be empty
            --   - start or end with an alphanumeric
            --   - be a single '_'
            --   - start with an '_' followed by an alphanumeric
            --   - contain a '.' except if it is "."

            if Dot_Replacement'Length = 0
              or else Is_Alphanumeric
                        (Dot_Replacement (Dot_Replacement'First))
              or else Is_Alphanumeric
                        (Dot_Replacement (Dot_Replacement'Last))
              or else (Dot_Replacement (Dot_Replacement'First) = '_'
                        and then
                        (Dot_Replacement'Length = 1
                          or else
                           Is_Alphanumeric
                             (Dot_Replacement (Dot_Replacement'First + 1))))
              or else (Dot_Replacement'Length > 1
                         and then
                           Index (Source => Dot_Replacement,
                                  Pattern => ".") /= 0)
            then
               Error_Msg
                 ('"' & Dot_Replacement &
                  """ is illegal for Dot_Replacement.",
                  Naming.Dot_Repl_Loc);
            end if;

            --  Suffixes cannot
            --   - be empty
            --   - start with an alphanumeric
            --   - start with an '_' followed by an alphanumeric

            if Is_Illegal_Append (Specification_Suffix) then
               Error_Msg_Name_1 := Naming.Current_Spec_Suffix;
               Error_Msg
                 ("{ is illegal for Specification_Suffix",
                  Naming.Spec_Suffix_Loc);
            end if;

            if Is_Illegal_Append (Implementation_Suffix) then
               Error_Msg_Name_1 := Naming.Current_Impl_Suffix;
               Error_Msg
                 ("% is illegal for Implementation_Suffix",
                  Naming.Impl_Suffix_Loc);
            end if;

            if Implementation_Suffix /= Separate_Suffix then
               if Is_Illegal_Append (Separate_Suffix) then
                  Error_Msg_Name_1 := Naming.Separate_Suffix;
                  Error_Msg
                    ("{ is illegal for Separate_Append",
                     Naming.Sep_Suffix_Loc);
               end if;
            end if;

            --  Specification_Suffix cannot have the same termination as
            --  Implementation_Suffix or Separate_Suffix

            if Specification_Suffix'Length <= Implementation_Suffix'Length
              and then
                Implementation_Suffix (Implementation_Suffix'Last -
                             Specification_Suffix'Length + 1 ..
                             Implementation_Suffix'Last) = Specification_Suffix
            then
               Error_Msg
                 ("Implementation_Suffix (""" &
                  Implementation_Suffix &
                  """) cannot end with" &
                  "Specification_Suffix  (""" &
                   Specification_Suffix & """).",
                  Naming.Impl_Suffix_Loc);
            end if;

            if Specification_Suffix'Length <= Separate_Suffix'Length
              and then
                Separate_Suffix
                  (Separate_Suffix'Last - Specification_Suffix'Length + 1
                    ..
                   Separate_Suffix'Last) = Specification_Suffix
            then
               Error_Msg
                 ("Separate_Suffix (""" &
                  Separate_Suffix &
                  """) cannot end with" &
                  " Specification_Suffix (""" &
                  Specification_Suffix & """).",
                  Naming.Sep_Suffix_Loc);
            end if;
         end;
      end if;

   end Check_Ada_Naming_Scheme;

   ---------------
   -- Error_Msg --
   ---------------

   procedure Error_Msg (Msg : String; Flag_Location : Source_Ptr) is

      Error_Buffer : String (1 .. 5_000);
      Error_Last   : Natural := 0;
      Msg_Name     : Natural := 0;
      First        : Positive := Msg'First;

      procedure Add (C : Character);
      --  Add a character to the buffer

      procedure Add (S : String);
      --  Add a string to the buffer

      procedure Add (Id : Name_Id);
      --  Add a name to the buffer

      ---------
      -- Add --
      ---------

      procedure Add (C : Character) is
      begin
         Error_Last := Error_Last + 1;
         Error_Buffer (Error_Last) := C;
      end Add;

      procedure Add (S : String) is
      begin
         Error_Buffer (Error_Last + 1 .. Error_Last + S'Length) := S;
         Error_Last := Error_Last + S'Length;
      end Add;

      procedure Add (Id : Name_Id) is
      begin
         Get_Name_String (Id);
         Add (Name_Buffer (1 .. Name_Len));
      end Add;

   --  Start of processing for Error_Msg

   begin
      if Error_Report = null then
         Errout.Error_Msg (Msg, Flag_Location);
         return;
      end if;

      if Msg (First) = '\' then

         --  Continuation character, ignore.

         First := First + 1;

      elsif Msg (First) = '?' then

         --  Warning character. It is always the first one,
         --  in this package.

         First := First + 1;
         Add ("Warning: ");
      end if;

      for Index in First .. Msg'Last loop
         if Msg (Index) = '{' or else Msg (Index) = '%' then

            --  Include a name between double quotes.

            Msg_Name := Msg_Name + 1;
            Add ('"');

            case Msg_Name is
               when 1 => Add (Error_Msg_Name_1);

               when 2 => Add (Error_Msg_Name_2);

               when 3 => Add (Error_Msg_Name_3);

               when others => null;
            end case;

            Add ('"');

         else
            Add (Msg (Index));
         end if;

      end loop;

      Error_Report (Error_Buffer (1 .. Error_Last));
   end Error_Msg;

   ---------------------
   -- Get_Name_String --
   ---------------------

   function Get_Name_String (S : String_Id) return String is
   begin
      if S = No_String then
         return "";
      else
         String_To_Name_Buffer (S);
         return Name_Buffer (1 .. Name_Len);
      end if;
   end Get_Name_String;

   --------------
   -- Get_Unit --
   --------------

   procedure Get_Unit
     (File_Name    : Name_Id;
      Naming       : Naming_Data;
      Unit_Name    : out Name_Id;
      Unit_Kind    : out Spec_Or_Body;
      Needs_Pragma : out Boolean)
   is
      Canonical_Case_Name : Name_Id;

   begin
      Needs_Pragma := False;
      Get_Name_String (File_Name);
      Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
      Canonical_Case_Name := Name_Find;

      if Naming.Bodies /= No_Array_Element then

         --  There are some specified file names for some bodies
         --  of this project. Find out if File_Name is one of these bodies.

         declare
            Current : Array_Element_Id := Naming.Bodies;
            Element : Array_Element;

         begin
            while Current /= No_Array_Element loop
               Element := Array_Elements.Table (Current);

               if Element.Index /= No_Name then
                  String_To_Name_Buffer (Element.Value.Value);
                  Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));

                  if Canonical_Case_Name = Name_Find then

                     --  File_Name corresponds to one body.
                     --  So, we know it is a body, and we know the unit name.

                     Unit_Kind := Body_Part;
                     Unit_Name := Element.Index;
                     Needs_Pragma := True;
                     return;
                  end if;
               end if;

               Current := Element.Next;
            end loop;
         end;
      end if;

      if Naming.Specifications /= No_Array_Element then

         --  There are some specified file names for some bodiesspecifications
         --  of this project. Find out if File_Name is one of these
         --  specifications.

         declare
            Current : Array_Element_Id := Naming.Specifications;
            Element : Array_Element;

         begin
            while Current /= No_Array_Element loop
               Element := Array_Elements.Table (Current);

               if Element.Index /= No_Name then
                  String_To_Name_Buffer (Element.Value.Value);
                  Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));

                  if Canonical_Case_Name = Name_Find then

                     --  File_Name corresponds to one specification.
                     --  So, we know it is a spec, and we know the unit name.

                     Unit_Kind := Specification;
                     Unit_Name := Element.Index;
                     Needs_Pragma := True;
                     return;
                  end if;

               end if;

               Current := Element.Next;
            end loop;
         end;
      end if;

      declare
         File  : String   := Get_Name_String (Canonical_Case_Name);
         First : Positive := File'First;
         Last  : Natural  := File'Last;

      begin
         --  Check if the end of the file name is Specification_Append

         Get_Name_String (Naming.Current_Spec_Suffix);

         if File'Length > Name_Len
           and then File (Last - Name_Len + 1 .. Last) =
                                                Name_Buffer (1 .. Name_Len)
         then
            --  We have a spec

            Unit_Kind := Specification;
            Last := Last - Name_Len;

            if Current_Verbosity = High then
               Write_Str  ("   Specification: ");
               Write_Line (File (First .. Last));
            end if;

         else
            Get_Name_String (Naming.Current_Impl_Suffix);

            --  Check if the end of the file name is Body_Append

            if File'Length > Name_Len
              and then File (Last - Name_Len + 1 .. Last) =
                                                Name_Buffer (1 .. Name_Len)
            then
               --  We have a body

               Unit_Kind := Body_Part;
               Last := Last - Name_Len;

               if Current_Verbosity = High then
                  Write_Str  ("   Body: ");
                  Write_Line (File (First .. Last));
               end if;

            elsif Naming.Separate_Suffix /= Naming.Current_Spec_Suffix then
               Get_Name_String (Naming.Separate_Suffix);

               --  Check if the end of the file name is Separate_Append

               if File'Length > Name_Len
                 and then File (Last - Name_Len + 1 .. Last) =
                                                Name_Buffer (1 .. Name_Len)
               then
                  --  We have a separate (a body)

                  Unit_Kind := Body_Part;
                  Last := Last - Name_Len;

                  if Current_Verbosity = High then
                     Write_Str  ("   Separate: ");
                     Write_Line (File (First .. Last));
                  end if;

               else
                  Last := 0;
               end if;

            else
               Last := 0;
            end if;
         end if;

         if Last = 0 then

            --  This is not a source file

            Unit_Name := No_Name;
            Unit_Kind := Specification;

            if Current_Verbosity = High then
               Write_Line ("   Not a valid file name.");
            end if;

            return;
         end if;

         Get_Name_String (Naming.Dot_Replacement);

         if Name_Buffer (1 .. Name_Len) /= "." then

            --  If Dot_Replacement is not a single dot,
            --  then there should not be any dot in the name.

            for Index in First .. Last loop
               if File (Index) = '.' then
                  if Current_Verbosity = High then
                     Write_Line
                       ("   Not a valid file name (some dot not replaced).");
                  end if;

                  Unit_Name := No_Name;
                  return;

               end if;
            end loop;

            --  Replace the substring Dot_Replacement with dots

            declare
               Index : Positive := First;

            begin
               while Index <= Last - Name_Len + 1 loop

                  if File (Index .. Index + Name_Len - 1) =
                    Name_Buffer (1 .. Name_Len)
                  then
                     File (Index) := '.';

                     if Name_Len > 1 and then Index < Last then
                        File (Index + 1 .. Last - Name_Len + 1) :=
                          File (Index + Name_Len .. Last);
                     end if;

                     Last := Last - Name_Len + 1;
                  end if;

                  Index := Index + 1;
               end loop;
            end;
         end if;

         --  Check if the casing is right

         declare
            Src : String := File (First .. Last);

         begin
            case Naming.Casing is
               when All_Lower_Case =>
                  Fixed.Translate
                    (Source  => Src,
                     Mapping => Lower_Case_Map);

               when All_Upper_Case =>
                  Fixed.Translate
                    (Source  => Src,
                     Mapping => Upper_Case_Map);

               when Mixed_Case | Unknown =>
                  null;
            end case;

            if Src /= File (First .. Last) then
               if Current_Verbosity = High then
                  Write_Line ("   Not a valid file name (casing).");
               end if;

               Unit_Name := No_Name;
               return;
            end if;

            --  We put the name in lower case

            Fixed.Translate
              (Source  => Src,
               Mapping => Lower_Case_Map);

            if Current_Verbosity = High then
               Write_Str  ("      ");
               Write_Line (Src);
            end if;

            Name_Len := Src'Length;
            Name_Buffer (1 .. Name_Len) := Src;

            --  Now, we check if this name is a valid unit name

            Check_Ada_Name (Name => Name_Find, Unit => Unit_Name);
         end;

      end;

   end Get_Unit;

   -----------------------
   -- Is_Illegal_Append --
   -----------------------

   function Is_Illegal_Append (This : String) return Boolean is
   begin
      return This'Length = 0
        or else Is_Alphanumeric (This (This'First))
        or else Index (This, ".") = 0
        or else (This'Length >= 2
                 and then This (This'First) = '_'
                 and then Is_Alphanumeric (This (This'First + 1)));
   end Is_Illegal_Append;

   --------------------------------
   -- Language_Independent_Check --
   --------------------------------

   procedure Language_Independent_Check
     (Project      : Project_Id;
      Report_Error : Put_Line_Access)
   is
      Last_Source_Dir   : String_List_Id  := Nil_String;
      Data              : Project_Data    := Projects.Table (Project);

      procedure Find_Source_Dirs (From : String_Id; Location : Source_Ptr);
      --  Find one or several source directories, and add them
      --  to the list of source directories of the project.

      ----------------------
      -- Find_Source_Dirs --
      ----------------------

      procedure Find_Source_Dirs (From : String_Id; Location : Source_Ptr) is

         Directory    : String (1 .. Integer (String_Length (From)));
         Directory_Id : Name_Id;
         Element      : String_Element;

         procedure Recursive_Find_Dirs (Path : String_Id);
         --  Find all the subdirectories (recursively) of Path
         --  and add them to the list of source directories
         --  of the project.

         -------------------------
         -- Recursive_Find_Dirs --
         -------------------------

         procedure Recursive_Find_Dirs (Path : String_Id) is
            Dir      : Dir_Type;
            Name     : String (1 .. 250);
            Last     : Natural;
            The_Path : String := Get_Name_String (Path) & Dir_Sep;

            The_Path_Last : Positive := The_Path'Last;

         begin
            if The_Path'Length > 1
              and then
                (The_Path (The_Path_Last - 1) = Dir_Sep
                   or else The_Path (The_Path_Last - 1) = '/')
            then
               The_Path_Last := The_Path_Last - 1;
            end if;

            if Current_Verbosity = High then
               Write_Str  ("   ");
               Write_Line (The_Path (The_Path'First .. The_Path_Last));
            end if;

            String_Elements.Increment_Last;
            Element :=
              (Value    => Path,
               Location => No_Location,
               Next     => Nil_String);

            --  Case of first source directory

            if Last_Source_Dir = Nil_String then
               Data.Source_Dirs := String_Elements.Last;

            --  Here we already have source directories.

            else
               --  Link the previous last to the new one

               String_Elements.Table (Last_Source_Dir).Next :=
                 String_Elements.Last;
            end if;

            --  And register this source directory as the new last

            Last_Source_Dir  := String_Elements.Last;
            String_Elements.Table (Last_Source_Dir) := Element;

            --  Now look for subdirectories

            Open (Dir, The_Path (The_Path'First .. The_Path_Last));

            loop
               Read (Dir, Name, Last);
               exit when Last = 0;

               if Current_Verbosity = High then
                  Write_Str  ("   Checking ");
                  Write_Line (Name (1 .. Last));
               end if;

               if Name (1 .. Last) /= "."
                 and then Name (1 .. Last) /= ".."
               then
                  --  Avoid . and ..

                  declare
                     Path_Name : constant String :=
                                   The_Path (The_Path'First .. The_Path_Last) &
                                   Name (1 .. Last);

                  begin
                     if Is_Directory (Path_Name) then

                        --  We have found a new subdirectory,
                        --  register it and find its own subdirectories.

                        Start_String;
                        Store_String_Chars (Path_Name);
                        Recursive_Find_Dirs (End_String);
                     end if;
                  end;
               end if;
            end loop;

            Close (Dir);

         exception
            when Directory_Error =>
               null;
         end Recursive_Find_Dirs;

         --  Start of processing for Find_Source_Dirs

      begin
         if Current_Verbosity = High then
            Write_Str ("Find_Source_Dirs (""");
         end if;

         String_To_Name_Buffer (From);
         Directory    := Name_Buffer (1 .. Name_Len);
         Directory_Id := Name_Find;

         if Current_Verbosity = High then
            Write_Str (Directory);
            Write_Line (""")");
         end if;

         --  First, check if we are looking for a directory tree,
         --  indicated by "/**" at the end.

         if Directory'Length >= 3
           and then Directory (Directory'Last - 1 .. Directory'Last) = "**"
           and then (Directory (Directory'Last - 2) = '/'
                       or else
                     Directory (Directory'Last - 2) = Dir_Sep)
         then
            Name_Len := Directory'Length - 3;

            if Name_Len = 0 then
               --  This is the case of "/**": all directories
               --  in the file system.

               Name_Len := 1;
               Name_Buffer (1) := Directory (Directory'First);

            else
               Name_Buffer (1 .. Name_Len) :=
                 Directory (Directory'First .. Directory'Last - 3);
            end if;

            if Current_Verbosity = High then
               Write_Str ("Looking for all subdirectories of """);
               Write_Str (Name_Buffer (1 .. Name_Len));
               Write_Line ("""");
            end if;

            declare
               Base_Dir : constant Name_Id := Name_Find;
               Root     : constant Name_Id :=
                            Locate_Directory (Base_Dir, Data.Directory);

            begin
               if Root = No_Name then
                  Error_Msg_Name_1 := Base_Dir;
                  if Location = No_Location then
                     Error_Msg ("{ is not a valid directory.", Data.Location);
                  else
                     Error_Msg ("{ is not a valid directory.", Location);
                  end if;

               else
                  --  We have an existing directory,
                  --  we register it and all of its subdirectories.

                  if Current_Verbosity = High then
                     Write_Line ("Looking for source directories:");
                  end if;

                  Start_String;
                  Store_String_Chars (Get_Name_String (Root));
                  Recursive_Find_Dirs (End_String);

                  if Current_Verbosity = High then
                     Write_Line ("End of looking for source directories.");
                  end if;
               end if;
            end;

         --  We have a single directory

         else
            declare
               Path_Name : constant Name_Id :=
                 Locate_Directory (Directory_Id, Data.Directory);

            begin
               if Path_Name = No_Name then
                  Error_Msg_Name_1 := Directory_Id;
                  if Location = No_Location then
                     Error_Msg ("{ is not a valid directory", Data.Location);
                  else
                     Error_Msg ("{ is not a valid directory", Location);
                  end if;
               else

                  --  As it is an existing directory, we add it to
                  --  the list of directories.

                  String_Elements.Increment_Last;
                  Start_String;
                  Store_String_Chars (Get_Name_String (Path_Name));
                  Element.Value := End_String;

                  if Last_Source_Dir = Nil_String then

                     --  This is the first source directory

                     Data.Source_Dirs := String_Elements.Last;

                  else
                     --  We already have source directories,
                     --  link the previous last to the new one.

                     String_Elements.Table (Last_Source_Dir).Next :=
                       String_Elements.Last;
                  end if;

                  --  And register this source directory as the new last

                  Last_Source_Dir := String_Elements.Last;
                  String_Elements.Table (Last_Source_Dir) := Element;
               end if;
            end;
         end if;
      end Find_Source_Dirs;

      --  Start of processing for Language_Independent_Check

   begin

      if Data.Language_Independent_Checked then
         return;
      end if;

      Data.Language_Independent_Checked := True;

      Error_Report := Report_Error;

      if Current_Verbosity = High then
         Write_Line ("Starting to look for directories");
      end if;

      --  Check the object directory

      declare
         Object_Dir : Variable_Value :=
                        Util.Value_Of (Name_Object_Dir, Data.Decl.Attributes);

      begin
         pragma Assert (Object_Dir.Kind = Single,
                        "Object_Dir is not a single string");

         --  We set the object directory to its default

         Data.Object_Directory := Data.Directory;

         if not String_Equal (Object_Dir.Value, Empty_String) then

            String_To_Name_Buffer (Object_Dir.Value);

            if Name_Len = 0 then
               Error_Msg ("Object_Dir cannot be empty",
                          Object_Dir.Location);

            else
               --  We check that the specified object directory
               --  does exist.

               Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));

               declare
                  Dir_Id : constant Name_Id := Name_Find;

               begin
                  Data.Object_Directory :=
                    Locate_Directory (Dir_Id, Data.Directory);

                  if Data.Object_Directory = No_Name then
                     Error_Msg_Name_1 := Dir_Id;
                     Error_Msg
                       ("the object directory { cannot be found",
                        Data.Location);
                  end if;
               end;
            end if;
         end if;
      end;

      if Current_Verbosity = High then
         if Data.Object_Directory = No_Name then
            Write_Line ("No object directory");
         else
            Write_Str ("Object directory: """);
            Write_Str (Get_Name_String (Data.Object_Directory));
            Write_Line ("""");
         end if;
      end if;

      --  Check the exec directory

      declare
         Exec_Dir : Variable_Value :=
                      Util.Value_Of (Name_Exec_Dir, Data.Decl.Attributes);

      begin
         pragma Assert (Exec_Dir.Kind = Single,
                        "Exec_Dir is not a single string");

         --  We set the object directory to its default

         Data.Exec_Directory := Data.Object_Directory;

         if not String_Equal (Exec_Dir.Value, Empty_String) then

            String_To_Name_Buffer (Exec_Dir.Value);

            if Name_Len = 0 then
               Error_Msg ("Exec_Dir cannot be empty",
                          Exec_Dir.Location);

            else
               --  We check that the specified object directory
               --  does exist.

               Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));

               declare
                  Dir_Id : constant Name_Id := Name_Find;

               begin
                  Data.Exec_Directory :=
                    Locate_Directory (Dir_Id, Data.Directory);

                  if Data.Exec_Directory = No_Name then
                     Error_Msg_Name_1 := Dir_Id;
                     Error_Msg
                       ("the exec directory { cannot be found",
                        Data.Location);
                  end if;
               end;
            end if;
         end if;
      end;

      if Current_Verbosity = High then
         if Data.Exec_Directory = No_Name then
            Write_Line ("No exec directory");
         else
            Write_Str ("Exec directory: """);
            Write_Str (Get_Name_String (Data.Exec_Directory));
            Write_Line ("""");
         end if;
      end if;

      --  Look for the source directories

      declare
         Source_Dirs : Variable_Value :=
           Util.Value_Of (Name_Source_Dirs, Data.Decl.Attributes);

      begin

         if Current_Verbosity = High then
            Write_Line ("Starting to look for source directories");
         end if;

         pragma Assert (Source_Dirs.Kind = List,
                          "Source_Dirs is not a list");

         if Source_Dirs.Default then

            --  No Source_Dirs specified: the single source directory
            --  is the one containing the project file

            String_Elements.Increment_Last;
            Data.Source_Dirs := String_Elements.Last;
            Start_String;
            Store_String_Chars (Get_Name_String (Data.Directory));
            String_Elements.Table (Data.Source_Dirs) :=
              (Value    => End_String,
               Location => No_Location,
               Next     => Nil_String);

            if Current_Verbosity = High then
               Write_Line ("(Undefined) Single object directory:");
               Write_Str ("    """);
               Write_Str (Get_Name_String (Data.Directory));
               Write_Line ("""");
            end if;

         elsif Source_Dirs.Values = Nil_String then

            --  If Source_Dirs is an empty string list, this means
            --  that this project contains no source.

            if Data.Object_Directory = Data.Directory then
               Data.Object_Directory := No_Name;
            end if;

            Data.Source_Dirs     := Nil_String;
            Data.Sources_Present := False;

         else
            declare
               Source_Dir : String_List_Id := Source_Dirs.Values;
               Element    : String_Element;

            begin
               --  We will find the source directories for each
               --  element of the list

               while Source_Dir /= Nil_String loop
                  Element := String_Elements.Table (Source_Dir);
                  Find_Source_Dirs (Element.Value, Element.Location);
                  Source_Dir := Element.Next;
               end loop;
            end;
         end if;

         if Current_Verbosity = High then
            Write_Line ("Puting source directories in canonical cases");
         end if;

         declare
            Current : String_List_Id := Data.Source_Dirs;
            Element : String_Element;

         begin
            while Current /= Nil_String loop
               Element := String_Elements.Table (Current);
               if Element.Value /= No_String then
                  String_To_Name_Buffer (Element.Value);
                  Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
                  Start_String;
                  Store_String_Chars (Name_Buffer (1 .. Name_Len));
                  Element.Value := End_String;
                  String_Elements.Table (Current) := Element;
               end if;

               Current := Element.Next;
            end loop;
         end;
      end;

      --  Library Dir, Name, Version and Kind

      declare
         Attributes : constant Prj.Variable_Id := Data.Decl.Attributes;

         Lib_Dir : Prj.Variable_Value :=
                     Prj.Util.Value_Of (Snames.Name_Library_Dir, Attributes);

         Lib_Name : Prj.Variable_Value :=
                      Prj.Util.Value_Of (Snames.Name_Library_Name, Attributes);

         Lib_Version : Prj.Variable_Value :=
                         Prj.Util.Value_Of
                           (Snames.Name_Library_Version, Attributes);

         The_Lib_Kind : Prj.Variable_Value :=
                          Prj.Util.Value_Of
                            (Snames.Name_Library_Kind, Attributes);

      begin
         pragma Assert (Lib_Dir.Kind = Single);

         if Lib_Dir.Value = Empty_String then

            if Current_Verbosity = High then
               Write_Line ("No library directory");
            end if;

         else
            --  Find path name, check that it is a directory

            Stringt.String_To_Name_Buffer (Lib_Dir.Value);

            declare
               Dir_Id : constant Name_Id := Name_Find;

            begin
               Data.Library_Dir :=
                 Locate_Directory (Dir_Id, Data.Directory);

               if Data.Library_Dir = No_Name then
                  Error_Msg ("not an existing directory",
                             Lib_Dir.Location);

               elsif Data.Library_Dir = Data.Object_Directory then
                  Error_Msg
                    ("library directory cannot be the same " &
                     "as object directory",
                     Lib_Dir.Location);
                  Data.Library_Dir := No_Name;

               else
                  if Current_Verbosity = High then
                     Write_Str ("Library directory =""");
                     Write_Str (Get_Name_String (Data.Library_Dir));
                     Write_Line ("""");
                  end if;
               end if;
            end;
         end if;

         pragma Assert (Lib_Name.Kind = Single);

         if Lib_Name.Value = Empty_String then
            if Current_Verbosity = High then
               Write_Line ("No library name");
            end if;

         else
            Stringt.String_To_Name_Buffer (Lib_Name.Value);

            if not Is_Letter (Name_Buffer (1)) then
               Error_Msg ("must start with a letter",
                          Lib_Name.Location);

            else
               Data.Library_Name := Name_Find;

               for Index in 2 .. Name_Len loop
                  if not Is_Alphanumeric (Name_Buffer (Index)) then
                     Data.Library_Name := No_Name;
                     Error_Msg ("only letters and digits are allowed",
                                Lib_Name.Location);
                     exit;
                  end if;
               end loop;

               if Data.Library_Name /= No_Name
                 and then Current_Verbosity = High then
                  Write_Str ("Library name = """);
                  Write_Str (Get_Name_String (Data.Library_Name));
                  Write_Line ("""");
               end if;
            end if;
         end if;

         Data.Library :=
           Data.Library_Dir /= No_Name
             and then
           Data.Library_Name /= No_Name;

         if Data.Library then

            if not MLib.Tgt.Libraries_Are_Supported then
               Error_Msg ("?libraries are not supported on this platform",
                          Lib_Name.Location);
               Data.Library := False;

            else
               if Current_Verbosity = High then
                  Write_Line ("This is a library project file");
               end if;

               pragma Assert (Lib_Version.Kind = Single);

               if Lib_Version.Value = Empty_String then
                  if Current_Verbosity = High then
                     Write_Line ("No library version specified");
                  end if;

               else
                  Stringt.String_To_Name_Buffer (Lib_Version.Value);
                  Data.Lib_Internal_Name := Name_Find;
               end if;

               pragma Assert (The_Lib_Kind.Kind = Single);

               if The_Lib_Kind.Value = Empty_String then
                  if Current_Verbosity = High then
                     Write_Line ("No library kind specified");
                  end if;

               else
                  Stringt.String_To_Name_Buffer (The_Lib_Kind.Value);

                  declare
                     Kind_Name : constant String :=
                                   To_Lower (Name_Buffer (1 .. Name_Len));

                     OK : Boolean := True;

                  begin
                     if Kind_Name = "static" then
                        Data.Library_Kind := Static;

                     elsif Kind_Name = "dynamic" then
                        Data.Library_Kind := Dynamic;

                     elsif Kind_Name = "relocatable" then
                        Data.Library_Kind := Relocatable;

                     else
                        Error_Msg
                          ("illegal value for Library_Kind",
                           The_Lib_Kind.Location);
                        OK := False;
                     end if;

                     if Current_Verbosity = High and then OK then
                        Write_Str ("Library kind = ");
                        Write_Line (Kind_Name);
                     end if;
                  end;
               end if;
            end if;
         end if;
      end;

      if Current_Verbosity = High then
         Show_Source_Dirs (Project);
      end if;

      declare
         Naming_Id : constant Package_Id :=
                       Util.Value_Of (Name_Naming, Data.Decl.Packages);

         Naming    : Package_Element;

      begin
         --  If there is a package Naming, we will put in Data.Naming
         --  what is in this package Naming.

         if Naming_Id /= No_Package then
            Naming := Packages.Table (Naming_Id);

            if Current_Verbosity = High then
               Write_Line ("Checking ""Naming"".");
            end if;

            --  Check Specification_Suffix

            Data.Naming.Specification_Suffix := Util.Value_Of
                                                 (Name_Specification_Suffix,
                                                  Naming.Decl.Arrays);

            declare
               Current : Array_Element_Id := Data.Naming.Specification_Suffix;
               Element : Array_Element;

            begin
               while Current /= No_Array_Element loop
                  Element := Array_Elements.Table (Current);
                  String_To_Name_Buffer (Element.Value.Value);

                  if Name_Len = 0 then
                     Error_Msg
                       ("Specification_Suffix cannot be empty",
                        Element.Value.Location);
                  end if;

                  Array_Elements.Table (Current) := Element;
                  Current := Element.Next;
               end loop;
            end;

            --  Check Implementation_Suffix

            Data.Naming.Implementation_Suffix := Util.Value_Of
                                          (Name_Implementation_Suffix,
                                           Naming.Decl.Arrays);

            declare
               Current : Array_Element_Id := Data.Naming.Implementation_Suffix;
               Element : Array_Element;

            begin
               while Current /= No_Array_Element loop
                  Element := Array_Elements.Table (Current);
                  String_To_Name_Buffer (Element.Value.Value);

                  if Name_Len = 0 then
                     Error_Msg
                       ("Implementation_Suffix cannot be empty",
                        Element.Value.Location);
                  end if;

                  Array_Elements.Table (Current) := Element;
                  Current := Element.Next;
               end loop;
            end;

         end if;
      end;

      Projects.Table (Project) := Data;
   end Language_Independent_Check;

   ----------------------
   -- Locate_Directory --
   ----------------------

   function Locate_Directory
     (Name   : Name_Id;
      Parent : Name_Id)
      return   Name_Id
   is
      The_Name   : constant String := Get_Name_String (Name);
      The_Parent : constant String :=
                     Get_Name_String (Parent) & Dir_Sep;

      The_Parent_Last : Positive := The_Parent'Last;

   begin
      if The_Parent'Length > 1
        and then (The_Parent (The_Parent_Last - 1) = Dir_Sep
                    or else The_Parent (The_Parent_Last - 1) = '/')
      then
         The_Parent_Last := The_Parent_Last - 1;
      end if;

      if Current_Verbosity = High then
         Write_Str ("Locate_Directory (""");
         Write_Str (The_Name);
         Write_Str (""", """);
         Write_Str (The_Parent);
         Write_Line (""")");
      end if;

      if Is_Absolute_Path (The_Name) then
         if Is_Directory (The_Name) then
            return Name;
         end if;

      else
         declare
            Full_Path : constant String :=
                          The_Parent (The_Parent'First .. The_Parent_Last) &
                                                                     The_Name;

         begin
            if Is_Directory (Full_Path) then
               Name_Len := Full_Path'Length;
               Name_Buffer (1 .. Name_Len) := Full_Path;
               return Name_Find;
            end if;
         end;

      end if;

      return No_Name;
   end Locate_Directory;

   ------------------
   -- Path_Name_Of --
   ------------------

   function Path_Name_Of
     (File_Name : String_Id;
      Directory : String_Id)
      return      String
   is
      Result : String_Access;

   begin
      String_To_Name_Buffer (File_Name);

      declare
         The_File_Name : constant String := Name_Buffer (1 .. Name_Len);

      begin
         String_To_Name_Buffer (Directory);
         Result := Locate_Regular_File
           (File_Name => The_File_Name,
            Path      => Name_Buffer (1 .. Name_Len));
      end;

      if Result = null then
         return "";
      else
         Canonical_Case_File_Name (Result.all);
         return Result.all;
      end if;
   end Path_Name_Of;

   function Path_Name_Of
     (File_Name : String_Id;
      Directory : Name_Id)
      return      String
   is
      Result : String_Access;
      The_Directory : constant String := Get_Name_String (Directory);

   begin
      String_To_Name_Buffer (File_Name);
      Result := Locate_Regular_File
        (File_Name => Name_Buffer (1 .. Name_Len),
         Path      => The_Directory);

      if Result = null then
         return "";
      else
         Canonical_Case_File_Name (Result.all);
         return Result.all;
      end if;
   end Path_Name_Of;

   -------------------
   -- Record_Source --
   -------------------

   procedure Record_Source
     (File_Name        : Name_Id;
      Path_Name        : Name_Id;
      Project          : Project_Id;
      Data             : in out Project_Data;
      Location         : Source_Ptr;
      Current_Source   : in out String_List_Id)
   is
      Unit_Name    : Name_Id;
      Unit_Kind    : Spec_Or_Body;
      Needs_Pragma : Boolean;
      The_Location : Source_Ptr := Location;

   begin
      --  Find out the unit name, the unit kind and if it needs
      --  a specific SFN pragma.

      Get_Unit
        (File_Name    => File_Name,
         Naming       => Data.Naming,
         Unit_Name    => Unit_Name,
         Unit_Kind    => Unit_Kind,
         Needs_Pragma => Needs_Pragma);

      if Unit_Name = No_Name then
         if Current_Verbosity = High then
            Write_Str  ("   """);
            Write_Str  (Get_Name_String (File_Name));
            Write_Line (""" is not a valid source file name (ignored).");
         end if;

      else
         --  Put the file name in the list of sources of the project

         String_Elements.Increment_Last;
         Get_Name_String (File_Name);
         Start_String;
         Store_String_Chars (Name_Buffer (1 .. Name_Len));
         String_Elements.Table (String_Elements.Last) :=
           (Value    => End_String,
            Location => No_Location,
            Next     => Nil_String);

         if Current_Source = Nil_String then
            Data.Sources := String_Elements.Last;

         else
            String_Elements.Table (Current_Source).Next :=
              String_Elements.Last;
         end if;

         Current_Source := String_Elements.Last;

         --  Put the unit in unit list

         declare
            The_Unit      : Unit_Id := Units_Htable.Get (Unit_Name);
            The_Unit_Data : Unit_Data;

         begin
            if Current_Verbosity = High then
               Write_Str  ("Putting ");
               Write_Str  (Get_Name_String (Unit_Name));
               Write_Line (" in the unit list.");
            end if;

            --  The unit is already in the list, but may be it is
            --  only the other unit kind (spec or body), or what is
            --  in the unit list is a unit of a project we are extending.

            if The_Unit /= Prj.Com.No_Unit then
               The_Unit_Data := Units.Table (The_Unit);

               if The_Unit_Data.File_Names (Unit_Kind).Name = No_Name
                 or else (Data.Modifies /= No_Project
                            and then
                          The_Unit_Data.File_Names (Unit_Kind).Project =
                                                            Data.Modifies)
               then
                  The_Unit_Data.File_Names (Unit_Kind) :=
                    (Name         => File_Name,
                     Path         => Path_Name,
                     Project      => Project,
                     Needs_Pragma => Needs_Pragma);
                  Units.Table (The_Unit) := The_Unit_Data;

               else
                  --  It is an error to have two units with the same name
                  --  and the same kind (spec or body).

                  if The_Location = No_Location then
                     The_Location := Projects.Table (Project).Location;
                  end if;

                  Error_Msg_Name_1 := Unit_Name;
                  Error_Msg ("duplicate source {", The_Location);

                  Error_Msg_Name_1 :=
                    Projects.Table
                      (The_Unit_Data.File_Names (Unit_Kind).Project).Name;
                  Error_Msg_Name_2 :=
                    The_Unit_Data.File_Names (Unit_Kind).Path;
                  Error_Msg ("\   project file {, {", The_Location);

                  Error_Msg_Name_1 := Projects.Table (Project).Name;
                  Error_Msg_Name_2 := Path_Name;
                  Error_Msg ("\   project file {, {", The_Location);

               end if;

            --  It is a new unit, create a new record

            else
               Units.Increment_Last;
               The_Unit := Units.Last;
               Units_Htable.Set (Unit_Name, The_Unit);
               The_Unit_Data.Name := Unit_Name;
               The_Unit_Data.File_Names (Unit_Kind) :=
                 (Name         => File_Name,
                  Path         => Path_Name,
                  Project      => Project,
                  Needs_Pragma => Needs_Pragma);
               Units.Table (The_Unit) := The_Unit_Data;
            end if;
         end;
      end if;
   end Record_Source;

   ----------------------
   -- Show_Source_Dirs --
   ----------------------

   procedure Show_Source_Dirs (Project : Project_Id) is
      Current : String_List_Id := Projects.Table (Project).Source_Dirs;
      Element : String_Element;

   begin
      Write_Line ("Source_Dirs:");

      while Current /= Nil_String loop
         Element := String_Elements.Table (Current);
         Write_Str  ("   ");
         Write_Line (Get_Name_String (Element.Value));
         Current := Element.Next;
      end loop;

      Write_Line ("end Source_Dirs.");
   end Show_Source_Dirs;

end Prj.Nmsc;
