1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2007, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
26 with Ada.Unchecked_Deallocation;
28 with GNAT.Case_Util; use GNAT.Case_Util;
30 with Osint; use Osint;
31 with Output; use Output;
33 with Snames; use Snames;
34 with Targparm; use Targparm;
36 package body Prj.Util is
38 procedure Free is new Ada.Unchecked_Deallocation
39 (Text_File_Data, Text_File);
45 procedure Close (File : in out Text_File) is
48 Prj.Com.Fail ("Close attempted on an invalid Text_File");
51 -- Close file, no need to test status, since this is a file that we
52 -- read, and the file was read successfully before we closed it.
63 (This : in out Name_List_Index;
64 In_Tree : Project_Tree_Ref)
66 Old_Current : Name_List_Index;
67 New_Current : Name_List_Index;
70 if This /= No_Name_List then
72 Name_List_Table.Increment_Last (In_Tree.Name_Lists);
73 New_Current := Name_List_Table.Last (In_Tree.Name_Lists);
75 In_Tree.Name_Lists.Table (New_Current) :=
76 (In_Tree.Name_Lists.Table (Old_Current).Name, No_Name_List);
79 Old_Current := In_Tree.Name_Lists.Table (Old_Current).Next;
80 exit when Old_Current = No_Name_List;
81 In_Tree.Name_Lists.Table (New_Current).Next := New_Current + 1;
82 Name_List_Table.Increment_Last (In_Tree.Name_Lists);
83 New_Current := New_Current + 1;
84 In_Tree.Name_Lists.Table (New_Current) :=
85 (In_Tree.Name_Lists.Table (Old_Current).Name, No_Name_List);
94 function End_Of_File (File : Text_File) return Boolean is
97 Prj.Com.Fail ("End_Of_File attempted on an invalid Text_File");
100 return File.End_Of_File_Reached;
107 function Executable_Of
108 (Project : Project_Id;
109 In_Tree : Project_Tree_Ref;
110 Main : File_Name_Type;
112 Ada_Main : Boolean := True) return File_Name_Type
114 pragma Assert (Project /= No_Project);
116 The_Packages : constant Package_Id :=
117 In_Tree.Projects.Table (Project).Decl.Packages;
119 Builder_Package : constant Prj.Package_Id :=
121 (Name => Name_Builder,
122 In_Packages => The_Packages,
125 Executable : Variable_Value :=
127 (Name => Name_Id (Main),
129 Attribute_Or_Array_Name => Name_Executable,
130 In_Package => Builder_Package,
133 Executable_Suffix : Variable_Value := Nil_Variable_Value;
135 Executable_Suffix_Name : Name_Id := No_Name;
137 Naming : constant Naming_Data := In_Tree.Projects.Table (Project).Naming;
139 Body_Suffix : constant String :=
140 Body_Suffix_Of (In_Tree, "ada", Naming);
142 Spec_Suffix : constant String :=
143 Spec_Suffix_Of (In_Tree, "ada", Naming);
146 if Builder_Package /= No_Package then
147 if Get_Mode = Multi_Language then
148 Executable_Suffix_Name := In_Tree.Config.Executable_Suffix;
151 Executable_Suffix := Prj.Util.Value_Of
152 (Variable_Name => Name_Executable_Suffix,
153 In_Variables => In_Tree.Packages.Table
154 (Builder_Package).Decl.Attributes,
157 if Executable_Suffix /= Nil_Variable_Value
158 and then not Executable_Suffix.Default
160 Executable_Suffix_Name := Executable_Suffix.Value;
164 if Executable = Nil_Variable_Value and Ada_Main then
165 Get_Name_String (Main);
167 -- Try as index the name minus the implementation suffix or minus
168 -- the specification suffix.
171 Name : constant String (1 .. Name_Len) :=
172 Name_Buffer (1 .. Name_Len);
173 Last : Positive := Name_Len;
175 Truncated : Boolean := False;
178 if Last > Body_Suffix'Length
179 and then Name (Last - Body_Suffix'Length + 1 .. Last) =
183 Last := Last - Body_Suffix'Length;
187 and then Last > Spec_Suffix'Length
188 and then Name (Last - Spec_Suffix'Length + 1 .. Last) =
192 Last := Last - Spec_Suffix'Length;
197 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
202 Attribute_Or_Array_Name => Name_Executable,
203 In_Package => Builder_Package,
209 -- If we have found an Executable attribute, return its value,
210 -- possibly suffixed by the executable suffix.
212 if Executable /= Nil_Variable_Value
213 and then Executable.Value /= Empty_Name
215 -- Get the executable name. If Executable_Suffix is defined,
216 -- make sure that it will be the extension of the executable.
219 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
220 Result : File_Name_Type;
223 if Executable_Suffix_Name /= No_Name then
224 Executable_Extension_On_Target := Executable_Suffix_Name;
227 Result := Executable_Name (File_Name_Type (Executable.Value));
228 Executable_Extension_On_Target := Saved_EEOT;
234 Get_Name_String (Main);
236 -- If there is a body suffix or a spec suffix, remove this suffix,
237 -- otherwise remove any suffix ('.' followed by other characters), if
240 if Ada_Main and then Name_Len > Body_Suffix'Length
241 and then Name_Buffer (Name_Len - Body_Suffix'Length + 1 .. Name_Len) =
244 -- Found the body termination, remove it
246 Name_Len := Name_Len - Body_Suffix'Length;
248 elsif Ada_Main and then Name_Len > Spec_Suffix'Length
249 and then Name_Buffer (Name_Len - Spec_Suffix'Length + 1 .. Name_Len) =
252 -- Found the spec termination, remove it
254 Name_Len := Name_Len - Spec_Suffix'Length;
257 -- Remove any suffix, if there is one
259 Get_Name_String (Strip_Suffix (Main));
262 if Executable_Suffix /= Nil_Variable_Value
263 and then not Executable_Suffix.Default
265 -- If attribute Executable_Suffix is specified, add this suffix
268 Suffix : constant String :=
269 Get_Name_String (Executable_Suffix.Value);
271 Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix;
272 Name_Len := Name_Len + Suffix'Length;
277 -- Get the executable name. If Executable_Suffix is defined in the
278 -- configuration, make sure that it will be the extension of the
282 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
283 Result : File_Name_Type;
286 Executable_Extension_On_Target := In_Tree.Config.Executable_Suffix;
287 Result := Executable_Name (Name_Find);
288 Executable_Extension_On_Target := Saved_EEOT;
313 if File.Cursor = File.Buffer_Len then
317 A => File.Buffer'Address,
318 N => File.Buffer'Length);
320 if File.Buffer_Len = 0 then
321 File.End_Of_File_Reached := True;
328 File.Cursor := File.Cursor + 1;
332 -- Start of processing for Get_Line
336 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
339 Last := Line'First - 1;
341 if not File.End_Of_File_Reached then
343 C := File.Buffer (File.Cursor);
344 exit when C = ASCII.CR or else C = ASCII.LF;
349 if File.End_Of_File_Reached then
353 exit when Last = Line'Last;
356 if C = ASCII.CR or else C = ASCII.LF then
359 if File.End_Of_File_Reached then
365 and then File.Buffer (File.Cursor) = ASCII.LF
376 function Is_Valid (File : Text_File) return Boolean is
385 procedure Open (File : out Text_File; Name : String) is
386 FD : File_Descriptor;
387 File_Name : String (1 .. Name'Length + 1);
390 File_Name (1 .. Name'Length) := Name;
391 File_Name (File_Name'Last) := ASCII.NUL;
392 FD := Open_Read (Name => File_Name'Address,
393 Fmode => GNAT.OS_Lib.Text);
395 if FD = Invalid_FD then
399 File := new Text_File_Data;
403 A => File.Buffer'Address,
404 N => File.Buffer'Length);
406 if File.Buffer_Len = 0 then
407 File.End_Of_File_Reached := True;
419 (Into_List : in out Name_List_Index;
420 From_List : String_List_Id;
421 In_Tree : Project_Tree_Ref)
423 Current_Name : Name_List_Index;
424 List : String_List_Id;
425 Element : String_Element;
426 Last : Name_List_Index :=
427 Name_List_Table.Last (In_Tree.Name_Lists);
430 Current_Name := Into_List;
431 while Current_Name /= No_Name_List and then
432 In_Tree.Name_Lists.Table (Current_Name).Next /= No_Name_List
434 Current_Name := In_Tree.Name_Lists.Table (Current_Name).Next;
438 while List /= Nil_String loop
439 Element := In_Tree.String_Elements.Table (List);
441 Name_List_Table.Append
443 (Name => Element.Value, Next => No_Name_List));
447 if Current_Name = No_Name_List then
451 In_Tree.Name_Lists.Table (Current_Name).Next := Last;
454 Current_Name := Last;
456 List := Element.Next;
465 (Variable : Variable_Value;
466 Default : String) return String
469 if Variable.Kind /= Single
470 or else Variable.Default
471 or else Variable.Value = No_Name
475 return Get_Name_String (Variable.Value);
481 In_Array : Array_Element_Id;
482 In_Tree : Project_Tree_Ref) return Name_Id
484 Current : Array_Element_Id;
485 Element : Array_Element;
486 Real_Index : Name_Id := Index;
491 if Current = No_Array_Element then
495 Element := In_Tree.Array_Elements.Table (Current);
497 if not Element.Index_Case_Sensitive then
498 Get_Name_String (Index);
499 To_Lower (Name_Buffer (1 .. Name_Len));
500 Real_Index := Name_Find;
503 while Current /= No_Array_Element loop
504 Element := In_Tree.Array_Elements.Table (Current);
506 if Real_Index = Element.Index then
507 exit when Element.Value.Kind /= Single;
508 exit when Element.Value.Value = Empty_String;
509 return Element.Value.Value;
511 Current := Element.Next;
520 Src_Index : Int := 0;
521 In_Array : Array_Element_Id;
522 In_Tree : Project_Tree_Ref;
523 Force_Lower_Case_Index : Boolean := False) return Variable_Value
525 Current : Array_Element_Id;
526 Element : Array_Element;
527 Real_Index : Name_Id;
532 if Current = No_Array_Element then
533 return Nil_Variable_Value;
536 Element := In_Tree.Array_Elements.Table (Current);
540 if not Element.Index_Case_Sensitive or Force_Lower_Case_Index then
541 Get_Name_String (Index);
542 To_Lower (Name_Buffer (1 .. Name_Len));
543 Real_Index := Name_Find;
546 while Current /= No_Array_Element loop
547 Element := In_Tree.Array_Elements.Table (Current);
549 if Real_Index = Element.Index and then
550 Src_Index = Element.Src_Index
552 return Element.Value;
554 Current := Element.Next;
558 return Nil_Variable_Value;
564 Attribute_Or_Array_Name : Name_Id;
565 In_Package : Package_Id;
566 In_Tree : Project_Tree_Ref;
567 Force_Lower_Case_Index : Boolean := False) return Variable_Value
569 The_Array : Array_Element_Id;
570 The_Attribute : Variable_Value := Nil_Variable_Value;
573 if In_Package /= No_Package then
575 -- First, look if there is an array element that fits
579 (Name => Attribute_Or_Array_Name,
580 In_Arrays => In_Tree.Packages.Table (In_Package).Decl.Arrays,
586 In_Array => The_Array,
588 Force_Lower_Case_Index => Force_Lower_Case_Index);
590 -- If there is no array element, look for a variable
592 if The_Attribute = Nil_Variable_Value then
595 (Variable_Name => Attribute_Or_Array_Name,
596 In_Variables => In_Tree.Packages.Table
597 (In_Package).Decl.Attributes,
602 return The_Attribute;
608 In_Arrays : Array_Id;
609 In_Tree : Project_Tree_Ref) return Name_Id
612 The_Array : Array_Data;
615 Current := In_Arrays;
616 while Current /= No_Array loop
617 The_Array := In_Tree.Arrays.Table (Current);
618 if The_Array.Name = In_Array then
620 (Index, In_Array => The_Array.Value, In_Tree => In_Tree);
622 Current := The_Array.Next;
631 In_Arrays : Array_Id;
632 In_Tree : Project_Tree_Ref) return Array_Element_Id
635 The_Array : Array_Data;
638 Current := In_Arrays;
639 while Current /= No_Array loop
640 The_Array := In_Tree.Arrays.Table (Current);
642 if The_Array.Name = Name then
643 return The_Array.Value;
645 Current := The_Array.Next;
649 return No_Array_Element;
654 In_Packages : Package_Id;
655 In_Tree : Project_Tree_Ref) return Package_Id
657 Current : Package_Id;
658 The_Package : Package_Element;
661 Current := In_Packages;
662 while Current /= No_Package loop
663 The_Package := In_Tree.Packages.Table (Current);
664 exit when The_Package.Name /= No_Name
665 and then The_Package.Name = Name;
666 Current := The_Package.Next;
673 (Variable_Name : Name_Id;
674 In_Variables : Variable_Id;
675 In_Tree : Project_Tree_Ref) return Variable_Value
677 Current : Variable_Id;
678 The_Variable : Variable;
681 Current := In_Variables;
682 while Current /= No_Variable loop
684 In_Tree.Variable_Elements.Table (Current);
686 if Variable_Name = The_Variable.Name then
687 return The_Variable.Value;
689 Current := The_Variable.Next;
693 return Nil_Variable_Value;
702 Max_Length : Positive;
703 Separator : Character)
705 First : Positive := S'First;
706 Last : Natural := S'Last;
709 -- Nothing to do for empty strings
713 -- Start on a new line if current line is already longer than
716 if Positive (Column) >= Max_Length then
720 -- If length of remainder is longer than Max_Length, we need to
721 -- cut the remainder in several lines.
723 while Positive (Column) + S'Last - First > Max_Length loop
725 -- Try the maximum length possible
727 Last := First + Max_Length - Positive (Column);
729 -- Look for last Separator in the line
731 while Last >= First and then S (Last) /= Separator loop
735 -- If we do not find a separator, we output the maximum length
739 Last := First + Max_Length - Positive (Column);
742 Write_Line (S (First .. Last));
744 -- Set the beginning of the new remainder
749 -- What is left goes to the buffer, without EOL
751 Write_Str (S (First .. S'Last));