* make.adb:
[platform/upstream/gcc.git] / gcc / ada / prj-nmsc.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                             P R J . N M S C                              --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                            $Revision$
10 --                                                                          --
11 --          Copyright (C) 2000-2001 Free Software Foundation, Inc.          --
12 --                                                                          --
13 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
14 -- terms of the  GNU General Public License as published  by the Free Soft- --
15 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
16 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
19 -- for  more details.  You should have  received  a copy of the GNU General --
20 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
21 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
22 -- MA 02111-1307, USA.                                                      --
23 --                                                                          --
24 -- GNAT was originally developed  by the GNAT team at  New York University. --
25 -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
26 --                                                                          --
27 ------------------------------------------------------------------------------
28
29 with Ada.Characters.Handling;    use Ada.Characters.Handling;
30 with Ada.Strings;                use Ada.Strings;
31 with Ada.Strings.Fixed;          use Ada.Strings.Fixed;
32 with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants;
33 with Errout;                     use Errout;
34 with GNAT.Case_Util;             use GNAT.Case_Util;
35 with GNAT.Directory_Operations;  use GNAT.Directory_Operations;
36 with GNAT.OS_Lib;                use GNAT.OS_Lib;
37 with Namet;                      use Namet;
38 with Osint;                      use Osint;
39 with Output;                     use Output;
40 with Prj.Com;                    use Prj.Com;
41 with Prj.Util;                   use Prj.Util;
42 with Snames;                     use Snames;
43 with Stringt;                    use Stringt;
44 with Types;                      use Types;
45
46 package body Prj.Nmsc is
47
48    Dir_Sep      : Character renames GNAT.OS_Lib.Directory_Separator;
49
50    Error_Report : Put_Line_Access := null;
51
52    procedure Check_Ada_Naming_Scheme (Naming : Naming_Data);
53    --  Check that the package Naming is correct.
54
55    procedure Check_Ada_Name
56      (Name : Name_Id;
57       Unit : out Name_Id);
58    --  Check that a name is a valid Ada unit name.
59
60    procedure Error_Msg (Msg : String; Flag_Location : Source_Ptr);
61    --  Output an error message. If Error_Report is null, simply call
62    --  Errout.Error_Msg. Otherwise, disregard Flag_Location and use
63    --  Error_Report.
64
65    function Get_Name_String (S : String_Id) return String;
66    --  Get the string from a String_Id
67
68    procedure Get_Unit
69      (File_Name    : Name_Id;
70       Naming       : Naming_Data;
71       Unit_Name    : out Name_Id;
72       Unit_Kind    : out Spec_Or_Body;
73       Needs_Pragma : out Boolean);
74    --  Find out, from a file name, the unit name, the unit kind and if a
75    --  specific SFN pragma is needed. If the file name corresponds to no
76    --  unit, then Unit_Name will be No_Name.
77
78    function Is_Illegal_Append (This : String) return Boolean;
79    --  Returns True if the string This cannot be used as
80    --  a Specification_Append, a Body_Append or a Separate_Append.
81
82    procedure Record_Source
83      (File_Name        : Name_Id;
84       Path_Name        : Name_Id;
85       Project          : Project_Id;
86       Data             : in out Project_Data;
87       Location         : Source_Ptr;
88       Current_Source   : in out String_List_Id);
89    --  Put a unit in the list of units of a project, if the file name
90    --  corresponds to a valid unit name.
91
92    procedure Show_Source_Dirs (Project : Project_Id);
93    --  List all the source directories of a project.
94
95    function Locate_Directory
96      (Name   : Name_Id;
97       Parent : Name_Id)
98       return   Name_Id;
99    --  Locate a directory.
100    --  Returns No_Name if directory does not exist.
101
102    function Path_Name_Of
103      (File_Name : String_Id;
104       Directory : Name_Id)
105       return      String;
106    --  Returns the path name of a (non project) file.
107    --  Returns an empty string if file cannot be found.
108
109    function Path_Name_Of
110      (File_Name : String_Id;
111       Directory : String_Id)
112       return      String;
113    --  Same as above except that Directory is a String_Id instead
114    --  of a Name_Id.
115
116    ---------------
117    -- Ada_Check --
118    ---------------
119
120    procedure Ada_Check
121      (Project      : Project_Id;
122       Report_Error : Put_Line_Access)
123    is
124       Data         : Project_Data;
125       Languages    : Variable_Value := Nil_Variable_Value;
126
127       procedure Check_Unit_Names (List : Array_Element_Id);
128       --  Check that a list of unit names contains only valid names.
129
130       procedure Find_Sources;
131       --  Find all the sources in all of the source directories
132       --  of a project.
133
134       procedure Get_Path_Name_And_Record_Source
135         (File_Name        : String;
136          Location         : Source_Ptr;
137          Current_Source   : in out String_List_Id);
138       --  Find the path name of a source in the source directories and
139       --  record the source, if found.
140
141       procedure Get_Sources_From_File
142         (Path     : String;
143          Location : Source_Ptr);
144       --  Get the sources of a project from a text file
145
146       ----------------------
147       -- Check_Unit_Names --
148       ----------------------
149
150       procedure Check_Unit_Names (List : Array_Element_Id) is
151          Current   : Array_Element_Id := List;
152          Element   : Array_Element;
153          Unit_Name : Name_Id;
154
155       begin
156          --  Loop through elements of the string list
157
158          while Current /= No_Array_Element loop
159             Element := Array_Elements.Table (Current);
160
161             --  Check that it contains a valid unit name
162
163             Check_Ada_Name (Element.Index, Unit_Name);
164
165             if Unit_Name = No_Name then
166                Error_Msg_Name_1 := Element.Index;
167                Error_Msg
168                  ("{ is not a valid unit name.",
169                   Element.Value.Location);
170
171             else
172                if Current_Verbosity = High then
173                   Write_Str ("   Body_Part (""");
174                   Write_Str (Get_Name_String (Unit_Name));
175                   Write_Line (""")");
176                end if;
177
178                Element.Index := Unit_Name;
179                Array_Elements.Table (Current) := Element;
180             end if;
181
182             Current := Element.Next;
183          end loop;
184       end Check_Unit_Names;
185
186       ------------------
187       -- Find_Sources --
188       ------------------
189
190       procedure Find_Sources is
191          Source_Dir     : String_List_Id := Data.Source_Dirs;
192          Element        : String_Element;
193          Dir            : Dir_Type;
194          Current_Source : String_List_Id := Nil_String;
195
196       begin
197          if Current_Verbosity = High then
198             Write_Line ("Looking for sources:");
199          end if;
200
201          --  For each subdirectory
202
203          while Source_Dir /= Nil_String loop
204             begin
205                Element := String_Elements.Table (Source_Dir);
206                if Element.Value /= No_String then
207                   declare
208                      Source_Directory : String
209                        (1 .. Integer (String_Length (Element.Value)));
210                   begin
211                      String_To_Name_Buffer (Element.Value);
212                      Source_Directory := Name_Buffer (1 .. Name_Len);
213                      if Current_Verbosity = High then
214                         Write_Str ("Source_Dir = ");
215                         Write_Line (Source_Directory);
216                      end if;
217
218                      --  We look to every entry in the source directory
219
220                      Open (Dir, Source_Directory);
221
222                      loop
223                         Read (Dir, Name_Buffer, Name_Len);
224
225                         if Current_Verbosity = High then
226                            Write_Str  ("   Checking ");
227                            Write_Line (Name_Buffer (1 .. Name_Len));
228                         end if;
229
230                         exit when Name_Len = 0;
231
232                         declare
233                            Path_Access : constant GNAT.OS_Lib.String_Access :=
234                                            Locate_Regular_File
235                                              (Name_Buffer (1 .. Name_Len),
236                                               Source_Directory);
237
238                            File_Name : Name_Id;
239                            Path_Name : Name_Id;
240
241                         begin
242                            --  If it is a regular file
243
244                            if Path_Access /= null then
245                               File_Name := Name_Find;
246                               Name_Len := Path_Access'Length;
247                               Name_Buffer (1 .. Name_Len) := Path_Access.all;
248                               Path_Name := Name_Find;
249
250                               --  We attempt to register it as a source.
251                               --  However, there is no error if the file
252                               --  does not contain a valid source (as
253                               --  indicated by Error_If_Invalid => False).
254                               --  But there is an error if we have a
255                               --  duplicate unit name.
256
257                               Record_Source
258                                 (File_Name        => File_Name,
259                                  Path_Name        => Path_Name,
260                                  Project          => Project,
261                                  Data             => Data,
262                                  Location         => No_Location,
263                                  Current_Source   => Current_Source);
264
265                            else
266                               if Current_Verbosity = High then
267                                  Write_Line
268                                    ("      Not a regular file.");
269                               end if;
270                            end if;
271                         end;
272                      end loop;
273
274                      Close (Dir);
275                   end;
276                end if;
277
278             exception
279                when Directory_Error =>
280                   null;
281             end;
282
283             Source_Dir := Element.Next;
284          end loop;
285
286          if Current_Verbosity = High then
287             Write_Line ("end Looking for sources.");
288          end if;
289
290          --  If we have looked for sources and found none, then
291          --  it is an error. If a project is not supposed to contain
292          --  any source, then we never call Find_Sources.
293
294          if Current_Source = Nil_String then
295             Error_Msg ("there are no sources in this project",
296                        Data.Location);
297          end if;
298       end Find_Sources;
299
300       -------------------------------------
301       -- Get_Path_Name_And_Record_Source --
302       -------------------------------------
303
304       procedure Get_Path_Name_And_Record_Source
305         (File_Name        : String;
306          Location         : Source_Ptr;
307          Current_Source   : in out String_List_Id)
308       is
309          Source_Dir : String_List_Id := Data.Source_Dirs;
310          Element    : String_Element;
311          Path_Name  : GNAT.OS_Lib.String_Access;
312          Found      : Boolean := False;
313          File       : Name_Id;
314
315       begin
316          if Current_Verbosity = High then
317             Write_Str  ("   Checking """);
318             Write_Str  (File_Name);
319             Write_Line (""".");
320          end if;
321
322          --  We look in all source directories for this file name
323
324          while Source_Dir /= Nil_String loop
325             Element := String_Elements.Table (Source_Dir);
326
327             if Current_Verbosity = High then
328                Write_Str ("      """);
329                Write_Str (Get_Name_String (Element.Value));
330                Write_Str (""": ");
331             end if;
332
333             Path_Name :=
334               Locate_Regular_File
335               (File_Name,
336                Get_Name_String (Element.Value));
337
338             if Path_Name /= null then
339                if Current_Verbosity = High then
340                   Write_Line ("OK");
341                end if;
342
343                Name_Len := File_Name'Length;
344                Name_Buffer (1 .. Name_Len) := File_Name;
345                File := Name_Find;
346                Name_Len := Path_Name'Length;
347                Name_Buffer (1 .. Name_Len) := Path_Name.all;
348
349                --  Register the source. Report an error if the file does not
350                --  correspond to a source.
351
352                Record_Source
353                  (File_Name        => File,
354                   Path_Name        => Name_Find,
355                   Project          => Project,
356                   Data             => Data,
357                   Location         => Location,
358                   Current_Source   => Current_Source);
359                Found := True;
360                exit;
361
362             else
363                if Current_Verbosity = High then
364                   Write_Line ("No");
365                end if;
366
367                Source_Dir := Element.Next;
368             end if;
369          end loop;
370
371       end Get_Path_Name_And_Record_Source;
372
373       ---------------------------
374       -- Get_Sources_From_File --
375       ---------------------------
376
377       procedure Get_Sources_From_File
378         (Path     : String;
379          Location : Source_Ptr)
380       is
381          File           : Prj.Util.Text_File;
382          Line           : String (1 .. 250);
383          Last           : Natural;
384          Current_Source : String_List_Id := Nil_String;
385
386          Nmb_Errors : constant Nat := Errors_Detected;
387
388       begin
389          if Current_Verbosity = High then
390             Write_Str  ("Opening """);
391             Write_Str  (Path);
392             Write_Line (""".");
393          end if;
394
395          --  We open the file
396
397          Prj.Util.Open (File, Path);
398
399          if not Prj.Util.Is_Valid (File) then
400             Error_Msg ("file does not exist", Location);
401          else
402             while not Prj.Util.End_Of_File (File) loop
403                Prj.Util.Get_Line (File, Line, Last);
404
405                --  If the line is not empty and does not start with "--",
406                --  then it must contains a file name.
407
408                if Last /= 0
409                  and then (Last = 1 or else Line (1 .. 2) /= "--")
410                then
411                   Get_Path_Name_And_Record_Source
412                     (File_Name => Line (1 .. Last),
413                      Location => Location,
414                      Current_Source => Current_Source);
415                   exit when Nmb_Errors /= Errors_Detected;
416                end if;
417             end loop;
418
419             Prj.Util.Close (File);
420
421          end if;
422
423          --  We should have found at least one source.
424          --  If not, report an error.
425
426          if Current_Source = Nil_String then
427             Error_Msg ("this project has no source", Location);
428          end if;
429       end Get_Sources_From_File;
430
431       --  Start of processing for Ada_Check
432
433    begin
434       Language_Independent_Check (Project, Report_Error);
435
436       Error_Report := Report_Error;
437
438       Data      := Projects.Table (Project);
439       Languages := Prj.Util.Value_Of (Name_Languages, Data.Decl.Attributes);
440
441       Data.Naming.Current_Language := Name_Ada;
442       Data.Sources_Present         := Data.Source_Dirs /= Nil_String;
443
444       if not Languages.Default then
445          declare
446             Current   : String_List_Id := Languages.Values;
447             Element   : String_Element;
448             Ada_Found : Boolean := False;
449
450          begin
451             Look_For_Ada : while Current /= Nil_String loop
452                Element := String_Elements.Table (Current);
453                String_To_Name_Buffer (Element.Value);
454                To_Lower (Name_Buffer (1 .. Name_Len));
455
456                if Name_Buffer (1 .. Name_Len) = "ada" then
457                   Ada_Found := True;
458                   exit Look_For_Ada;
459                end if;
460
461                Current := Element.Next;
462             end loop Look_For_Ada;
463
464             if not Ada_Found then
465
466                --  Mark the project file as having no sources for Ada
467
468                Data.Sources_Present := False;
469             end if;
470          end;
471       end if;
472
473       declare
474          Naming_Id : constant Package_Id :=
475                        Util.Value_Of (Name_Naming, Data.Decl.Packages);
476
477          Naming : Package_Element;
478
479       begin
480          --  If there is a package Naming, we will put in Data.Naming
481          --  what is in this package Naming.
482
483          if Naming_Id /= No_Package then
484             Naming := Packages.Table (Naming_Id);
485
486             if Current_Verbosity = High then
487                Write_Line ("Checking ""Naming"" for Ada.");
488             end if;
489
490             declare
491                Bodies : constant Array_Element_Id :=
492                                   Util.Value_Of
493                                     (Name_Implementation, Naming.Decl.Arrays);
494
495                Specifications : constant Array_Element_Id :=
496                                   Util.Value_Of
497                                     (Name_Specification, Naming.Decl.Arrays);
498
499             begin
500                if Bodies /= No_Array_Element then
501
502                   --  We have elements in the array Body_Part
503
504                   if Current_Verbosity = High then
505                      Write_Line ("Found Bodies.");
506                   end if;
507
508                   Data.Naming.Bodies := Bodies;
509                   Check_Unit_Names (Bodies);
510
511                else
512                   if Current_Verbosity = High then
513                      Write_Line ("No Bodies.");
514                   end if;
515                end if;
516
517                if Specifications /= No_Array_Element then
518
519                   --  We have elements in the array Specification
520
521                   if Current_Verbosity = High then
522                      Write_Line ("Found Specifications.");
523                   end if;
524
525                   Data.Naming.Specifications := Specifications;
526                   Check_Unit_Names (Specifications);
527
528                else
529                   if Current_Verbosity = High then
530                      Write_Line ("No Specifications.");
531                   end if;
532                end if;
533             end;
534
535             --  We are now checking if variables Dot_Replacement, Casing,
536             --  Specification_Append, Body_Append and/or Separate_Append
537             --  exist.
538
539             --  For each variable, if it does not exist, we do nothing,
540             --  because we already have the default.
541
542             --  Check Dot_Replacement
543
544             declare
545                Dot_Replacement : constant Variable_Value :=
546                                    Util.Value_Of
547                                      (Name_Dot_Replacement,
548                                       Naming.Decl.Attributes);
549
550             begin
551                pragma Assert (Dot_Replacement.Kind = Single,
552                               "Dot_Replacement is not a single string");
553
554                if not Dot_Replacement.Default then
555
556                   String_To_Name_Buffer (Dot_Replacement.Value);
557
558                   if Name_Len = 0 then
559                      Error_Msg ("Dot_Replacement cannot be empty",
560                                 Dot_Replacement.Location);
561
562                   else
563                      Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
564                      Data.Naming.Dot_Replacement := Name_Find;
565                      Data.Naming.Dot_Repl_Loc := Dot_Replacement.Location;
566                   end if;
567
568                end if;
569
570             end;
571
572             if Current_Verbosity = High then
573                Write_Str  ("  Dot_Replacement = """);
574                Write_Str  (Get_Name_String (Data.Naming.Dot_Replacement));
575                Write_Char ('"');
576                Write_Eol;
577             end if;
578
579             --  Check Casing
580
581             declare
582                Casing_String : constant Variable_Value :=
583                  Util.Value_Of (Name_Casing, Naming.Decl.Attributes);
584
585             begin
586                pragma Assert (Casing_String.Kind = Single,
587                               "Casing is not a single string");
588
589                if not Casing_String.Default then
590                   declare
591                      Casing_Image : constant String :=
592                                       Get_Name_String (Casing_String.Value);
593
594                   begin
595                      declare
596                         Casing : constant Casing_Type :=
597                           Value (Casing_Image);
598
599                      begin
600                         Data.Naming.Casing := Casing;
601                      end;
602
603                   exception
604                      when Constraint_Error =>
605                         if Casing_Image'Length = 0 then
606                            Error_Msg ("Casing cannot be an empty string",
607                                       Casing_String.Location);
608
609                         else
610                            Name_Len := Casing_Image'Length;
611                            Name_Buffer (1 .. Name_Len) := Casing_Image;
612                            Error_Msg_Name_1 := Name_Find;
613                            Error_Msg
614                              ("{ is not a correct Casing",
615                               Casing_String.Location);
616                         end if;
617                   end;
618                end if;
619             end;
620
621             if Current_Verbosity = High then
622                Write_Str  ("  Casing = ");
623                Write_Str  (Image (Data.Naming.Casing));
624                Write_Char ('.');
625                Write_Eol;
626             end if;
627
628             --  Check Specification_Suffix
629
630             declare
631                Ada_Spec_Suffix : constant Name_Id :=
632                  Prj.Util.Value_Of
633                    (Index => Name_Ada,
634                     In_Array => Data.Naming.Specification_Suffix);
635
636             begin
637                if Ada_Spec_Suffix /= No_Name then
638                   Data.Naming.Current_Spec_Suffix := Ada_Spec_Suffix;
639
640                else
641                   Data.Naming.Current_Spec_Suffix := Ada_Default_Spec_Suffix;
642                end if;
643             end;
644
645             if Current_Verbosity = High then
646                Write_Str  ("  Specification_Suffix = """);
647                Write_Str  (Get_Name_String (Data.Naming.Current_Spec_Suffix));
648                Write_Char ('"');
649                Write_Eol;
650             end if;
651
652             --  Check Implementation_Suffix
653
654             declare
655                Ada_Impl_Suffix : constant Name_Id :=
656                  Prj.Util.Value_Of
657                    (Index => Name_Ada,
658                     In_Array => Data.Naming.Implementation_Suffix);
659
660             begin
661                if Ada_Impl_Suffix /= No_Name then
662                   Data.Naming.Current_Impl_Suffix := Ada_Impl_Suffix;
663
664                else
665                   Data.Naming.Current_Impl_Suffix := Ada_Default_Impl_Suffix;
666                end if;
667             end;
668
669             if Current_Verbosity = High then
670                Write_Str  ("  Implementation_Suffix = """);
671                Write_Str  (Get_Name_String (Data.Naming.Current_Impl_Suffix));
672                Write_Char ('"');
673                Write_Eol;
674             end if;
675
676             --  Check Separate_Suffix
677
678             declare
679                Ada_Sep_Suffix : constant Variable_Value :=
680                  Prj.Util.Value_Of
681                  (Variable_Name => Name_Separate_Suffix,
682                   In_Variables  => Naming.Decl.Attributes);
683             begin
684                if Ada_Sep_Suffix.Default then
685                   Data.Naming.Separate_Suffix :=
686                     Data.Naming.Current_Impl_Suffix;
687
688                else
689                   String_To_Name_Buffer (Ada_Sep_Suffix.Value);
690
691                   if Name_Len = 0 then
692                      Error_Msg ("Separate_Suffix cannot be empty",
693                                 Ada_Sep_Suffix.Location);
694
695                   else
696                      Data.Naming.Separate_Suffix := Name_Find;
697                      Data.Naming.Sep_Suffix_Loc  := Ada_Sep_Suffix.Location;
698                   end if;
699
700                end if;
701
702             end;
703
704             if Current_Verbosity = High then
705                Write_Str  ("  Separate_Suffix = """);
706                Write_Str  (Get_Name_String (Data.Naming.Separate_Suffix));
707                Write_Char ('"');
708                Write_Eol;
709             end if;
710
711             --  Check if Data.Naming is valid
712
713             Check_Ada_Naming_Scheme (Data.Naming);
714
715          else
716             Data.Naming.Current_Spec_Suffix := Ada_Default_Spec_Suffix;
717             Data.Naming.Current_Impl_Suffix := Ada_Default_Impl_Suffix;
718             Data.Naming.Separate_Suffix     := Ada_Default_Impl_Suffix;
719          end if;
720       end;
721
722       --  If we have source directories, then find the sources
723
724       if Data.Sources_Present then
725          if Data.Source_Dirs = Nil_String then
726             Data.Sources_Present := False;
727
728          else
729             declare
730                Sources : constant Variable_Value :=
731                  Util.Value_Of
732                  (Name_Source_Files,
733                   Data.Decl.Attributes);
734
735                Source_List_File : constant Variable_Value :=
736                  Util.Value_Of
737                  (Name_Source_List_File,
738                   Data.Decl.Attributes);
739
740             begin
741                pragma Assert
742                  (Sources.Kind = List,
743                     "Source_Files is not a list");
744                pragma Assert
745                  (Source_List_File.Kind = Single,
746                     "Source_List_File is not a single string");
747
748                if not Sources.Default then
749                   if not Source_List_File.Default then
750                      Error_Msg
751                        ("?both variables source_files and " &
752                         "source_list_file are present",
753                         Source_List_File.Location);
754                   end if;
755
756                   --  Sources is a list of file names
757
758                   declare
759                      Current_Source : String_List_Id := Nil_String;
760                      Current        : String_List_Id := Sources.Values;
761                      Element        : String_Element;
762
763                   begin
764                      Data.Sources_Present := Current /= Nil_String;
765
766                      while Current /= Nil_String loop
767                         Element := String_Elements.Table (Current);
768                         String_To_Name_Buffer (Element.Value);
769
770                         declare
771                            File_Name : constant String :=
772                              Name_Buffer (1 .. Name_Len);
773
774                         begin
775                            Get_Path_Name_And_Record_Source
776                              (File_Name        => File_Name,
777                               Location         => Element.Location,
778                               Current_Source   => Current_Source);
779                            Current := Element.Next;
780                         end;
781                      end loop;
782                   end;
783
784                   --  No source_files specified.
785                   --  We check Source_List_File has been specified.
786
787                elsif not Source_List_File.Default then
788
789                   --  Source_List_File is the name of the file
790                   --  that contains the source file names
791
792                   declare
793                      Source_File_Path_Name : constant String :=
794                        Path_Name_Of
795                        (Source_List_File.Value,
796                         Data.Directory);
797
798                   begin
799                      if Source_File_Path_Name'Length = 0 then
800                         String_To_Name_Buffer (Source_List_File.Value);
801                         Error_Msg_Name_1 := Name_Find;
802                         Error_Msg
803                           ("file with sources { does not exist",
804                            Source_List_File.Location);
805
806                      else
807                         Get_Sources_From_File
808                           (Source_File_Path_Name,
809                            Source_List_File.Location);
810                      end if;
811                   end;
812
813                else
814                   --  Neither Source_Files nor Source_List_File has been
815                   --  specified.
816                   --  Find all the files that satisfy
817                   --  the naming scheme in all the source directories.
818
819                   Find_Sources;
820                end if;
821             end;
822          end if;
823       end if;
824
825       Projects.Table (Project) := Data;
826    end Ada_Check;
827
828    --------------------
829    -- Check_Ada_Name --
830    --------------------
831
832    procedure Check_Ada_Name
833      (Name : Name_Id;
834       Unit : out Name_Id)
835    is
836       The_Name        : String := Get_Name_String (Name);
837       Need_Letter     : Boolean := True;
838       Last_Underscore : Boolean := False;
839       OK              : Boolean := The_Name'Length > 0;
840
841    begin
842       for Index in The_Name'Range loop
843          if Need_Letter then
844
845             --  We need a letter (at the beginning, and following a dot),
846             --  but we don't have one.
847
848             if Is_Letter (The_Name (Index)) then
849                Need_Letter := False;
850
851             else
852                OK := False;
853
854                if Current_Verbosity = High then
855                   Write_Int  (Types.Int (Index));
856                   Write_Str  (": '");
857                   Write_Char (The_Name (Index));
858                   Write_Line ("' is not a letter.");
859                end if;
860
861                exit;
862             end if;
863
864          elsif Last_Underscore
865            and then (The_Name (Index) = '_' or else The_Name (Index) = '.')
866          then
867             --  Two underscores are illegal, and a dot cannot follow
868             --  an underscore.
869
870             OK := False;
871
872             if Current_Verbosity = High then
873                Write_Int  (Types.Int (Index));
874                Write_Str  (": '");
875                Write_Char (The_Name (Index));
876                Write_Line ("' is illegal here.");
877             end if;
878
879             exit;
880
881          elsif The_Name (Index) = '.' then
882
883             --  We need a letter after a dot
884
885             Need_Letter := True;
886
887          elsif The_Name (Index) = '_' then
888             Last_Underscore := True;
889
890          else
891             --  We need an letter or a digit
892
893             Last_Underscore := False;
894
895             if not Is_Alphanumeric (The_Name (Index)) then
896                OK := False;
897
898                if Current_Verbosity = High then
899                   Write_Int  (Types.Int (Index));
900                   Write_Str  (": '");
901                   Write_Char (The_Name (Index));
902                   Write_Line ("' is not alphanumeric.");
903                end if;
904
905                exit;
906             end if;
907          end if;
908       end loop;
909
910       --  Cannot end with an underscore or a dot
911
912       OK := OK and then not Need_Letter and then not Last_Underscore;
913
914       if OK then
915          Unit := Name;
916       else
917          --  Signal a problem with No_Name
918
919          Unit := No_Name;
920       end if;
921    end Check_Ada_Name;
922
923    -------------------------
924    -- Check_Naming_Scheme --
925    -------------------------
926
927    procedure Check_Ada_Naming_Scheme (Naming : Naming_Data) is
928    begin
929       --  Only check if we are not using the standard naming scheme
930
931       if Naming /= Standard_Naming_Data then
932          declare
933             Dot_Replacement       : constant String :=
934                                      Get_Name_String
935                                        (Naming.Dot_Replacement);
936
937             Specification_Suffix : constant String :=
938                                      Get_Name_String
939                                        (Naming.Current_Spec_Suffix);
940
941             Implementation_Suffix : constant String :=
942                                      Get_Name_String
943                                        (Naming.Current_Impl_Suffix);
944
945             Separate_Suffix       : constant String :=
946                                      Get_Name_String
947                                        (Naming.Separate_Suffix);
948
949          begin
950             --  Dot_Replacement cannot
951             --   - be empty
952             --   - start or end with an alphanumeric
953             --   - be a single '_'
954             --   - start with an '_' followed by an alphanumeric
955             --   - contain a '.' except if it is "."
956
957             if Dot_Replacement'Length = 0
958               or else Is_Alphanumeric
959                         (Dot_Replacement (Dot_Replacement'First))
960               or else Is_Alphanumeric
961                         (Dot_Replacement (Dot_Replacement'Last))
962               or else (Dot_Replacement (Dot_Replacement'First) = '_'
963                         and then
964                         (Dot_Replacement'Length = 1
965                           or else
966                            Is_Alphanumeric
967                              (Dot_Replacement (Dot_Replacement'First + 1))))
968               or else (Dot_Replacement'Length > 1
969                          and then
970                            Index (Source => Dot_Replacement,
971                                   Pattern => ".") /= 0)
972             then
973                Error_Msg
974                  ('"' & Dot_Replacement &
975                   """ is illegal for Dot_Replacement.",
976                   Naming.Dot_Repl_Loc);
977             end if;
978
979             --  Suffixs cannot
980             --   - be empty
981             --   - start with an alphanumeric
982             --   - start with an '_' followed by an alphanumeric
983
984             if Is_Illegal_Append (Specification_Suffix) then
985                Error_Msg
986                  ('"' & Specification_Suffix &
987                   """ is illegal for Specification_Suffix.",
988                   Naming.Spec_Suffix_Loc);
989             end if;
990
991             if Is_Illegal_Append (Implementation_Suffix) then
992                Error_Msg
993                  ('"' & Implementation_Suffix &
994                   """ is illegal for Implementation_Suffix.",
995                   Naming.Impl_Suffix_Loc);
996             end if;
997
998             if Implementation_Suffix /= Separate_Suffix then
999                if Is_Illegal_Append (Separate_Suffix) then
1000                   Error_Msg
1001                     ('"' & Separate_Suffix &
1002                      """ is illegal for Separate_Append.",
1003                      Naming.Sep_Suffix_Loc);
1004                end if;
1005             end if;
1006
1007             --  Specification_Suffix cannot have the same termination as
1008             --  Implementation_Suffix or Separate_Suffix
1009
1010             if Specification_Suffix'Length <= Implementation_Suffix'Length
1011               and then
1012                 Implementation_Suffix (Implementation_Suffix'Last -
1013                              Specification_Suffix'Length + 1 ..
1014                              Implementation_Suffix'Last) = Specification_Suffix
1015             then
1016                Error_Msg
1017                  ("Implementation_Suffix (""" &
1018                   Implementation_Suffix &
1019                   """) cannot end with" &
1020                   "Specification_Suffix  (""" &
1021                    Specification_Suffix & """).",
1022                   Naming.Impl_Suffix_Loc);
1023             end if;
1024
1025             if Specification_Suffix'Length <= Separate_Suffix'Length
1026               and then
1027                 Separate_Suffix
1028                   (Separate_Suffix'Last - Specification_Suffix'Length + 1
1029                     ..
1030                    Separate_Suffix'Last) = Specification_Suffix
1031             then
1032                Error_Msg
1033                  ("Separate_Suffix (""" &
1034                   Separate_Suffix &
1035                   """) cannot end with" &
1036                   " Specification_Suffix (""" &
1037                   Specification_Suffix & """).",
1038                   Naming.Sep_Suffix_Loc);
1039             end if;
1040          end;
1041       end if;
1042    end Check_Ada_Naming_Scheme;
1043
1044    ---------------
1045    -- Error_Msg --
1046    ---------------
1047
1048    procedure Error_Msg (Msg : String; Flag_Location : Source_Ptr) is
1049
1050       Error_Buffer : String (1 .. 5_000);
1051       Error_Last   : Natural := 0;
1052       Msg_Name     : Natural := 0;
1053       First        : Positive := Msg'First;
1054
1055       procedure Add (C : Character);
1056       --  Add a character to the buffer
1057
1058       procedure Add (S : String);
1059       --  Add a string to the buffer
1060
1061       procedure Add (Id : Name_Id);
1062       --  Add a name to the buffer
1063
1064       ---------
1065       -- Add --
1066       ---------
1067
1068       procedure Add (C : Character) is
1069       begin
1070          Error_Last := Error_Last + 1;
1071          Error_Buffer (Error_Last) := C;
1072       end Add;
1073
1074       procedure Add (S : String) is
1075       begin
1076          Error_Buffer (Error_Last + 1 .. Error_Last + S'Length) := S;
1077          Error_Last := Error_Last + S'Length;
1078       end Add;
1079
1080       procedure Add (Id : Name_Id) is
1081       begin
1082          Get_Name_String (Id);
1083          Add (Name_Buffer (1 .. Name_Len));
1084       end Add;
1085
1086    --  Start of processing for Error_Msg
1087
1088    begin
1089       if Error_Report = null then
1090          Errout.Error_Msg (Msg, Flag_Location);
1091          return;
1092       end if;
1093
1094       if Msg (First) = '\' then
1095
1096          --  Continuation character, ignore.
1097
1098          First := First + 1;
1099
1100       elsif Msg (First) = '?' then
1101
1102          --  Warning character. It is always the first one,
1103          --  in this package.
1104
1105          First := First + 1;
1106          Add ("Warning: ");
1107       end if;
1108
1109       for Index in First .. Msg'Last loop
1110          if Msg (Index) = '{' or else Msg (Index) = '%' then
1111
1112             --  Include a name between double quotes.
1113
1114             Msg_Name := Msg_Name + 1;
1115             Add ('"');
1116
1117             case Msg_Name is
1118                when 1 => Add (Error_Msg_Name_1);
1119
1120                when 2 => Add (Error_Msg_Name_2);
1121
1122                when 3 => Add (Error_Msg_Name_3);
1123
1124                when others => null;
1125             end case;
1126
1127             Add ('"');
1128
1129          else
1130             Add (Msg (Index));
1131          end if;
1132
1133       end loop;
1134
1135       Error_Report (Error_Buffer (1 .. Error_Last));
1136    end Error_Msg;
1137
1138    ---------------------
1139    -- Get_Name_String --
1140    ---------------------
1141
1142    function Get_Name_String (S : String_Id) return String is
1143    begin
1144       if S = No_String then
1145          return "";
1146       else
1147          String_To_Name_Buffer (S);
1148          return Name_Buffer (1 .. Name_Len);
1149       end if;
1150    end Get_Name_String;
1151
1152    --------------
1153    -- Get_Unit --
1154    --------------
1155
1156    procedure Get_Unit
1157      (File_Name    : Name_Id;
1158       Naming       : Naming_Data;
1159       Unit_Name    : out Name_Id;
1160       Unit_Kind    : out Spec_Or_Body;
1161       Needs_Pragma : out Boolean)
1162    is
1163       Canonical_Case_Name : Name_Id;
1164
1165    begin
1166       Needs_Pragma := False;
1167       Get_Name_String (File_Name);
1168       Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1169       Canonical_Case_Name := Name_Find;
1170
1171       if Naming.Bodies /= No_Array_Element then
1172
1173          --  There are some specified file names for some bodies
1174          --  of this project. Find out if File_Name is one of these bodies.
1175
1176          declare
1177             Current : Array_Element_Id := Naming.Bodies;
1178             Element : Array_Element;
1179
1180          begin
1181             while Current /= No_Array_Element loop
1182                Element := Array_Elements.Table (Current);
1183
1184                if Element.Index /= No_Name then
1185                   String_To_Name_Buffer (Element.Value.Value);
1186                   Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1187
1188                   if Canonical_Case_Name = Name_Find then
1189
1190                      --  File_Name corresponds to one body.
1191                      --  So, we know it is a body, and we know the unit name.
1192
1193                      Unit_Kind := Body_Part;
1194                      Unit_Name := Element.Index;
1195                      Needs_Pragma := True;
1196                      return;
1197                   end if;
1198                end if;
1199
1200                Current := Element.Next;
1201             end loop;
1202          end;
1203       end if;
1204
1205       if Naming.Specifications /= No_Array_Element then
1206
1207          --  There are some specified file names for some bodiesspecifications
1208          --  of this project. Find out if File_Name is one of these
1209          --  specifications.
1210
1211          declare
1212             Current : Array_Element_Id := Naming.Specifications;
1213             Element : Array_Element;
1214
1215          begin
1216             while Current /= No_Array_Element loop
1217                Element := Array_Elements.Table (Current);
1218
1219                if Element.Index /= No_Name then
1220                   String_To_Name_Buffer (Element.Value.Value);
1221                   Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1222
1223                   if Canonical_Case_Name = Name_Find then
1224
1225                      --  File_Name corresponds to one specification.
1226                      --  So, we know it is a spec, and we know the unit name.
1227
1228                      Unit_Kind := Specification;
1229                      Unit_Name := Element.Index;
1230                      Needs_Pragma := True;
1231                      return;
1232                   end if;
1233
1234                end if;
1235
1236                Current := Element.Next;
1237             end loop;
1238          end;
1239       end if;
1240
1241       declare
1242          File  : String   := Get_Name_String (Canonical_Case_Name);
1243          First : Positive := File'First;
1244          Last  : Natural  := File'Last;
1245
1246       begin
1247          --  Check if the end of the file name is Specification_Append
1248
1249          Get_Name_String (Naming.Current_Spec_Suffix);
1250
1251          if File'Length > Name_Len
1252            and then File (Last - Name_Len + 1 .. Last) =
1253                                                 Name_Buffer (1 .. Name_Len)
1254          then
1255             --  We have a spec
1256
1257             Unit_Kind := Specification;
1258             Last := Last - Name_Len;
1259
1260             if Current_Verbosity = High then
1261                Write_Str  ("   Specification: ");
1262                Write_Line (File (First .. Last));
1263             end if;
1264
1265          else
1266             Get_Name_String (Naming.Current_Impl_Suffix);
1267
1268             --  Check if the end of the file name is Body_Append
1269
1270             if File'Length > Name_Len
1271               and then File (Last - Name_Len + 1 .. Last) =
1272                                                 Name_Buffer (1 .. Name_Len)
1273             then
1274                --  We have a body
1275
1276                Unit_Kind := Body_Part;
1277                Last := Last - Name_Len;
1278
1279                if Current_Verbosity = High then
1280                   Write_Str  ("   Body: ");
1281                   Write_Line (File (First .. Last));
1282                end if;
1283
1284             elsif Naming.Separate_Suffix /= Naming.Current_Spec_Suffix then
1285                Get_Name_String (Naming.Separate_Suffix);
1286
1287                --  Check if the end of the file name is Separate_Append
1288
1289                if File'Length > Name_Len
1290                  and then File (Last - Name_Len + 1 .. Last) =
1291                                                 Name_Buffer (1 .. Name_Len)
1292                then
1293                   --  We have a separate (a body)
1294
1295                   Unit_Kind := Body_Part;
1296                   Last := Last - Name_Len;
1297
1298                   if Current_Verbosity = High then
1299                      Write_Str  ("   Separate: ");
1300                      Write_Line (File (First .. Last));
1301                   end if;
1302
1303                else
1304                   Last := 0;
1305                end if;
1306
1307             else
1308                Last := 0;
1309             end if;
1310          end if;
1311
1312          if Last = 0 then
1313
1314             --  This is not a source file
1315
1316             Unit_Name := No_Name;
1317             Unit_Kind := Specification;
1318
1319             if Current_Verbosity = High then
1320                Write_Line ("   Not a valid file name.");
1321             end if;
1322
1323             return;
1324          end if;
1325
1326          Get_Name_String (Naming.Dot_Replacement);
1327
1328          if Name_Buffer (1 .. Name_Len) /= "." then
1329
1330             --  If Dot_Replacement is not a single dot,
1331             --  then there should not be any dot in the name.
1332
1333             for Index in First .. Last loop
1334                if File (Index) = '.' then
1335                   if Current_Verbosity = High then
1336                      Write_Line
1337                        ("   Not a valid file name (some dot not replaced).");
1338                   end if;
1339
1340                   Unit_Name := No_Name;
1341                   return;
1342
1343                end if;
1344             end loop;
1345
1346             --  Replace the substring Dot_Replacement with dots
1347
1348             declare
1349                Index : Positive := First;
1350
1351             begin
1352                while Index <= Last - Name_Len + 1 loop
1353
1354                   if File (Index .. Index + Name_Len - 1) =
1355                     Name_Buffer (1 .. Name_Len)
1356                   then
1357                      File (Index) := '.';
1358
1359                      if Name_Len > 1 and then Index < Last then
1360                         File (Index + 1 .. Last - Name_Len + 1) :=
1361                           File (Index + Name_Len .. Last);
1362                      end if;
1363
1364                      Last := Last - Name_Len + 1;
1365                   end if;
1366
1367                   Index := Index + 1;
1368                end loop;
1369             end;
1370          end if;
1371
1372          --  Check if the casing is right
1373
1374          declare
1375             Src : String := File (First .. Last);
1376
1377          begin
1378             case Naming.Casing is
1379                when All_Lower_Case =>
1380                   Fixed.Translate
1381                     (Source  => Src,
1382                      Mapping => Lower_Case_Map);
1383
1384                when All_Upper_Case =>
1385                   Fixed.Translate
1386                     (Source  => Src,
1387                      Mapping => Upper_Case_Map);
1388
1389                when Mixed_Case | Unknown =>
1390                   null;
1391             end case;
1392
1393             if Src /= File (First .. Last) then
1394                if Current_Verbosity = High then
1395                   Write_Line ("   Not a valid file name (casing).");
1396                end if;
1397
1398                Unit_Name := No_Name;
1399                return;
1400             end if;
1401
1402             --  We put the name in lower case
1403
1404             Fixed.Translate
1405               (Source  => Src,
1406                Mapping => Lower_Case_Map);
1407
1408             if Current_Verbosity = High then
1409                Write_Str  ("      ");
1410                Write_Line (Src);
1411             end if;
1412
1413             Name_Len := Src'Length;
1414             Name_Buffer (1 .. Name_Len) := Src;
1415
1416             --  Now, we check if this name is a valid unit name
1417
1418             Check_Ada_Name (Name => Name_Find, Unit => Unit_Name);
1419          end;
1420
1421       end;
1422
1423    end Get_Unit;
1424
1425    -----------------------
1426    -- Is_Illegal_Append --
1427    -----------------------
1428
1429    function Is_Illegal_Append (This : String) return Boolean is
1430    begin
1431       return This'Length = 0
1432         or else Is_Alphanumeric (This (This'First))
1433         or else (This'Length >= 2
1434                  and then This (This'First) = '_'
1435                  and then Is_Alphanumeric (This (This'First + 1)));
1436    end Is_Illegal_Append;
1437
1438    --------------------------------
1439    -- Language_Independent_Check --
1440    --------------------------------
1441
1442    procedure Language_Independent_Check
1443      (Project      : Project_Id;
1444       Report_Error : Put_Line_Access)
1445    is
1446       Last_Source_Dir   : String_List_Id  := Nil_String;
1447       Data              : Project_Data    := Projects.Table (Project);
1448
1449       procedure Find_Source_Dirs (From : String_Id; Location : Source_Ptr);
1450       --  Find one or several source directories, and add them
1451       --  to the list of source directories of the project.
1452
1453       ----------------------
1454       -- Find_Source_Dirs --
1455       ----------------------
1456
1457       procedure Find_Source_Dirs (From : String_Id; Location : Source_Ptr) is
1458
1459          Directory    : String (1 .. Integer (String_Length (From)));
1460          Directory_Id : Name_Id;
1461          Element      : String_Element;
1462
1463          procedure Recursive_Find_Dirs (Path : String_Id);
1464          --  Find all the subdirectories (recursively) of Path
1465          --  and add them to the list of source directories
1466          --  of the project.
1467
1468          -------------------------
1469          -- Recursive_Find_Dirs --
1470          -------------------------
1471
1472          procedure Recursive_Find_Dirs (Path : String_Id) is
1473             Dir      : Dir_Type;
1474             Name     : String (1 .. 250);
1475             Last     : Natural;
1476             The_Path : String := Get_Name_String (Path) & Dir_Sep;
1477
1478             The_Path_Last : Positive := The_Path'Last;
1479
1480          begin
1481             if The_Path'Length > 1
1482               and then
1483                 (The_Path (The_Path_Last - 1) = Dir_Sep
1484                    or else The_Path (The_Path_Last - 1) = '/')
1485             then
1486                The_Path_Last := The_Path_Last - 1;
1487             end if;
1488
1489             if Current_Verbosity = High then
1490                Write_Str  ("   ");
1491                Write_Line (The_Path (The_Path'First .. The_Path_Last));
1492             end if;
1493
1494             String_Elements.Increment_Last;
1495             Element :=
1496               (Value    => Path,
1497                Location => No_Location,
1498                Next     => Nil_String);
1499
1500             --  Case of first source directory
1501
1502             if Last_Source_Dir = Nil_String then
1503                Data.Source_Dirs := String_Elements.Last;
1504
1505             --  Here we already have source directories.
1506
1507             else
1508                --  Link the previous last to the new one
1509
1510                String_Elements.Table (Last_Source_Dir).Next :=
1511                  String_Elements.Last;
1512             end if;
1513
1514             --  And register this source directory as the new last
1515
1516             Last_Source_Dir  := String_Elements.Last;
1517             String_Elements.Table (Last_Source_Dir) := Element;
1518
1519             --  Now look for subdirectories
1520
1521             Open (Dir, The_Path (The_Path'First .. The_Path_Last));
1522
1523             loop
1524                Read (Dir, Name, Last);
1525                exit when Last = 0;
1526
1527                if Current_Verbosity = High then
1528                   Write_Str  ("   Checking ");
1529                   Write_Line (Name (1 .. Last));
1530                end if;
1531
1532                if Name (1 .. Last) /= "."
1533                  and then Name (1 .. Last) /= ".."
1534                then
1535                   --  Avoid . and ..
1536
1537                   declare
1538                      Path_Name : constant String :=
1539                                    The_Path (The_Path'First .. The_Path_Last) &
1540                                    Name (1 .. Last);
1541
1542                   begin
1543                      if Is_Directory (Path_Name) then
1544
1545                         --  We have found a new subdirectory,
1546                         --  register it and find its own subdirectories.
1547
1548                         Start_String;
1549                         Store_String_Chars (Path_Name);
1550                         Recursive_Find_Dirs (End_String);
1551                      end if;
1552                   end;
1553                end if;
1554             end loop;
1555
1556             Close (Dir);
1557
1558          exception
1559             when Directory_Error =>
1560                null;
1561          end Recursive_Find_Dirs;
1562
1563          --  Start of processing for Find_Source_Dirs
1564
1565       begin
1566          if Current_Verbosity = High then
1567             Write_Str ("Find_Source_Dirs (""");
1568          end if;
1569
1570          String_To_Name_Buffer (From);
1571          Directory    := Name_Buffer (1 .. Name_Len);
1572          Directory_Id := Name_Find;
1573
1574          if Current_Verbosity = High then
1575             Write_Str (Directory);
1576             Write_Line (""")");
1577          end if;
1578
1579          --  First, check if we are looking for a directory tree,
1580          --  indicated by "/**" at the end.
1581
1582          if Directory'Length >= 3
1583            and then Directory (Directory'Last - 1 .. Directory'Last) = "**"
1584            and then (Directory (Directory'Last - 2) = '/'
1585                        or else
1586                      Directory (Directory'Last - 2) = Dir_Sep)
1587          then
1588             Name_Len := Directory'Length - 3;
1589
1590             if Name_Len = 0 then
1591                --  This is the case of "/**": all directories
1592                --  in the file system.
1593
1594                Name_Len := 1;
1595                Name_Buffer (1) := Directory (Directory'First);
1596
1597             else
1598                Name_Buffer (1 .. Name_Len) :=
1599                  Directory (Directory'First .. Directory'Last - 3);
1600             end if;
1601
1602             if Current_Verbosity = High then
1603                Write_Str ("Looking for all subdirectories of """);
1604                Write_Str (Name_Buffer (1 .. Name_Len));
1605                Write_Line ("""");
1606             end if;
1607
1608             declare
1609                Base_Dir : constant Name_Id := Name_Find;
1610                Root     : constant Name_Id :=
1611                             Locate_Directory (Base_Dir, Data.Directory);
1612
1613             begin
1614                if Root = No_Name then
1615                   Error_Msg_Name_1 := Base_Dir;
1616                   if Location = No_Location then
1617                      Error_Msg ("{ is not a valid directory.", Data.Location);
1618                   else
1619                      Error_Msg ("{ is not a valid directory.", Location);
1620                   end if;
1621
1622                else
1623                   --  We have an existing directory,
1624                   --  we register it and all of its subdirectories.
1625
1626                   if Current_Verbosity = High then
1627                      Write_Line ("Looking for source directories:");
1628                   end if;
1629
1630                   Start_String;
1631                   Store_String_Chars (Get_Name_String (Root));
1632                   Recursive_Find_Dirs (End_String);
1633
1634                   if Current_Verbosity = High then
1635                      Write_Line ("End of looking for source directories.");
1636                   end if;
1637                end if;
1638             end;
1639
1640          --  We have a single directory
1641
1642          else
1643             declare
1644                Path_Name : constant Name_Id :=
1645                  Locate_Directory (Directory_Id, Data.Directory);
1646
1647             begin
1648                if Path_Name = No_Name then
1649                   Error_Msg_Name_1 := Directory_Id;
1650                   if Location = No_Location then
1651                      Error_Msg ("{ is not a valid directory", Data.Location);
1652                   else
1653                      Error_Msg ("{ is not a valid directory", Location);
1654                   end if;
1655                else
1656
1657                   --  As it is an existing directory, we add it to
1658                   --  the list of directories.
1659
1660                   String_Elements.Increment_Last;
1661                   Start_String;
1662                   Store_String_Chars (Get_Name_String (Path_Name));
1663                   Element.Value := End_String;
1664
1665                   if Last_Source_Dir = Nil_String then
1666
1667                      --  This is the first source directory
1668
1669                      Data.Source_Dirs := String_Elements.Last;
1670
1671                   else
1672                      --  We already have source directories,
1673                      --  link the previous last to the new one.
1674
1675                      String_Elements.Table (Last_Source_Dir).Next :=
1676                        String_Elements.Last;
1677                   end if;
1678
1679                   --  And register this source directory as the new last
1680
1681                   Last_Source_Dir := String_Elements.Last;
1682                   String_Elements.Table (Last_Source_Dir) := Element;
1683                end if;
1684             end;
1685          end if;
1686       end Find_Source_Dirs;
1687
1688       --  Start of processing for Language_Independent_Check
1689
1690    begin
1691
1692       if Data.Language_Independent_Checked then
1693          return;
1694       end if;
1695
1696       Data.Language_Independent_Checked := True;
1697
1698       Error_Report := Report_Error;
1699
1700       if Current_Verbosity = High then
1701          Write_Line ("Starting to look for directories");
1702       end if;
1703
1704       --  Let's check the object directory
1705
1706       declare
1707          Object_Dir : Variable_Value :=
1708                         Util.Value_Of (Name_Object_Dir, Data.Decl.Attributes);
1709
1710       begin
1711          pragma Assert (Object_Dir.Kind = Single,
1712                         "Object_Dir is not a single string");
1713
1714          --  We set the object directory to its default
1715
1716          Data.Object_Directory := Data.Directory;
1717
1718          if not String_Equal (Object_Dir.Value, Empty_String) then
1719
1720             String_To_Name_Buffer (Object_Dir.Value);
1721
1722             if Name_Len = 0 then
1723                Error_Msg ("Object_Dir cannot be empty",
1724                           Object_Dir.Location);
1725
1726             else
1727                --  We check that the specified object directory
1728                --  does exist.
1729
1730                Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1731
1732                declare
1733                   Dir_Id : constant Name_Id := Name_Find;
1734
1735                begin
1736                   Data.Object_Directory :=
1737                     Locate_Directory (Dir_Id, Data.Directory);
1738
1739                   if Data.Object_Directory = No_Name then
1740                      Error_Msg_Name_1 := Dir_Id;
1741                      Error_Msg
1742                        ("the object directory { cannot be found",
1743                         Data.Location);
1744                   end if;
1745                end;
1746             end if;
1747          end if;
1748       end;
1749
1750       if Current_Verbosity = High then
1751          if Data.Object_Directory = No_Name then
1752             Write_Line ("No object directory");
1753          else
1754             Write_Str ("Object directory: """);
1755             Write_Str (Get_Name_String (Data.Object_Directory));
1756             Write_Line ("""");
1757          end if;
1758       end if;
1759
1760       --  Look for the source directories
1761
1762       declare
1763          Source_Dirs : Variable_Value :=
1764            Util.Value_Of (Name_Source_Dirs, Data.Decl.Attributes);
1765
1766       begin
1767
1768          if Current_Verbosity = High then
1769             Write_Line ("Starting to look for source directories");
1770          end if;
1771
1772          pragma Assert (Source_Dirs.Kind = List,
1773                           "Source_Dirs is not a list");
1774
1775          if Source_Dirs.Default then
1776
1777             --  No Source_Dirs specified: the single source directory
1778             --  is the one containing the project file
1779
1780             String_Elements.Increment_Last;
1781             Data.Source_Dirs := String_Elements.Last;
1782             Start_String;
1783             Store_String_Chars (Get_Name_String (Data.Directory));
1784             String_Elements.Table (Data.Source_Dirs) :=
1785               (Value    => End_String,
1786                Location => No_Location,
1787                Next     => Nil_String);
1788
1789             if Current_Verbosity = High then
1790                Write_Line ("(Undefined) Single object directory:");
1791                Write_Str ("    """);
1792                Write_Str (Get_Name_String (Data.Directory));
1793                Write_Line ("""");
1794             end if;
1795
1796          elsif Source_Dirs.Values = Nil_String then
1797
1798             --  If Source_Dirs is an empty string list, this means
1799             --  that this project contains no source.
1800
1801             if Data.Object_Directory = Data.Directory then
1802                Data.Object_Directory := No_Name;
1803             end if;
1804
1805             Data.Source_Dirs     := Nil_String;
1806             Data.Sources_Present := False;
1807
1808          else
1809             declare
1810                Source_Dir : String_List_Id := Source_Dirs.Values;
1811                Element    : String_Element;
1812
1813             begin
1814                --  We will find the source directories for each
1815                --  element of the list
1816
1817                while Source_Dir /= Nil_String loop
1818                   Element := String_Elements.Table (Source_Dir);
1819                   Find_Source_Dirs (Element.Value, Element.Location);
1820                   Source_Dir := Element.Next;
1821                end loop;
1822             end;
1823          end if;
1824
1825          if Current_Verbosity = High then
1826             Write_Line ("Puting source directories in canonical cases");
1827          end if;
1828
1829          declare
1830             Current : String_List_Id := Data.Source_Dirs;
1831             Element : String_Element;
1832
1833          begin
1834             while Current /= Nil_String loop
1835                Element := String_Elements.Table (Current);
1836                if Element.Value /= No_String then
1837                   String_To_Name_Buffer (Element.Value);
1838                   Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
1839                   Start_String;
1840                   Store_String_Chars (Name_Buffer (1 .. Name_Len));
1841                   Element.Value := End_String;
1842                   String_Elements.Table (Current) := Element;
1843                end if;
1844
1845                Current := Element.Next;
1846             end loop;
1847          end;
1848       end;
1849
1850       --  Library Dir, Name, Version and Kind
1851
1852       declare
1853          Attributes : constant Prj.Variable_Id := Data.Decl.Attributes;
1854
1855          Lib_Dir : Prj.Variable_Value :=
1856                      Prj.Util.Value_Of (Snames.Name_Library_Dir, Attributes);
1857
1858          Lib_Name : Prj.Variable_Value :=
1859                       Prj.Util.Value_Of (Snames.Name_Library_Name, Attributes);
1860
1861          Lib_Version : Prj.Variable_Value :=
1862                          Prj.Util.Value_Of
1863                            (Snames.Name_Library_Version, Attributes);
1864
1865          The_Lib_Kind : Prj.Variable_Value :=
1866                           Prj.Util.Value_Of
1867                             (Snames.Name_Library_Kind, Attributes);
1868
1869       begin
1870          pragma Assert (Lib_Dir.Kind = Single);
1871
1872          if Lib_Dir.Value = Empty_String then
1873
1874             if Current_Verbosity = High then
1875                Write_Line ("No library directory");
1876             end if;
1877
1878          else
1879             --  Find path name, check that it is a directory
1880
1881             Stringt.String_To_Name_Buffer (Lib_Dir.Value);
1882
1883             declare
1884                Dir_Id : constant Name_Id := Name_Find;
1885
1886             begin
1887                Data.Library_Dir :=
1888                  Locate_Directory (Dir_Id, Data.Directory);
1889
1890                if Data.Library_Dir = No_Name then
1891                   Error_Msg ("not an existing directory",
1892                              Lib_Dir.Location);
1893
1894                elsif Data.Library_Dir = Data.Object_Directory then
1895                   Error_Msg
1896                     ("library directory cannot be the same " &
1897                      "as object directory",
1898                      Lib_Dir.Location);
1899                   Data.Library_Dir := No_Name;
1900
1901                else
1902                   if Current_Verbosity = High then
1903                      Write_Str ("Library directory =""");
1904                      Write_Str (Get_Name_String (Data.Library_Dir));
1905                      Write_Line ("""");
1906                   end if;
1907                end if;
1908             end;
1909          end if;
1910
1911          pragma Assert (Lib_Name.Kind = Single);
1912
1913          if Lib_Name.Value = Empty_String then
1914             if Current_Verbosity = High then
1915                Write_Line ("No library name");
1916             end if;
1917
1918          else
1919             Stringt.String_To_Name_Buffer (Lib_Name.Value);
1920
1921             if not Is_Letter (Name_Buffer (1)) then
1922                Error_Msg ("must start with a letter",
1923                           Lib_Name.Location);
1924
1925             else
1926                Data.Library_Name := Name_Find;
1927
1928                for Index in 2 .. Name_Len loop
1929                   if not Is_Alphanumeric (Name_Buffer (Index)) then
1930                      Data.Library_Name := No_Name;
1931                      Error_Msg ("only letters and digits are allowed",
1932                                 Lib_Name.Location);
1933                      exit;
1934                   end if;
1935                end loop;
1936
1937                if Data.Library_Name /= No_Name
1938                  and then Current_Verbosity = High then
1939                   Write_Str ("Library name = """);
1940                   Write_Str (Get_Name_String (Data.Library_Name));
1941                   Write_Line ("""");
1942                end if;
1943             end if;
1944          end if;
1945
1946          Data.Library :=
1947            Data.Library_Dir /= No_Name
1948              and then
1949            Data.Library_Name /= No_Name;
1950
1951          if Data.Library then
1952             if Current_Verbosity = High then
1953                Write_Line ("This is a library project file");
1954             end if;
1955
1956             pragma Assert (Lib_Version.Kind = Single);
1957
1958             if Lib_Version.Value = Empty_String then
1959                if Current_Verbosity = High then
1960                   Write_Line ("No library version specified");
1961                end if;
1962
1963             else
1964                Stringt.String_To_Name_Buffer (Lib_Version.Value);
1965                Data.Lib_Internal_Name := Name_Find;
1966             end if;
1967
1968             pragma Assert (The_Lib_Kind.Kind = Single);
1969
1970             if The_Lib_Kind.Value = Empty_String then
1971                if Current_Verbosity = High then
1972                   Write_Line ("No library kind specified");
1973                end if;
1974
1975             else
1976                Stringt.String_To_Name_Buffer (The_Lib_Kind.Value);
1977
1978                declare
1979                   Kind_Name : constant String :=
1980                     To_Lower (Name_Buffer (1 .. Name_Len));
1981
1982                   OK : Boolean := True;
1983
1984                begin
1985
1986                   if Kind_Name = "static" then
1987                      Data.Library_Kind := Static;
1988
1989                   elsif Kind_Name = "dynamic" then
1990                      Data.Library_Kind := Dynamic;
1991
1992                   elsif Kind_Name = "relocatable" then
1993                      Data.Library_Kind := Relocatable;
1994
1995                   else
1996                      Error_Msg
1997                        ("illegal value for Library_Kind",
1998                         The_Lib_Kind.Location);
1999                      OK := False;
2000                   end if;
2001
2002                   if Current_Verbosity = High and then OK then
2003                      Write_Str ("Library kind = ");
2004                      Write_Line (Kind_Name);
2005                   end if;
2006                end;
2007             end if;
2008          end if;
2009       end;
2010
2011       if Current_Verbosity = High then
2012          Show_Source_Dirs (Project);
2013       end if;
2014
2015       declare
2016          Naming_Id : constant Package_Id :=
2017                        Util.Value_Of (Name_Naming, Data.Decl.Packages);
2018
2019          Naming    : Package_Element;
2020
2021       begin
2022          --  If there is a package Naming, we will put in Data.Naming
2023          --  what is in this package Naming.
2024
2025          if Naming_Id /= No_Package then
2026             Naming := Packages.Table (Naming_Id);
2027
2028             if Current_Verbosity = High then
2029                Write_Line ("Checking ""Naming"".");
2030             end if;
2031
2032             --  Check Specification_Suffix
2033
2034             Data.Naming.Specification_Suffix := Util.Value_Of
2035                                                  (Name_Specification_Suffix,
2036                                                   Naming.Decl.Arrays);
2037
2038             declare
2039                Current : Array_Element_Id := Data.Naming.Specification_Suffix;
2040                Element : Array_Element;
2041
2042             begin
2043                while Current /= No_Array_Element loop
2044                   Element := Array_Elements.Table (Current);
2045                   String_To_Name_Buffer (Element.Value.Value);
2046
2047                   if Name_Len = 0 then
2048                      Error_Msg
2049                        ("Specification_Suffix cannot be empty",
2050                         Element.Value.Location);
2051                   end if;
2052
2053                   Array_Elements.Table (Current) := Element;
2054                   Current := Element.Next;
2055                end loop;
2056             end;
2057
2058             --  Check Implementation_Suffix
2059
2060             Data.Naming.Implementation_Suffix := Util.Value_Of
2061                                           (Name_Implementation_Suffix,
2062                                            Naming.Decl.Arrays);
2063
2064             declare
2065                Current : Array_Element_Id := Data.Naming.Implementation_Suffix;
2066                Element : Array_Element;
2067
2068             begin
2069                while Current /= No_Array_Element loop
2070                   Element := Array_Elements.Table (Current);
2071                   String_To_Name_Buffer (Element.Value.Value);
2072
2073                   if Name_Len = 0 then
2074                      Error_Msg
2075                        ("Implementation_Suffix cannot be empty",
2076                         Element.Value.Location);
2077                   end if;
2078
2079                   Array_Elements.Table (Current) := Element;
2080                   Current := Element.Next;
2081                end loop;
2082             end;
2083
2084          end if;
2085       end;
2086
2087       Projects.Table (Project) := Data;
2088    end Language_Independent_Check;
2089
2090    ----------------------
2091    -- Locate_Directory --
2092    ----------------------
2093
2094    function Locate_Directory
2095      (Name   : Name_Id;
2096       Parent : Name_Id)
2097       return   Name_Id
2098    is
2099       The_Name   : constant String := Get_Name_String (Name);
2100       The_Parent : constant String :=
2101                      Get_Name_String (Parent) & Dir_Sep;
2102
2103       The_Parent_Last : Positive := The_Parent'Last;
2104
2105    begin
2106       if The_Parent'Length > 1
2107         and then (The_Parent (The_Parent_Last - 1) = Dir_Sep
2108                     or else The_Parent (The_Parent_Last - 1) = '/')
2109       then
2110          The_Parent_Last := The_Parent_Last - 1;
2111       end if;
2112
2113       if Current_Verbosity = High then
2114          Write_Str ("Locate_Directory (""");
2115          Write_Str (The_Name);
2116          Write_Str (""", """);
2117          Write_Str (The_Parent);
2118          Write_Line (""")");
2119       end if;
2120
2121       if Is_Absolute_Path (The_Name) then
2122          if Is_Directory (The_Name) then
2123             return Name;
2124          end if;
2125
2126       else
2127          declare
2128             Full_Path : constant String :=
2129                           The_Parent (The_Parent'First .. The_Parent_Last) &
2130                                                                      The_Name;
2131
2132          begin
2133             if Is_Directory (Full_Path) then
2134                Name_Len := Full_Path'Length;
2135                Name_Buffer (1 .. Name_Len) := Full_Path;
2136                return Name_Find;
2137             end if;
2138          end;
2139
2140       end if;
2141
2142       return No_Name;
2143    end Locate_Directory;
2144
2145    ------------------
2146    -- Path_Name_Of --
2147    ------------------
2148
2149    function Path_Name_Of
2150      (File_Name : String_Id;
2151       Directory : String_Id)
2152       return      String
2153    is
2154       Result : String_Access;
2155
2156    begin
2157       String_To_Name_Buffer (File_Name);
2158
2159       declare
2160          The_File_Name : constant String := Name_Buffer (1 .. Name_Len);
2161
2162       begin
2163          String_To_Name_Buffer (Directory);
2164          Result := Locate_Regular_File
2165            (File_Name => The_File_Name,
2166             Path      => Name_Buffer (1 .. Name_Len));
2167       end;
2168
2169       if Result = null then
2170          return "";
2171       else
2172          Canonical_Case_File_Name (Result.all);
2173          return Result.all;
2174       end if;
2175    end Path_Name_Of;
2176
2177    function Path_Name_Of
2178      (File_Name : String_Id;
2179       Directory : Name_Id)
2180       return      String
2181    is
2182       Result : String_Access;
2183       The_Directory : constant String := Get_Name_String (Directory);
2184
2185    begin
2186       String_To_Name_Buffer (File_Name);
2187       Result := Locate_Regular_File
2188         (File_Name => Name_Buffer (1 .. Name_Len),
2189          Path      => The_Directory);
2190
2191       if Result = null then
2192          return "";
2193       else
2194          Canonical_Case_File_Name (Result.all);
2195          return Result.all;
2196       end if;
2197    end Path_Name_Of;
2198
2199    -------------------
2200    -- Record_Source --
2201    -------------------
2202
2203    procedure Record_Source
2204      (File_Name        : Name_Id;
2205       Path_Name        : Name_Id;
2206       Project          : Project_Id;
2207       Data             : in out Project_Data;
2208       Location         : Source_Ptr;
2209       Current_Source   : in out String_List_Id)
2210    is
2211       Unit_Name    : Name_Id;
2212       Unit_Kind    : Spec_Or_Body;
2213       Needs_Pragma : Boolean;
2214       The_Location : Source_Ptr := Location;
2215
2216    begin
2217       --  Find out the unit name, the unit kind and if it needs
2218       --  a specific SFN pragma.
2219
2220       Get_Unit
2221         (File_Name    => File_Name,
2222          Naming       => Data.Naming,
2223          Unit_Name    => Unit_Name,
2224          Unit_Kind    => Unit_Kind,
2225          Needs_Pragma => Needs_Pragma);
2226
2227       --  If it is not a source file, report an error only if
2228       --  Error_If_Invalid is true.
2229
2230       if Unit_Name = No_Name then
2231          if Current_Verbosity = High then
2232             Write_Str  ("   """);
2233             Write_Str  (Get_Name_String (File_Name));
2234             Write_Line (""" is not a valid source file name (ignored).");
2235          end if;
2236
2237       else
2238          --  Put the file name in the list of sources of the project
2239
2240          String_Elements.Increment_Last;
2241          Get_Name_String (File_Name);
2242          Start_String;
2243          Store_String_Chars (Name_Buffer (1 .. Name_Len));
2244          String_Elements.Table (String_Elements.Last) :=
2245            (Value    => End_String,
2246             Location => No_Location,
2247             Next     => Nil_String);
2248
2249          if Current_Source = Nil_String then
2250             Data.Sources := String_Elements.Last;
2251
2252          else
2253             String_Elements.Table (Current_Source).Next :=
2254               String_Elements.Last;
2255          end if;
2256
2257          Current_Source := String_Elements.Last;
2258
2259          --  Put the unit in unit list
2260
2261          declare
2262             The_Unit      : Unit_Id := Units_Htable.Get (Unit_Name);
2263             The_Unit_Data : Unit_Data;
2264
2265          begin
2266             if Current_Verbosity = High then
2267                Write_Str  ("Putting ");
2268                Write_Str  (Get_Name_String (Unit_Name));
2269                Write_Line (" in the unit list.");
2270             end if;
2271
2272             --  The unit is already in the list, but may be it is
2273             --  only the other unit kind (spec or body), or what is
2274             --  in the unit list is a unit of a project we are modifying.
2275
2276             if The_Unit /= Prj.Com.No_Unit then
2277                The_Unit_Data := Units.Table (The_Unit);
2278
2279                if The_Unit_Data.File_Names (Unit_Kind).Name = No_Name
2280                  or else (Data.Modifies /= No_Project
2281                             and then
2282                           The_Unit_Data.File_Names (Unit_Kind).Project =
2283                                                             Data.Modifies)
2284                then
2285                   The_Unit_Data.File_Names (Unit_Kind) :=
2286                     (Name         => File_Name,
2287                      Path         => Path_Name,
2288                      Project      => Project,
2289                      Needs_Pragma => Needs_Pragma);
2290                   Units.Table (The_Unit) := The_Unit_Data;
2291
2292                else
2293                   --  It is an error to have two units with the same name
2294                   --  and the same kind (spec or body).
2295
2296                   if The_Location = No_Location then
2297                      The_Location := Projects.Table (Project).Location;
2298                   end if;
2299
2300                   Error_Msg_Name_1 := Unit_Name;
2301                   Error_Msg ("duplicate source {", The_Location);
2302
2303                   Error_Msg_Name_1 :=
2304                     Projects.Table
2305                       (The_Unit_Data.File_Names (Unit_Kind).Project).Name;
2306                   Error_Msg_Name_2 :=
2307                     The_Unit_Data.File_Names (Unit_Kind).Path;
2308                   Error_Msg ("\   project file {, {", The_Location);
2309
2310                   Error_Msg_Name_1 := Projects.Table (Project).Name;
2311                   Error_Msg_Name_2 := Path_Name;
2312                   Error_Msg ("\   project file {, {", The_Location);
2313
2314                end if;
2315
2316             --  It is a new unit, create a new record
2317
2318             else
2319                Units.Increment_Last;
2320                The_Unit := Units.Last;
2321                Units_Htable.Set (Unit_Name, The_Unit);
2322                The_Unit_Data.Name := Unit_Name;
2323                The_Unit_Data.File_Names (Unit_Kind) :=
2324                  (Name         => File_Name,
2325                   Path         => Path_Name,
2326                   Project      => Project,
2327                   Needs_Pragma => Needs_Pragma);
2328                Units.Table (The_Unit) := The_Unit_Data;
2329             end if;
2330          end;
2331       end if;
2332    end Record_Source;
2333
2334    ----------------------
2335    -- Show_Source_Dirs --
2336    ----------------------
2337
2338    procedure Show_Source_Dirs (Project : Project_Id) is
2339       Current : String_List_Id := Projects.Table (Project).Source_Dirs;
2340       Element : String_Element;
2341
2342    begin
2343       Write_Line ("Source_Dirs:");
2344
2345       while Current /= Nil_String loop
2346          Element := String_Elements.Table (Current);
2347          Write_Str  ("   ");
2348          Write_Line (Get_Name_String (Element.Value));
2349          Current := Element.Next;
2350       end loop;
2351
2352       Write_Line ("end Source_Dirs.");
2353    end Show_Source_Dirs;
2354
2355 end Prj.Nmsc;