Index: prj-nmsc.adb
===================================================================
--- prj-nmsc.adb	(revision 164969)
+++ prj-nmsc.adb	(working copy)
@@ -211,6 +211,33 @@ package body Prj.Nmsc is
    --  exceptions, and copied into the Source_Names and Unit_Exceptions tables
    --  as appropriate.
 
+   type Search_Type is (Search_Files, Search_Directories);
+   pragma Unreferenced (Search_Files);
+
+   generic
+      with procedure Callback
+        (Path_Id         : Path_Name_Type;
+         Display_Path_Id : Path_Name_Type;
+         Pattern_Index   : Natural);
+   procedure Expand_Subdirectory_Pattern
+     (Project       : Project_Id;
+      Data          : in out Tree_Processing_Data;
+      Patterns      : String_List_Id;
+      Search_For    : Search_Type;
+      Resolve_Links : Boolean);
+   --  Search the subdirectories of Project's directory for files or
+   --  directories that match the globbing patterns found in Patterns (for
+   --  instance "**/*.adb"). Typically, Patterns will be the value of the
+   --  Source_Dirs or Excluded_Source_Dirs attributes.
+   --  Every time such a file or directory is found, the callback is called.
+   --  Resolve_Links indicates whether we should resolve links while
+   --  normalizing names.
+   --  In the callback, Pattern_Index is the index within Patterns where the
+   --  expanded pattern was found (1 for the first element of Patterns and
+   --  all its matching directories, then 2,...).
+   --  We use a generic and not an access-to-subprogram because in some cases
+   --  this code is compiled with the restriction No_Implicit_Dynamic_Code
+
    procedure Add_Source
      (Id                  : out Source_Id;
       Data                : in out Tree_Processing_Data;
@@ -4853,19 +4880,6 @@ package body Prj.Nmsc is
      (Project : Project_Id;
       Data    : in out Tree_Processing_Data)
    is
-      package Recursive_Dirs is new GNAT.Dynamic_HTables.Simple_HTable
-        (Header_Num => Header_Num,
-         Element    => Boolean,
-         No_Element => False,
-         Key        => Path_Name_Type,
-         Hash       => Hash,
-         Equal      => "=");
-      --  Hash table stores recursive source directories, to avoid looking
-      --  several times, and to avoid cycles that may be introduced by symbolic
-      --  links.
-
-      Visited : Recursive_Dirs.Instance;
-
       Object_Dir  : constant Variable_Value :=
                       Util.Value_Of
                         (Name_Object_Dir, Project.Decl.Attributes, Data.Tree);
@@ -4894,25 +4908,21 @@ package body Prj.Nmsc is
 
       Languages : constant Variable_Value :=
                       Prj.Util.Value_Of
-                        (Name_Languages, Project.Decl.Attributes, Data.Tree);
+          (Name_Languages, Project.Decl.Attributes, Data.Tree);
 
-      procedure Find_Source_Dirs
-        (From     : File_Name_Type;
-         Location : Source_Ptr;
-         Rank     : Natural;
-         Removed  : Boolean := False);
-      --  Find one or several source directories, and add (or remove, if
-      --  Removed is True) them to list of source directories of the project.
+      Remove_Source_Dirs : Boolean := False;
 
       procedure Add_To_Or_Remove_From_Source_Dirs
         (Path_Id         : Path_Name_Type;
          Display_Path_Id : Path_Name_Type;
-         Rank            : Natural;
-         Removed         : Boolean);
+         Rank            : Natural);
       --  When Removed = False, the directory Path_Id to the list of
       --  source_dirs if not already in the list. When Removed = True,
       --  removed directory Path_Id if in the list.
 
+      procedure Find_Source_Dirs is new Expand_Subdirectory_Pattern
+        (Add_To_Or_Remove_From_Source_Dirs);
+
       ---------------------------------------
       -- Add_To_Or_Remove_From_Source_Dirs --
       ---------------------------------------
@@ -4920,8 +4930,7 @@ package body Prj.Nmsc is
       procedure Add_To_Or_Remove_From_Source_Dirs
         (Path_Id         : Path_Name_Type;
          Display_Path_Id : Path_Name_Type;
-         Rank            : Natural;
-         Removed         : Boolean)
+         Rank            : Natural)
       is
          List       : String_List_Id;
          Prev       : String_List_Id;
@@ -4945,7 +4954,7 @@ package body Prj.Nmsc is
 
          --  The directory is in the list if List is not Nil_String
 
