-------------------------------------------------------------------------------
--
--  This file is part of AdaBrowse.
--
-- <STRONG>Copyright (c) 2002 by Thomas Wolf.</STRONG>
-- <BLOCKQUOTE>
--    AdaBrowse is free software; you can redistribute it and/or modify it
--    under the terms of the  GNU General Public License as published by the
--    Free Software  Foundation; either version 2, or (at your option) any
--    later version. AdaBrowse is distributed in the hope that it will be
--    useful, but <EM>without any warranty</EM>; without even the implied
--    warranty of <EM>merchantability or fitness for a particular purpose.</EM>
--    See the GNU General Public License for  more details. You should have
--    received a copy of the GNU General Public License with this distribution,
--    see file "<A HREF="GPL.txt">GPL.txt</A>". If not, write to the Free
--    Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
--    USA.
-- </BLOCKQUOTE>
--
-- <DL><DT><STRONG>
-- Author:</STRONG><DD>
--   Thomas Wolf  (TW)
--   <ADDRESS><A HREF="mailto:twolf@acm.org">twolf@acm.org</A></ADDRESS></DL>
--
-- <DL><DT><STRONG>
-- Purpose:</STRONG><DD>
--   Project manager implementation.</DL>
--
-- <!--
-- Revision History
--
--   07-JUN-2003   TW  Initial version.
--   09-JUN-2003   TW  Added support for naming schemes. What a hack!
--   10-JUN-2003   TW  Make sure we don't use this when ASIS is initialized!
--   08-JUL-2003   TW  Make sure we don't use the object directory for trees.
--   09-JUL-2003   TW  Use GAL.Containers.Simple, and AD.Known_Units. Also
--                     implemented the strong directorty equality check for
--                     Tree_Dir and Object_Dir.
--   19-NOV-2003   TW  Additional check that the tree directory really exists.
--                     Also uses Prj.Ext now (support for the -X option).
-- -->
-------------------------------------------------------------------------------

pragma License (GPL);

--  Standard library units

with Ada.Exceptions;
with Ada.Strings.Unbounded;
with Ada.Text_IO;

--  GNAT library units

with GNAT.Directory_Operations;
with GNAT.OS_Lib;

--  GNAT project manager units:

with Prj.Com;
with Prj.Env;
with Prj.Ext;
with Prj.Pars;

--  These GNAT units are in ASIS-for-GNAT:

with Csets;
with Namet;
with Snames;
with Stringt;
with Types;

--  Asis units:

with Asis.Implementation;

--  My own units:

with AD.Config;
with AD.File_Ops;
with AD.Known_Units;
with AD.Messages;
with AD.Text_Utilities;

with GAL.Containers.Simple;
with GAL.Storage.Standard;

pragma Elaborate_All (GAL.Containers.Simple);

with Util.Files.Text_IO;
with Util.Pathes;
with Util.Strings;

