1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2008, 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;
113 Language : String := "") return File_Name_Type
115 pragma Assert (Project /= No_Project);
117 The_Packages : constant Package_Id :=
118 In_Tree.Projects.Table (Project).Decl.Packages;
120 Builder_Package : constant Prj.Package_Id :=
122 (Name => Name_Builder,
123 In_Packages => The_Packages,
126 Executable : Variable_Value :=
128 (Name => Name_Id (Main),
130 Attribute_Or_Array_Name => Name_Executable,
131 In_Package => Builder_Package,
134 Executable_Suffix : Variable_Value := Nil_Variable_Value;
136 Executable_Suffix_Name : Name_Id := No_Name;
138 Naming : constant Naming_Data := In_Tree.Projects.Table (Project).Naming;
140 Spec_Suffix : Name_Id := No_Name;
141 Body_Suffix : Name_Id := No_Name;
143 Spec_Suffix_Length : Natural := 0;
144 Body_Suffix_Length : Natural := 0;
146 procedure Get_Suffixes
149 -- Get the non empty suffixes in variables Spec_Suffix and Body_Suffix
155 procedure Get_Suffixes
160 if B_Suffix'Length > 0 then
161 Name_Len := B_Suffix'Length;
162 Name_Buffer (1 .. Name_Len) := B_Suffix;
163 Body_Suffix := Name_Find;
164 Body_Suffix_Length := B_Suffix'Length;
167 if S_Suffix'Length > 0 then
168 Name_Len := S_Suffix'Length;
169 Name_Buffer (1 .. Name_Len) := S_Suffix;
170 Spec_Suffix := Name_Find;
171 Spec_Suffix_Length := S_Suffix'Length;
175 -- Start of processing for Executable_Of
180 (B_Suffix => Body_Suffix_Of (In_Tree, "ada", Naming),
181 S_Suffix => Spec_Suffix_Of (In_Tree, "ada", Naming));
183 elsif Language /= "" then
185 (B_Suffix => Body_Suffix_Of (In_Tree, Language, Naming),
186 S_Suffix => Spec_Suffix_Of (In_Tree, Language, Naming));
189 if Builder_Package /= No_Package then
190 if Get_Mode = Multi_Language then
191 Executable_Suffix_Name :=
192 In_Tree.Projects.Table (Project).Config.Executable_Suffix;
195 Executable_Suffix := Prj.Util.Value_Of
196 (Variable_Name => Name_Executable_Suffix,
197 In_Variables => In_Tree.Packages.Table
198 (Builder_Package).Decl.Attributes,
201 if Executable_Suffix /= Nil_Variable_Value
202 and then not Executable_Suffix.Default
204 Executable_Suffix_Name := Executable_Suffix.Value;
208 if Executable = Nil_Variable_Value and Ada_Main then
209 Get_Name_String (Main);
211 -- Try as index the name minus the implementation suffix or minus
212 -- the specification suffix.
215 Name : constant String (1 .. Name_Len) :=
216 Name_Buffer (1 .. Name_Len);
217 Last : Positive := Name_Len;
219 Truncated : Boolean := False;
222 if Last > Natural (Length_Of_Name (Body_Suffix))
223 and then Name (Last - Body_Suffix_Length + 1 .. Last) =
224 Get_Name_String (Body_Suffix)
227 Last := Last - Body_Suffix_Length;
231 and then Last > Spec_Suffix_Length
232 and then Name (Last - Spec_Suffix_Length + 1 .. Last) =
233 Get_Name_String (Spec_Suffix)
236 Last := Last - Spec_Suffix_Length;
241 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
246 Attribute_Or_Array_Name => Name_Executable,
247 In_Package => Builder_Package,
253 -- If we have found an Executable attribute, return its value,
254 -- possibly suffixed by the executable suffix.
256 if Executable /= Nil_Variable_Value
257 and then Executable.Value /= Empty_Name
259 -- Get the executable name. If Executable_Suffix is defined,
260 -- make sure that it will be the extension of the executable.
263 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
264 Result : File_Name_Type;
267 if Executable_Suffix_Name /= No_Name then
268 Executable_Extension_On_Target := Executable_Suffix_Name;
271 Result := Executable_Name (File_Name_Type (Executable.Value));
272 Executable_Extension_On_Target := Saved_EEOT;
278 Get_Name_String (Main);
280 -- If there is a body suffix or a spec suffix, remove this suffix,
281 -- otherwise remove any suffix ('.' followed by other characters), if
284 if Body_Suffix /= No_Name
285 and then Name_Len > Body_Suffix_Length
286 and then Name_Buffer (Name_Len - Body_Suffix_Length + 1 .. Name_Len) =
287 Get_Name_String (Body_Suffix)
289 -- Found the body termination, remove it
291 Name_Len := Name_Len - Body_Suffix_Length;
293 elsif Spec_Suffix /= No_Name
294 and then Name_Len > Spec_Suffix_Length
296 Name_Buffer (Name_Len - Spec_Suffix_Length + 1 .. Name_Len) =
297 Get_Name_String (Spec_Suffix)
299 -- Found the spec termination, remove it
301 Name_Len := Name_Len - Spec_Suffix_Length;
304 -- Remove any suffix, if there is one
306 Get_Name_String (Strip_Suffix (Main));
309 if Executable_Suffix /= Nil_Variable_Value
310 and then not Executable_Suffix.Default
312 -- If attribute Executable_Suffix is specified, add this suffix
315 Suffix : constant String :=
316 Get_Name_String (Executable_Suffix.Value);
318 Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix;
319 Name_Len := Name_Len + Suffix'Length;
324 -- Get the executable name. If Executable_Suffix is defined in the
325 -- configuration, make sure that it will be the extension of the
329 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
330 Result : File_Name_Type;
333 if In_Tree.Projects.Table (Project).Config.Executable_Suffix /=
336 Executable_Extension_On_Target :=
337 In_Tree.Projects.Table (Project).Config.Executable_Suffix;
340 Result := Executable_Name (Name_Find);
341 Executable_Extension_On_Target := Saved_EEOT;
366 if File.Cursor = File.Buffer_Len then
370 A => File.Buffer'Address,
371 N => File.Buffer'Length);
373 if File.Buffer_Len = 0 then
374 File.End_Of_File_Reached := True;
381 File.Cursor := File.Cursor + 1;
385 -- Start of processing for Get_Line
389 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
392 Last := Line'First - 1;
394 if not File.End_Of_File_Reached then
396 C := File.Buffer (File.Cursor);
397 exit when C = ASCII.CR or else C = ASCII.LF;
402 if File.End_Of_File_Reached then
406 exit when Last = Line'Last;
409 if C = ASCII.CR or else C = ASCII.LF then
412 if File.End_Of_File_Reached then
418 and then File.Buffer (File.Cursor) = ASCII.LF
429 function Is_Valid (File : Text_File) return Boolean is
438 procedure Open (File : out Text_File; Name : String) is
439 FD : File_Descriptor;
440 File_Name : String (1 .. Name'Length + 1);
443 File_Name (1 .. Name'Length) := Name;
444 File_Name (File_Name'Last) := ASCII.NUL;
445 FD := Open_Read (Name => File_Name'Address,
446 Fmode => GNAT.OS_Lib.Text);
448 if FD = Invalid_FD then
452 File := new Text_File_Data;
456 A => File.Buffer'Address,
457 N => File.Buffer'Length);
459 if File.Buffer_Len = 0 then
460 File.End_Of_File_Reached := True;
472 (Into_List : in out Name_List_Index;
473 From_List : String_List_Id;
474 In_Tree : Project_Tree_Ref;
475 Lower_Case : Boolean := False)
477 Current_Name : Name_List_Index;
478 List : String_List_Id;
479 Element : String_Element;
480 Last : Name_List_Index :=
481 Name_List_Table.Last (In_Tree.Name_Lists);
485 Current_Name := Into_List;
486 while Current_Name /= No_Name_List
487 and then In_Tree.Name_Lists.Table (Current_Name).Next /= No_Name_List
489 Current_Name := In_Tree.Name_Lists.Table (Current_Name).Next;
493 while List /= Nil_String loop
494 Element := In_Tree.String_Elements.Table (List);
495 Value := Element.Value;
498 Get_Name_String (Value);
499 To_Lower (Name_Buffer (1 .. Name_Len));
503 Name_List_Table.Append
504 (In_Tree.Name_Lists, (Name => Value, Next => No_Name_List));
508 if Current_Name = No_Name_List then
512 In_Tree.Name_Lists.Table (Current_Name).Next := Last;
515 Current_Name := Last;
517 List := Element.Next;
526 (Variable : Variable_Value;
527 Default : String) return String
530 if Variable.Kind /= Single
531 or else Variable.Default
532 or else Variable.Value = No_Name
536 return Get_Name_String (Variable.Value);
542 In_Array : Array_Element_Id;
543 In_Tree : Project_Tree_Ref) return Name_Id
545 Current : Array_Element_Id;
546 Element : Array_Element;
547 Real_Index : Name_Id := Index;
552 if Current = No_Array_Element then
556 Element := In_Tree.Array_Elements.Table (Current);
558 if not Element.Index_Case_Sensitive then
559 Get_Name_String (Index);
560 To_Lower (Name_Buffer (1 .. Name_Len));
561 Real_Index := Name_Find;
564 while Current /= No_Array_Element loop
565 Element := In_Tree.Array_Elements.Table (Current);
567 if Real_Index = Element.Index then
568 exit when Element.Value.Kind /= Single;
569 exit when Element.Value.Value = Empty_String;
570 return Element.Value.Value;
572 Current := Element.Next;
581 Src_Index : Int := 0;
582 In_Array : Array_Element_Id;
583 In_Tree : Project_Tree_Ref;
584 Force_Lower_Case_Index : Boolean := False) return Variable_Value
586 Current : Array_Element_Id;
587 Element : Array_Element;
588 Real_Index_1 : Name_Id;
589 Real_Index_2 : Name_Id;
594 if Current = No_Array_Element then
595 return Nil_Variable_Value;
598 Element := In_Tree.Array_Elements.Table (Current);
600 Real_Index_1 := Index;
602 if not Element.Index_Case_Sensitive or Force_Lower_Case_Index then
603 Get_Name_String (Index);
604 To_Lower (Name_Buffer (1 .. Name_Len));
605 Real_Index_1 := Name_Find;
608 while Current /= No_Array_Element loop
609 Element := In_Tree.Array_Elements.Table (Current);
610 Real_Index_2 := Element.Index;
612 if not Element.Index_Case_Sensitive or Force_Lower_Case_Index then
613 Get_Name_String (Element.Index);
614 To_Lower (Name_Buffer (1 .. Name_Len));
615 Real_Index_2 := Name_Find;
618 if Real_Index_1 = Real_Index_2 and then
619 Src_Index = Element.Src_Index
621 return Element.Value;
623 Current := Element.Next;
627 return Nil_Variable_Value;
633 Attribute_Or_Array_Name : Name_Id;
634 In_Package : Package_Id;
635 In_Tree : Project_Tree_Ref;
636 Force_Lower_Case_Index : Boolean := False) return Variable_Value
638 The_Array : Array_Element_Id;
639 The_Attribute : Variable_Value := Nil_Variable_Value;
642 if In_Package /= No_Package then
644 -- First, look if there is an array element that fits
648 (Name => Attribute_Or_Array_Name,
649 In_Arrays => In_Tree.Packages.Table (In_Package).Decl.Arrays,
655 In_Array => The_Array,
657 Force_Lower_Case_Index => Force_Lower_Case_Index);
659 -- If there is no array element, look for a variable
661 if The_Attribute = Nil_Variable_Value then
664 (Variable_Name => Attribute_Or_Array_Name,
665 In_Variables => In_Tree.Packages.Table
666 (In_Package).Decl.Attributes,
671 return The_Attribute;
677 In_Arrays : Array_Id;
678 In_Tree : Project_Tree_Ref) return Name_Id
681 The_Array : Array_Data;
684 Current := In_Arrays;
685 while Current /= No_Array loop
686 The_Array := In_Tree.Arrays.Table (Current);
687 if The_Array.Name = In_Array then
689 (Index, In_Array => The_Array.Value, In_Tree => In_Tree);
691 Current := The_Array.Next;
700 In_Arrays : Array_Id;
701 In_Tree : Project_Tree_Ref) return Array_Element_Id
704 The_Array : Array_Data;
707 Current := In_Arrays;
708 while Current /= No_Array loop
709 The_Array := In_Tree.Arrays.Table (Current);
711 if The_Array.Name = Name then
712 return The_Array.Value;
714 Current := The_Array.Next;
718 return No_Array_Element;
723 In_Packages : Package_Id;
724 In_Tree : Project_Tree_Ref) return Package_Id
726 Current : Package_Id;
727 The_Package : Package_Element;
730 Current := In_Packages;
731 while Current /= No_Package loop
732 The_Package := In_Tree.Packages.Table (Current);
733 exit when The_Package.Name /= No_Name
734 and then The_Package.Name = Name;
735 Current := The_Package.Next;
742 (Variable_Name : Name_Id;
743 In_Variables : Variable_Id;
744 In_Tree : Project_Tree_Ref) return Variable_Value
746 Current : Variable_Id;
747 The_Variable : Variable;
750 Current := In_Variables;
751 while Current /= No_Variable loop
753 In_Tree.Variable_Elements.Table (Current);
755 if Variable_Name = The_Variable.Name then
756 return The_Variable.Value;
758 Current := The_Variable.Next;
762 return Nil_Variable_Value;
771 Max_Length : Positive;
772 Separator : Character)
774 First : Positive := S'First;
775 Last : Natural := S'Last;
778 -- Nothing to do for empty strings
782 -- Start on a new line if current line is already longer than
785 if Positive (Column) >= Max_Length then
789 -- If length of remainder is longer than Max_Length, we need to
790 -- cut the remainder in several lines.
792 while Positive (Column) + S'Last - First > Max_Length loop
794 -- Try the maximum length possible
796 Last := First + Max_Length - Positive (Column);
798 -- Look for last Separator in the line
800 while Last >= First and then S (Last) /= Separator loop
804 -- If we do not find a separator, we output the maximum length
808 Last := First + Max_Length - Positive (Column);
811 Write_Line (S (First .. Last));
813 -- Set the beginning of the new remainder
818 -- What is left goes to the buffer, without EOL
820 Write_Str (S (First .. S'Last));