-         if not Removed and then List = Nil_String then
+         if not Remove_Source_Dirs and then List = Nil_String then
             if Current_Verbosity = High then
                Write_Str  ("   Adding Source Dir=");
                Write_Line (Get_Name_String (Display_Path_Id));
@@ -4991,7 +5000,7 @@ package body Prj.Nmsc is
             Data.Tree.Number_Lists.Table (Last_Src_Dir_Rank) :=
               (Number => Rank, Next => No_Number_List);
 
-         elsif Removed and then List /= Nil_String then
+         elsif Remove_Source_Dirs and then List /= Nil_String then
 
             --  Remove source dir, if present
 
@@ -5010,247 +5019,6 @@ package body Prj.Nmsc is
          end if;
       end Add_To_Or_Remove_From_Source_Dirs;
 
-      ----------------------
-      -- Find_Source_Dirs --
-      ----------------------
-
-      procedure Find_Source_Dirs
-        (From     : File_Name_Type;
-         Location : Source_Ptr;
-         Rank     : Natural;
-         Removed  : Boolean := False)
-      is
-         Directory : constant String := Get_Name_String (From);
-
-         procedure Recursive_Find_Dirs (Path : Name_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 : Name_Id) is
-            Dir  : Dir_Type;
-            Name : String (1 .. 250);
-            Last : Natural;
-
-            Non_Canonical_Path : Path_Name_Type := No_Path;
-            Canonical_Path     : Path_Name_Type := No_Path;
-
-            The_Path : constant String :=
-                         Normalize_Pathname
-                           (Get_Name_String (Path),
-                            Directory     =>
-                              Get_Name_String (Project.Directory.Display_Name),
-                            Resolve_Links => Opt.Follow_Links_For_Dirs) &
-                         Directory_Separator;
-
-            The_Path_Last : constant Natural :=
-                              Compute_Directory_Last (The_Path);
-
-         begin
-            Name_Len := The_Path_Last - The_Path'First + 1;
-            Name_Buffer (1 .. Name_Len) :=
-              The_Path (The_Path'First .. The_Path_Last);
-            Non_Canonical_Path := Name_Find;
-            Canonical_Path :=
-              Path_Name_Type
-                (Canonical_Case_File_Name (Name_Id (Non_Canonical_Path)));
-
-            --  To avoid processing the same directory several times, check
-            --  if the directory is already in Recursive_Dirs. If it is, then
-            --  there is nothing to do, just return. If it is not, put it there
-            --  and continue recursive processing.
-
-            if not Removed then
-               if Recursive_Dirs.Get (Visited, Canonical_Path) then
-                  return;
-               else
-                  Recursive_Dirs.Set (Visited, Canonical_Path, True);
-               end if;
-            end if;
-
-            Add_To_Or_Remove_From_Source_Dirs
-              (Path_Id         => Canonical_Path,
-               Display_Path_Id => Non_Canonical_Path,
-               Rank            => Rank,
-               Removed         => Removed);
-
-            --  Now look for subdirectories. Do that even when this directory
-            --  is already in the list, because some of its subdirectories may
-            --  not be in the list yet.
-
-            Open (Dir, The_Path (The_Path'First .. The_Path_Last));
-
-            loop
-               Read (Dir, Name, Last);
-               exit when Last = 0;
-
-               if Name (1 .. Last) /= "."
-                 and then Name (1 .. Last) /= ".."
-               then
-                  --  Avoid . and .. directories
-
-                  if Current_Verbosity = High then
-                     Write_Str  ("   Checking ");
-                     Write_Line (Name (1 .. Last));
-                  end if;
-
-                  declare
-                     Path_Name : constant String :=
-                                   Normalize_Pathname
-                                     (Name           => Name (1 .. Last),
-                                      Directory      =>
-                                        The_Path
-                                          (The_Path'First .. The_Path_Last),
-                                      Resolve_Links  =>
-                                        Opt.Follow_Links_For_Dirs,
-                                      Case_Sensitive => True);
-
-                  begin
-                     if Is_Directory (Path_Name) then
-
-                        --  We have found a new subdirectory, call self
-
-                        Name_Len := Path_Name'Length;
-                        Name_Buffer (1 .. Name_Len) := Path_Name;
-                        Recursive_Find_Dirs (Name_Find);
-                     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 and then not Removed then
-            Write_Str ("Find_Source_Dirs (""");
-            Write_Str (Directory);
-            Write_Str (",");
-            Write_Str (Rank'Img);
-            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) = Directory_Separator)
-         then
-            Name_Len := Directory'Length - 3;
-
-            if Name_Len = 0 then
-
-               --  Case of "/**": all directories in 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 File_Name_Type := Name_Find;
-               Root_Dir : constant String :=
-                            Normalize_Pathname
-                              (Name      => Name_Buffer (1 .. Name_Len),
-                               Directory =>
-                                 Get_Name_String
-                                   (Project.Directory.Display_Name),
-                               Resolve_Links  =>
-                                 Opt.Follow_Links_For_Dirs,
-                               Case_Sensitive => True);
-               Has_Error : Boolean := False;
-
-            begin
-               if Root_Dir'Length = 0 then
-                  Err_Vars.Error_Msg_File_1 := Base_Dir;
-                  Error_Or_Warning
-                    (Data.Flags, Data.Flags.Missing_Source_Files,
-                     "{ is not a valid directory.", Location, Project);
-                  Has_Error := Data.Flags.Missing_Source_Files = Error;
-               end if;
-
-               if not Has_Error then
-
-                  --  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;
-
-                  Name_Len := Root_Dir'Length;
-                  Name_Buffer (1 .. Name_Len) := Root_Dir;
-                  Recursive_Find_Dirs (Name_Find);
-
-                  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  : Path_Information;
-               Dir_Exists : Boolean;
-               Has_Error  : Boolean := False;
-
-            begin
-               Locate_Directory
-                 (Project     => Project,
-                  Name        => From,
-                  Path        => Path_Name,
-                  Dir_Exists  => Dir_Exists,
-                  Data        => Data,
-                  Must_Exist  => False);
-
-               if not Dir_Exists then
-                  Err_Vars.Error_Msg_File_1 := From;
-                  Error_Or_Warning
-                    (Data.Flags, Data.Flags.Missing_Source_Files,
-                     "{ is not a valid directory", Location, Project);
-                  Has_Error := Data.Flags.Missing_Source_Files = Error;
-               end if;
-
-               if not Has_Error then
-
-                  --  Links have been resolved if necessary, and Path_Name
-                  --  always ends with a directory separator.
-
-                  Add_To_Or_Remove_From_Source_Dirs
-                    (Path_Id         => Path_Name.Name,
-                     Display_Path_Id => Path_Name.Display_Name,
-                     Rank            => Rank,
-                     Removed         => Removed);
-               end if;
-            end;
-         end if;
-
-         Recursive_Dirs.Reset (Visited);
-      end Find_Source_Dirs;
-
       --  Local declarations
 
       Dir_Exists : Boolean;
@@ -5422,62 +5190,41 @@ package body Prj.Nmsc is
          --  No Source_Dirs specified: the single source directory is the one
          --  containing the project file.
 
+         Remove_Source_Dirs := False;
          Add_To_Or_Remove_From_Source_Dirs
            (Path_Id         => Project.Directory.Name,
             Display_Path_Id => Project.Directory.Display_Name,
-            Rank            => 1,
-            Removed         => False);
+            Rank            => 1);
 
       else
-         declare
-            Source_Dir : String_List_Id;
-            Element    : String_Element;
-            Rank       : Natural;
-         begin
-            --  Process the source directories for each element of the list
-
-            Source_Dir := Source_Dirs.Values;
-            Rank := 0;
-            while Source_Dir /= Nil_String loop
-               Element := Data.Tree.String_Elements.Table (Source_Dir);
-               Rank := Rank + 1;
-               Find_Source_Dirs
-                 (File_Name_Type (Element.Value), Element.Location, Rank);
-               Source_Dir := Element.Next;
-            end loop;
+         Remove_Source_Dirs := False;
+         Find_Source_Dirs
+           (Project         => Project,
+            Data            => Data,
+            Patterns        => Source_Dirs.Values,
+            Search_For      => Search_Directories,
+            Resolve_Links   => Opt.Follow_Links_For_Dirs);
 
-            if Project.Source_Dirs = Nil_String
-              and then Project.Qualifier = Standard
-            then
-               Error_Msg
-                 (Data.Flags,
-                  "a standard project cannot have no source directories",
-                  Source_Dirs.Location, Project);
-            end if;
-         end;
+         if Project.Source_Dirs = Nil_String
+           and then Project.Qualifier = Standard
+         then
+            Error_Msg
+              (Data.Flags,
+               "a standard project cannot have no source directories",
+               Source_Dirs.Location, Project);
+         end if;
       end if;
 
       if not Excluded_Source_Dirs.Default
         and then Excluded_Source_Dirs.Values /= Nil_String
       then
-         declare
-            Source_Dir : String_List_Id;
-            Element    : String_Element;
-
-         begin
-            --  Process the source directories for each element of the list
-
-            Source_Dir := Excluded_Source_Dirs.Values;
-            while Source_Dir /= Nil_String loop
-               Element := Data.Tree.String_Elements.Table (Source_Dir);
-               Find_Source_Dirs
-                 (File_Name_Type (Element.Value),
-                  Element.Location,
-                  0,
-                  Removed => True);
-               Source_Dir := Element.Next;
-            end loop;
-         end;
+         Remove_Source_Dirs := True;
+         Find_Source_Dirs
+           (Project         => Project,
+            Data            => Data,
+            Patterns        => Excluded_Source_Dirs.Values,
+            Search_For      => Search_Directories,
+            Resolve_Links   => Opt.Follow_Links_For_Dirs);
       end if;
 
       if Current_Verbosity = High then
@@ -6933,6 +6680,253 @@ package body Prj.Nmsc is
       end if;
    end Check_File;
 
+   ---------------------------------
+   -- Expand_Subdirectory_Pattern --
+   ---------------------------------
+
+   procedure Expand_Subdirectory_Pattern
+     (Project       : Project_Id;
+      Data          : in out Tree_Processing_Data;
+      Patterns      : String_List_Id;
+      Search_For    : Search_Type;
+      Resolve_Links : Boolean)
+   is
+      pragma Unreferenced (Search_For);
+      Project_Dir : constant String :=
+        Get_Name_String (Project.Directory.Display_Name);
+
+      package Recursive_Dirs is new GNAT.Dynamic_HTables.Simple_HTable
+        (Header_Num => Header_Num,
+         Element    => Boolean,
+         No_Element => False,
+         Key        => Path_Name_Type,
+         Hash       => Hash,
+         Equal      => "=");
+      --  Hash table stores recursive source directories, to avoid looking
+      --  several times, and to avoid cycles that may be introduced by symbolic
+      --  links.
+
+      Visited : Recursive_Dirs.Instance;
+
+      procedure Find_Pattern
+        (Pattern : String; Rank : Natural; Location : Source_Ptr);
+      --  Find a specific pattern
+
+      procedure Recursive_Find_Dirs (Normalized_Path : String; Rank : Natural);
+      --  Search all the subdirectories (recursively) of Path
+
+      -------------------------
+      -- Recursive_Find_Dirs --
+      -------------------------
+
+      procedure Recursive_Find_Dirs
+        (Normalized_Path : String; Rank : Natural)
+      is
+         Dir  : Dir_Type;
+         Name : String (1 .. 250);
+         Last : Natural;
+
+         Non_Canonical_Path : Path_Name_Type := No_Path;
+         Canonical_Path     : Path_Name_Type := No_Path;
+
+         The_Path_Last : constant Natural :=
+           Compute_Directory_Last (Normalized_Path);
+
+      begin
+         Name_Len := 0;
+         Add_Str_To_Name_Buffer
+           (Normalized_Path (Normalized_Path'First .. The_Path_Last));
+         Non_Canonical_Path := Name_Find;
+
+         Canonical_Path :=
+           Path_Name_Type
+             (Canonical_Case_File_Name (Name_Id (Non_Canonical_Path)));
+
+         if Recursive_Dirs.Get (Visited, Canonical_Path) then
+            return;
+         end if;
+
+         Recursive_Dirs.Set (Visited, Canonical_Path, True);
+
+         Callback (Canonical_Path, Non_Canonical_Path, Rank);
+
+         Open (Dir, Normalized_Path (Normalized_Path'First .. The_Path_Last));
+
+         loop
+            Read (Dir, Name, Last);
+            exit when Last = 0;
+
+            if Name (1 .. Last) /= "."
+              and then Name (1 .. Last) /= ".."
+            then
+               if Current_Verbosity = High then
+                  Write_Str  ("   Checking ");
+                  Write_Line (Name (1 .. Last));
+               end if;
+
+               declare
+                  Path_Name : constant String :=
+                    Normalize_Pathname
+                      (Name           => Name (1 .. Last),
+                       Directory      =>
+                         Normalized_Path
+                           (Normalized_Path'First .. The_Path_Last),
+                       Resolve_Links  => Resolve_Links)
+                    & Directory_Separator;
+               begin
+                  if Is_Directory (Path_Name) then
+                     Recursive_Find_Dirs (Path_Name, Rank);
+                  end if;
+               end;
+            end if;
+         end loop;
+
+         Close (Dir);
+
+      exception
+         when Directory_Error =>
+            null;
+      end Recursive_Find_Dirs;
+
+      ------------------
+      -- Find_Pattern --
+      ------------------
+
+      procedure Find_Pattern
+        (Pattern : String; Rank : Natural; Location : Source_Ptr) is
+      begin
+         if Current_Verbosity = High then
+            Write_Str ("Expand_Subdirectory_Pattern (""");
+            Write_Str (Pattern);
+            Write_Line (""")");
+         end if;
+
+         --  First, check if we are looking for a directory tree, indicated
+         --  by "/**" at the end.
+
+         if Pattern'Length >= 3
+           and then Pattern (Pattern'Last - 1 .. Pattern'Last) = "**"
+           and then (Pattern (Pattern'Last - 2) = '/'
+                     or else Pattern (Pattern'Last - 2) = Directory_Separator)
+         then
+            Name_Len := Pattern'Length - 3;
+
+            if Name_Len = 0 then
+
+               --  Case of "/**": all directories in file system
+
+               Name_Len := 1;
+               Name_Buffer (1) := Pattern (Pattern'First);
+
+            else
+               Name_Buffer (1 .. Name_Len) :=
+                 Pattern (Pattern'First .. Pattern'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 File_Name_Type := Name_Find;
+               Root_Dir : constant String :=
+                 Normalize_Pathname
+                   (Name          => Name_Buffer (1 .. Name_Len),
+                    Directory     => Project_Dir,
+                    Resolve_Links => Resolve_Links);
+               Has_Error : Boolean := False;
+
+            begin
+               if Root_Dir'Length = 0 then
+                  Err_Vars.Error_Msg_File_1 := Base_Dir;
+                  Error_Or_Warning
+                    (Data.Flags, Data.Flags.Missing_Source_Files,
+                     "{ is not a valid directory.", Location, Project);
+                  Has_Error := Data.Flags.Missing_Source_Files = Error;
+               end if;
+
+               if not Has_Error then
+
+                  --  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;
+
+                  if Root_Dir (Root_Dir'Last) /= Directory_Separator then
+                     Recursive_Find_Dirs
+                       (Root_Dir & Directory_Separator, Rank);
+                  else
+                     Recursive_Find_Dirs (Root_Dir, Rank);
+                  end if;
+
+                  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
+               Directory  : File_Name_Type;
+               Path_Name  : Path_Information;
+               Dir_Exists : Boolean;
+               Has_Error  : Boolean := False;
+
+            begin
+               Name_Len := Pattern'Length;
+               Name_Buffer (1 .. Name_Len) := Pattern;
+               Directory := Name_Find;
+
+               Locate_Directory
+                 (Project     => Project,
+                  Name        => Directory,
+                  Path        => Path_Name,
+                  Dir_Exists  => Dir_Exists,
+                  Data        => Data,
+                  Must_Exist  => False);
+
+               if not Dir_Exists then
+                  Err_Vars.Error_Msg_File_1 := Directory;
+                  Error_Or_Warning
+                    (Data.Flags, Data.Flags.Missing_Source_Files,
+                     "{ is not a valid directory", Location, Project);
+                  Has_Error := Data.Flags.Missing_Source_Files = Error;
+               end if;
+
+               if not Has_Error then
+
+                  --  Links have been resolved if necessary, and Path_Name
+                  --  always ends with a directory separator.
+
+                  Callback (Path_Name.Name, Path_Name.Display_Name, Rank);
+               end if;
+            end;
+         end if;
+      end Find_Pattern;
+
+      --  Start of processing for Expand_Subdirectory_Pattern
+
+      Pattern_Id : String_List_Id := Patterns;
+      Element    : String_Element;
+      Rank       : Natural := 1;
+   begin
+      while Pattern_Id /= Nil_String loop
+         Element := Data.Tree.String_Elements.Table (Pattern_Id);
+         Find_Pattern
+           (Get_Name_String (Element.Value), Rank, Element.Location);
+         Rank := Rank + 1;
+         Pattern_Id := Element.Next;
+      end loop;
+
+      Recursive_Dirs.Reset (Visited);
+   end Expand_Subdirectory_Pattern;
+
    ------------------------
    -- Search_Directories --
    ------------------------
Index: prj-err.adb
===================================================================
--- prj-err.adb	(revision 164906)
+++ prj-err.adb	(working copy)
@@ -95,6 +95,10 @@ package body Prj.Err is
          --  so we shouldn't report errors for projects that the user has no
          --  access to in any case.
 
+         if Current_Verbosity = High then
+            Write_Line ("Error in in-memory project, ignored");
+         end if;
+
          return;
       end if;
 