package body AD.Projects.Impl_Yes is

   --  Using the project manager is a bit tricky. First, we do have a dummy
   --  sdefault file, specifying dummy (null) default search pathes (we don't
   --  need them).
   --
   --  Second, the GNAT 3.16a source distribution contains an osint.adb
   --  which imports a C function "update_path", with comes from a file
   --  "prefix.c" from the GCC sources, but which appears to be lacking
   --  from the GNAT source distribution and which furthermore would only
   --  complicate even further the build process.
   --
   --  Since we do not need this anyway, we just provide yet another dummy
   --  function and export it under the name "update_path" and convention C.

   package Update_Path_Hack is
   end Update_Path_Hack;

   package body Update_Path_Hack is separate;
   --  It's separate because I don't want to clutter this code with references
   --  to Interfaces.C just because of such a stupid hack!

   package ASU renames Ada.Strings.Unbounded;

   AdaBrowse_Configurations  : constant String := "AdaBrowse_Configurations";
   AdaBrowse_Output          : constant String := "AdaBrowse_Output";
   AdaBrowse_Tree            : constant String := "AdaBrowse_Tree_Dir";
   Tree_Dir_Attribute        : constant String := "Tree_Dir";
   --  Support for future possible modification of the project manager!


   Output_Directory            : ASU.Unbounded_String;
   Tree_Directory              : ASU.Unbounded_String;
   Source_Directories          : ASU.Unbounded_String;

   Project_File_Name           : ASU.Unbounded_String;
   Own_Project_File            : Boolean       := False;

   Project_Manager_Activated   : Boolean       := False;
   Project_Manager_Initialized : Boolean       := False;

   procedure Check_Asis_Inactive
   is
   begin
      if Asis.Implementation.Is_Initialized then
         --  Unfortunately, ASIS-for-GNAT also initializes the tables. The
         --  rule we follow in AdaBrowse is thus a very simple one: uses of
         --  ASIS and the project manager must not overlap. Which means that
         --  we must fail hard if ASIS already has been initialized.
         --
         --  And also the call to Reset should come before the call to
         --  Asis.Implementation.Initialize, but we cannot enforce this.
         Ada.Exceptions.Raise_Exception
           (Program_Error'Identity,
            "Program logic error: ASIS already initialized!");
      end if;
   end Check_Asis_Inactive;

   function To_Name_Id
     (Name : in String)
     return Types.Name_Id
   is
   begin
      --  GNAT (and its project manager) use lower case internally for user-
      --  defined names.
      Namet.Name_Buffer (1 .. Name'Length) := Name;
      Namet.Name_Len                       := Name'Length;
      return Namet.Name_Find;
   end To_Name_Id;

   function To_Name
     (Name : in Types.Name_Id)
     return String
   is
      use type Types.Name_Id;
   begin
      if Name in Types.Error_Name_Or_No_Name then return ""; end if;
      Namet.Get_Decoded_Name_String (Name);
      return Namet.Name_Buffer (1 .. Namet.Name_Len);
   end To_Name;

   function To_File_Name
     (Name : in Types.Name_Id)
     return String
   is
      use type Types.Name_Id;
   begin
      if Name in Types.Error_Name_Or_No_Name then return ""; end if;
      Namet.Get_Decoded_Name_String (Name);
      return AD.Text_Utilities.Canonical
               (Namet.Name_Buffer (1 .. Namet.Name_Len));
   end To_File_Name;

   function To_File_Name
     (Name : in Types.String_Id)
     return String
   is
      --  This operation is unreferenced on the newer GNATs, but needed on the
      --  older GNAT versions.
      use type Types.String_Id;
   begin
      if Name = Types.No_String then return ""; end if;
      Stringt.String_To_Name_Buffer (Name);
      return AD.Text_Utilities.Canonical
               (Namet.Name_Buffer (1 .. Namet.Name_Len));
   end To_File_Name;

   pragma Warnings (Off, To_File_Name);
   --  Switch off warnings for this operation. See comment above.

   function Make_Absolute
     (Path_Name : in String;
      Project   : in Prj.Project_Id)
     return String
   is
   begin
      if Util.Pathes.Is_Absolute_Path (Path_Name) then
         return Path_Name;
      end if;
      return Util.Pathes.Concat
               (To_File_Name (Prj.Projects.Table (Project).Directory),
                Path_Name);
   end Make_Absolute;

   function Get_Parent
     (Project : in Prj.Project_Id)
     return Prj.Project_Id
      is separate;

   function Extends
     (Leaf_Project  : in Prj.Project_Id;
      Other_Project : in Prj.Project_Id)
     return Boolean
   is
      use type Prj.Project_Id;

      Current : Prj.Project_Id := Leaf_Project;

   begin
      --  No project extends a non-existing project.
      if Other_Project = Prj.No_Project then return False; end if;
      while Current /= Other_Project and then Current /= Prj.No_Project loop
         Current := Get_Parent (Current);
      end loop;
      return Current = Other_Project;
   end Extends;

   type Unit_Desc is
      record
         Unit_Name : ASU.Unbounded_String;
         Spec_Name : ASU.Unbounded_String;
         Body_Name : ASU.Unbounded_String;
      end record;

   package Unit_Lists is
      new GAL.Containers.Simple (Unit_Desc, GAL.Storage.Standard);

   All_Units : Unit_Lists.Simple_Container;

   procedure Add_Unit
     (Unit_Name : in String;
      Spec_Name : in String;
      Body_Name : in String)
   is
      New_Unit : constant Unit_Desc :=
        (Unit_Name => ASU.To_Unbounded_String (Unit_Name),
         Spec_Name => ASU.To_Unbounded_String (Spec_Name),
         Body_Name => ASU.To_Unbounded_String (Body_Name));
   begin
      AD.Messages.Debug
        ("Adding " & Unit_Name &
         " Spec => " & Spec_Name &
         " Body => " & Body_Name);
      Unit_Lists.Add (New_Unit, All_Units);
      AD.Known_Units.Add (Spec_Name, Unit_Name);
   end Add_Unit;

   procedure Get_Units
     (Project : in Prj.Project_Id)
   is
      --  Add only the units from this project or from any extended projects,
      --  but not those from imported projects!
      use Prj.Com;

      function Get_File_Name
        (Info : in File_Name_Data)
        return String
      is

         function Get
           (Info : in File_Name_Data)
           return String
         is
            use type Types.Name_Id;
         begin
            if Info.Name in Types.Error_Name_Or_No_Name then return ""; end if;
            return To_File_Name (Info.Path);
         end Get;

         Name : constant String := Get (Info);

      begin
         if Name'Length = 0 then return Name; end if;
         return Make_Absolute (Name, Info.Project);
      end Get_File_Name;

   begin
      for Unit in Units.First .. Units.Last loop
         declare
            Info : constant Unit_Data := Units.Table (Unit);
         begin
            if Extends (Project, Info.File_Names (Specification).Project) then
               --  Note: make these filenames absolute!
               Add_Unit (Util.Strings.To_Mixed (To_Name (Info.Name)),
                         Get_File_Name (Info.File_Names (Specification)),
                         Get_File_Name (Info.File_Names (Body_Part)));
            end if;
         end;
      end loop;
   end Get_Units;

   procedure Get_Source_File_List
     (File        : in out Ada.Text_IO.File_Type;
      For_Project : in     Boolean)
   is

      function Quote
        (S      : in String;
         Delim  : in Character := '"';
         Escape : in Character := '"')
        return String
        renames Util.Strings.Quote;

   begin
      if Unit_Lists.Nof_Elements (All_Units) = 0 then return; end if;
      declare
         First_Source : Boolean := True;

         procedure Process_Unit
           (Unit : in out Unit_Desc;
            Quit : in out Boolean)
         is
         begin
            if not For_Project then
               --  Extract only specs.
               if ASU.Length (Unit.Spec_Name) > 0 then
                  --  We have a spec.
                  Ada.Text_IO.Put_Line
                    (File, "--  " & ASU.To_String (Unit.Unit_Name));
                  Ada.Text_IO.Put_Line
                    (File, ASU.To_String (Unit.Spec_Name));
               end if;
            else
               --  For project file:
               if ASU.Length (Unit.Spec_Name) > 0 then
                  if not First_Source then
                     Ada.Text_IO.Put (File, ", ");
                  end if;
                  First_Source := False;
                  Ada.Text_IO.Put_Line
                    (File, '"' & Quote (ASU.To_String (Unit.Spec_Name)) & '"');
               end if;
               if ASU.Length (Unit.Body_Name) > 0 then
                  if not First_Source then
                     Ada.Text_IO.Put (File, ", ");
                  end if;
                  First_Source := False;
                  Ada.Text_IO.Put_Line
                    (File, '"' & Quote (ASU.To_String (Unit.Body_Name)) & '"');
               end if;
            end if;
            Quit := False;
         end Process_Unit;

         procedure Traverse is new Unit_Lists.Traverse_G (Process_Unit);

      begin
         Traverse (All_Units);
      end;
   end Get_Source_File_List;

   procedure Handle_Project_File
     (Name : in String)
   is

      procedure Set
        (Path   : in out ASU.Unbounded_String;
         From   : in     String;
         Option : in     String)
      is
      begin
         declare
            Directories : constant String :=
              Util.Strings.Replace
                (Source => From,
                 What   => "" & GNAT.OS_Lib.Path_Separator,
                 By     => ' ' & Option);
         begin
            if Directories'Length > 0 then
               Path := ASU.To_Unbounded_String (Option & Directories);
            end if;
         end;
      end Set;

      procedure Find_Variable
        (Project      : in     Prj.Project_Id;
         Name         : in     Types.Name_Id;
         Var          :    out Prj.Variable_Id;
         Found_In     :    out Prj.Project_Id;
         Recursive    : in     Boolean := True;
         Is_Attribute : in     Boolean := False)
      is
         use Prj;
         use type Types.Name_Id;
      begin
         if Project = No_Project then
            Var      := No_Variable;
            Found_In := No_Project;
            return;
         end if;
         if Is_Attribute then
            Var := Prj.Projects.Table (Project).Decl.Attributes;
         else
            Var := Prj.Projects.Table (Project).Decl.Variables;
         end if;
         while Var /= No_Variable loop
            --  Check the name.
            if Variable_Elements.Table (Var).Name = Name then
               Found_In := Project;
               return;
            end if;
            Var := Variable_Elements.Table (Var).Next;
         end loop;
         if Recursive then
            --  Check extended projects:
            Find_Variable (Get_Parent (Project), Name, Var, Found_In);
         else
            Var := No_Variable;
         end if;
      end Find_Variable;

      function Get_Variable
        (Variable_Name : in String;
         Project       : in Prj.Project_Id;
         Is_Attribute  : in Boolean)
        return String
      is

         function Kind_Name
           (Is_Attribute : in Boolean)
           return String
         is
         begin
            if Is_Attribute then
               return "Attribute";
            else
               return "Variable";
            end if;
         end Kind_Name;

         Kind     : constant String := Kind_Name (Is_Attribute);

         use Prj;

         Var      : Variable_Id := No_Variable;
         Found_In : Project_Id  := No_Project;

      begin
         Find_Variable
           (Project, To_Name_Id (Util.Strings.To_Lower (Variable_Name)),
            Var, Found_In, Recursive => False, Is_Attribute => Is_Attribute);
         if Var = No_Variable then return ""; end if;
         declare
            Val : constant Variable_Value :=
              Prj.Variable_Elements.Table (Var).Value;
         begin
            case Val.Kind is
               when Prj.Undefined =>
                  --  Shouldn't happen!
                  null;
               when Prj.Single =>
                  return Make_Absolute (To_File_Name (Val.Value), Found_In);
               when others =>
                  Ada.Exceptions.Raise_Exception
                    (Project_Error'Identity,
                     Kind & ' ' & Variable_Name & " in project file " &
                     To_File_Name (Prj.Projects.Table (Found_In).Path_Name) &
                     " must be a single string!");
            end case;
         end;
         return "";
      end Get_Variable;

      function Get_Tree_Directory
        (Project : in Prj.Project_Id)
        return String
      is
         use Prj;

         function Get_Tree_Dir
           return String
         is
         begin
            Own_Project_File := False;
            --  Support for a maybe future Tree_Dir attribute:
            declare
               Path : constant String :=
                 Get_Variable (Tree_Dir_Attribute, Project, True);
            begin
               if Path'Length > 0 then
                  return Make_Absolute (Path, Project);
               end if;
            end;
            Own_Project_File := True;
            --  Doesn't exist, use our variable:
            declare
               Path : constant String :=
                 Get_Variable (AdaBrowse_Tree, Project, False);
            begin
               return Make_Absolute (Path, Project);
            end;
         end Get_Tree_Dir;

         function Get_Obj_Dir
           return String
         is
         begin
            if Prj.Projects.Table (Project).Object_Directory not in
               Types.Error_Name_Or_No_Name
            then
               return Make_Absolute
                        (To_File_Name
                           (Prj.Projects.Table (Project).Object_Directory),
                         Project);
            else
               return Make_Absolute ("", Project);
            end if;
         end Get_Obj_Dir;

         Tree_Dir : constant String := Get_Tree_Dir;
         Obj_Dir  : constant String := Get_Obj_Dir;

      begin
         --  Check that the tree directory actually exists and is a directory:
         if not AD.File_Ops.Is_Directory (Tree_Dir) then
            Ada.Exceptions.Raise_Exception
              (Project_Error'Identity,
               "tree directory """ & Tree_Dir & """ doesn't exist?" &
               " (Project file " &
               To_File_Name (Prj.Projects.Table (Project).Path_Name) & ')');
         end if;
         --  Check the project's object directory:
         if Util.Strings.Equal (Tree_Dir, Obj_Dir) then
            Ada.Exceptions.Raise_Exception
              (Project_Error'Identity,
               "tree and object directory must be different in project file " &
               To_File_Name (Prj.Projects.Table (Project).Path_Name));
         end if;
         --  Tree_Dir and Obj_Dir are textually different. Now do a stronger
         --  check: create a file through one path and try to open it through
         --  the other.
         declare
            use Ada.Text_IO;
            File      : File_Type;
            File_Name : ASU.Unbounded_String;
            Are_Equal : Boolean := False;
         begin
            AD.File_Ops.Create_Unique_File
              (File, File_Name,
               Util.Pathes.Concat (Tree_Dir, "ABTest"),
               "txt");
            if ASU.Length (File_Name) > 0 then
               Put_Line (File, ASU.To_String (File_Name));
               Close (File);
               begin
                  Open (File, In_File,
                        Util.Pathes.Concat
                        (Obj_Dir,
                         Util.Pathes.Name (ASU.To_String (File_Name))));
                  --  Oops, that worked! Check the content:
                  declare
                     Line : constant String :=
                       Util.Files.Text_IO.Get_Line (File);
                  begin
                     if Line = ASU.To_String (File_Name) then
                        --  Yes, they are the same!
                        Delete (File);
                        Are_Equal := True;
                     else
                        --  Hmmm, apparently not.
                        Close (File);
                        Open (File, In_File, ASU.To_String (File_Name));
                        Delete (File);
                     end if;
                  end;
               exception
                  when others =>
                     --  Didn't work, so I guess we may presume the two
                     --  pathes really refer to different directories:
                     begin
                        Open (File, In_File, ASU.To_String (File_Name));
                        Delete (File);
                     exception
                        when others =>
                           if Is_Open (File) then Close (File); end if;
                     end;
               end;
            else
               --  We were not able to create a unique file.
               Ada.Exceptions.Raise_Exception
                 (Project_Error'Identity,
                  "tree directory """ & Tree_Dir & """ isn't writeable?" &
                  " (Project file " &
                  To_File_Name (Prj.Projects.Table (Project).Path_Name) & ')');
            end if;
            if Are_Equal then
               Ada.Exceptions.Raise_Exception
                 (Project_Error'Identity,
                  "tree and object directory appear to be the same " &
                  "in project file " &
                  To_File_Name (Prj.Projects.Table (Project).Path_Name));
            end if;
         end;
         return Tree_Dir;
      end Get_Tree_Directory;

      procedure Generate_Renaming
        (File           : in Ada.Text_IO.File_Type;
         Package_Name   : in String;
         Parent_Name    : in String;
         Parent_Project : in Prj.Project_Id)
      is
      begin
         --  Check that there is such a package.
         declare
            use Prj;
            use type Types.Name_Id;
            Package_Name_Id : constant Types.Name_Id :=
              To_Name_Id (Util.Strings.To_Lower (Package_Name));
            Ptr : Package_Id;
         begin
            Ptr := Prj.Projects.Table (Parent_Project).Decl.Packages;
            while Ptr /= No_Package loop
               exit when Packages.Table (Ptr).Name = Package_Name_Id and then
                         Packages.Table (Ptr).Parent = No_Package;
               Ptr := Prj.Packages.Table (Ptr).Next;
            end loop;
            if Ptr = No_Package then return; end if;
         end;
         Ada.Text_IO.Put_Line
           (File,
            "   package " & Package_Name & " renames " &
            Parent_Name & '.' & Package_Name & ';');
         Ada.Text_IO.New_Line (File);
      end Generate_Renaming;

      procedure Import_Sources
        (File : in out Ada.Text_IO.File_Type)
      is
      begin
         Ada.Text_IO.Put_Line (File, "   for Source_Files use (");
         Get_Source_File_List (File, True);
         Ada.Text_IO.Put_Line (File, "   ); -- End of sources");
         Ada.Text_IO.New_Line (File);
      end Import_Sources;

      procedure Do_Config
        (File_Name  : in String;
         In_Project : in Prj.Project_Id)
      is
         Current_Dir : constant String :=
           GNAT.Directory_Operations.Get_Current_Dir;
      begin
         GNAT.Directory_Operations.Change_Dir
           (To_File_Name (Prj.Projects.Table (In_Project).Directory));
         AD.Config.Configure (File_Name);
         GNAT.Directory_Operations.Change_Dir (Current_Dir);
      exception
         when others =>
            GNAT.Directory_Operations.Change_Dir (Current_Dir);
            raise;
      end Do_Config;

      use type Prj.Project_Id;

      Project : Prj.Project_Id;

   begin
      Check_Asis_Inactive;
      if Project_Manager_Activated then
         Ada.Exceptions.Raise_Exception
           (Program_Error'Identity,
            "Program logic error: Project manager already active!");
      end if;
      if not Project_Manager_Initialized then
         Ada.Exceptions.Raise_Exception
           (Program_Error'Identity,
            "Program logic error: Project manager not initialized!");
      end if;
      Project_Manager_Activated := True;
      Own_Project_File          := False;

      --  Set verbosity to default (i.e., quiet unless there are errors.)
      Prj.Pars.Set_Verbosity (Prj.Default);
      --  Parse the project file:
      Prj.Pars.Parse
        (Project           => Project,
         Project_File_Name => Name);
      if Project = Prj.No_Project then
         Ada.Exceptions.Raise_Exception
           (Project_Error'Identity,
            "Project file '" & Name & "' processing failed.");
      end if;

      Project_File_Name :=
        ASU.To_Unbounded_String
          (To_File_Name (Prj.Projects.Table (Project).Path_Name));

      Output_Directory :=
        ASU.To_Unbounded_String
          (Get_Variable (AdaBrowse_Output, Project, False));
      Tree_Directory   :=
        ASU.To_Unbounded_String (Get_Tree_Directory (Project));

      Set (Source_Directories, Prj.Env.Ada_Include_Path (Project).all, "-I");

      Get_Units (Project);

      if Own_Project_File then
         --  Create a new copy of the project file just for AdaBrowse
         --  purposes with a unique name.
         declare
            use Ada.Text_IO;
            File                : File_Type;
            File_Name           : ASU.Unbounded_String;
            Parent_Project_Name : constant String :=
              Util.Strings.To_Mixed
                (To_Name (Prj.Projects.Table (Project).Name));
         begin
            AD.File_Ops.Create_Unique_File
              (File, File_Name,
               Util.Pathes.Concat
                 (Util.Pathes.Path
                    (To_File_Name (Prj.Projects.Table (Project).Path_Name)),
                  "AB"),
               "gpr");
            Put_Line
              (File,
               "--  This file has been generated automatically by AdaBrowse.");
            Put_Line
              (File, "--  It should be deleted before AdaBrowse terminates.");
            New_Line (File);
            Put_Line
              (File,
               "project " & Util.Pathes.Base_Name (ASU.To_String (File_Name)));
            Put_Line
              (File,
               "  extends """ &
               Util.Pathes.Name
                 (To_File_Name (Prj.Projects.Table (Project).Path_Name)) &
               '"');
            Put_Line (File, "is");
            New_Line (File);
            Put_Line (File,
                      "   for Object_Dir use """ &
                      ASU.To_String (Tree_Directory) & """;");
            New_Line (File);
            --  We need to import the sources, otherwise the project manager
            --  will still use the parent project's object directory!
            Import_Sources (File);
            --  Generate renamings for existing parent project's packages we
            --  need.
            Generate_Renaming (File, "Naming", Parent_Project_Name, Project);
            Generate_Renaming (File, "Builder", Parent_Project_Name, Project);
            Generate_Renaming (File, "Compiler", Parent_Project_Name, Project);
            --  I think that's it.
            Put_Line
              (File,
               "end " &
               Util.Pathes.Base_Name (ASU.To_String (File_Name)) & ';');
            Close (File);
            Project_File_Name := File_Name;
         exception
            when E : others =>
               AD.Messages.Debug
                 ("Temp project file " & ASU.To_String (File_Name) &
                  " FAILED: " &
                  Ada.Exceptions.Exception_Information (E));
               if Is_Open (File) then
                  declare
                     use type AD.Messages.Verbosity;
                  begin
                     if AD.Messages.Get_Mode = AD.Messages.Including_Debug then
                        Close (File);
                     else
                        Delete (File);
                     end if;
                  end;
               end if;
               Ada.Exceptions.Raise_Exception
                 (Project_Error'Identity,
                  "Cannot create temporary project file!");
         end;
      end if;
      --  Check for AdaBrowse_Configurations...
      Handle_Configurations :
      declare
         use Prj;
         Var      : Variable_Id := No_Variable;
         Found_In : Project_Id  := No_Project;
      begin
         Find_Variable
           (Project,
            To_Name_Id (Util.Strings.To_Lower (AdaBrowse_Configurations)),
            Var, Found_In);
         if Var = No_Variable then return; end if;
         Process_Variable :
         declare
            Val : constant Variable_Value :=
              Prj.Variable_Elements.Table (Var).Value;
         begin
            case Val.Kind is
               when Prj.Undefined =>
                  --  Shouldn't happen!
                  return;
               when Prj.Single =>
                  declare
                     Output : constant String := To_File_Name (Val.Value);
                  begin
                     if Output'Length > 0 then
                        Do_Config (Output, Found_In);
                     end if;
                  end;
               when Prj.List =>
                  Process_String_List :
                  declare
                     Ptr : Prj.String_List_Id := Val.Values;
                  begin
                     while Ptr /= Prj.Nil_String loop
                        declare
                           File_Name : constant String :=
                             To_File_Name
                               (Prj.String_Elements.Table (Ptr).Value);
                        begin
                           if File_Name'Length > 0 then
                              Do_Config (File_Name, Found_In);
                           end if;
                        end;
                        Ptr := Prj.String_Elements.Table (Ptr).Next;
                     end loop;
                  end Process_String_List;
            end case;
         end Process_Variable;
      end Handle_Configurations;
      Prj.Reset;
      Namet.Reset_Name_Table; --  Just to be sure...
   exception
      when others =>
         begin
            Prj.Reset;
            Namet.Reset_Name_Table;
         exception
            when others => null;
         end;
         raise;
   end Handle_Project_File;

   procedure Get_Source_File_List
     (File : in out Ada.Text_IO.File_Type)
   is
   begin
      Get_Source_File_List (File, False);
   end Get_Source_File_List;

   function Get_Source_Directories
     return String
   is
   begin
      return ASU.To_String (Source_Directories);
   end Get_Source_Directories;

   function Get_Tree_Directory
     return String
   is
   begin
      return ASU.To_String (Tree_Directory);
   end Get_Tree_Directory;

   function Get_Output_Directory
     return String
   is
   begin
      return ASU.To_String (Output_Directory);
   end Get_Output_Directory;

   function Get_Project_File_Name
     return String
   is
   begin
      return ASU.To_String (Project_File_Name);
   end Get_Project_File_Name;

   function Project_Version
     return String
   is
   begin
      return "p";
   end Project_Version;

   procedure Reset
     (On_Error : in Boolean)
   is
   begin
      if not On_Error then
         if Own_Project_File then
            declare
               use Ada.Text_IO;
               File : File_Type;
            begin
               Open
                 (File, Ada.Text_IO.In_File,
                  ASU.To_String (Project_File_Name));
               Delete (File);
            exception
               when E : others =>
                  if Is_Open (File) then Close (File); end if;
                  AD.Messages.Debug
                    ("Can't delete file """ &
                     ASU.To_String (Project_File_Name) & """:" &
                     Ada.Exceptions.Exception_Information (E));
            end;
         end if;
      end if;
   end Reset;

   procedure Define_Variable
     (Name  : in String;
      Value : in String)
   is
   begin
      if Project_Manager_Activated then return; end if;
      if not Project_Manager_Initialized then
         Ada.Exceptions.Raise_Exception
           (Program_Error'Identity,
            "Program logic error: Project manager not initialized!");
      end if;
      Prj.Ext.Add (Name, Value);
   end Define_Variable;

   procedure Initialize
   is
   begin
      if Project_Manager_Initialized then
         Ada.Exceptions.Raise_Exception
           (Program_Error'Identity,
            "Program logic error: Project manager already initialized!");
      end if;
      --  Do the required initializations:
      Csets.Initialize;
      Namet.Initialize;
      Snames.Initialize;
      Prj.Initialize;
      Project_Manager_Initialized := True;
   end Initialize;

end AD.Projects.Impl_Yes;
