1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2010, 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 := "";
114 Include_Suffix
: Boolean := True) return File_Name_Type
116 pragma Assert
(Project
/= No_Project
);
118 The_Packages
: constant Package_Id
:= 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
,
136 Spec_Suffix
: Name_Id
:= No_Name
;
137 Body_Suffix
: Name_Id
:= No_Name
;
139 Spec_Suffix_Length
: Natural := 0;
140 Body_Suffix_Length
: Natural := 0;
142 procedure Get_Suffixes
143 (B_Suffix
: File_Name_Type
;
144 S_Suffix
: File_Name_Type
);
145 -- Get the non empty suffixes in variables Spec_Suffix and Body_Suffix
147 function Add_Suffix
(File
: File_Name_Type
) return File_Name_Type
;
148 -- Return the name of the executable, based on File, and adding the
149 -- executable suffix if needed
155 procedure Get_Suffixes
156 (B_Suffix
: File_Name_Type
;
157 S_Suffix
: File_Name_Type
)
160 if B_Suffix
/= No_File
then
161 Body_Suffix
:= Name_Id
(B_Suffix
);
162 Body_Suffix_Length
:= Natural (Length_Of_Name
(Body_Suffix
));
165 if S_Suffix
/= No_File
then
166 Spec_Suffix
:= Name_Id
(S_Suffix
);
167 Spec_Suffix_Length
:= Natural (Length_Of_Name
(Spec_Suffix
));
175 function Add_Suffix
(File
: File_Name_Type
) return File_Name_Type
is
176 Saved_EEOT
: constant Name_Id
:= Executable_Extension_On_Target
;
177 Result
: File_Name_Type
;
178 Suffix_From_Project
: Variable_Value
;
180 if Include_Suffix
then
181 if Project
.Config
.Executable_Suffix
/= No_Name
then
182 Executable_Extension_On_Target
:=
183 Project
.Config
.Executable_Suffix
;
186 Result
:= Executable_Name
(File
);
187 Executable_Extension_On_Target
:= Saved_EEOT
;
190 elsif Builder_Package
/= No_Package
then
192 -- If the suffix is specified in the project itself, as opposed to
193 -- the config file, it needs to be taken into account. However,
194 -- when the project was processed, in both cases the suffix was
195 -- stored in Project.Config, so get it from the project again.
197 Suffix_From_Project
:=
199 (Variable_Name
=> Name_Executable_Suffix
,
201 In_Tree
.Packages
.Table
(Builder_Package
).Decl
.Attributes
,
204 if Suffix_From_Project
/= Nil_Variable_Value
205 and then Suffix_From_Project
.Value
/= No_Name
207 Executable_Extension_On_Target
:= Suffix_From_Project
.Value
;
208 Result
:= Executable_Name
(File
);
209 Executable_Extension_On_Target
:= Saved_EEOT
;
217 -- Start of processing for Executable_Of
221 Lang
:= Get_Language_From_Name
(Project
, "ada");
222 elsif Language
/= "" then
223 Lang
:= Get_Language_From_Name
(Project
, Language
);
228 (B_Suffix
=> Lang
.Config
.Naming_Data
.Body_Suffix
,
229 S_Suffix
=> Lang
.Config
.Naming_Data
.Spec_Suffix
);
232 if Builder_Package
/= No_Package
then
233 if Executable
= Nil_Variable_Value
and then Ada_Main
then
234 Get_Name_String
(Main
);
236 -- Try as index the name minus the implementation suffix or minus
237 -- the specification suffix.
240 Name
: constant String (1 .. Name_Len
) :=
241 Name_Buffer
(1 .. Name_Len
);
242 Last
: Positive := Name_Len
;
244 Truncated
: Boolean := False;
247 if Body_Suffix
/= No_Name
248 and then Last
> Natural (Length_Of_Name
(Body_Suffix
))
249 and then Name
(Last
- Body_Suffix_Length
+ 1 .. Last
) =
250 Get_Name_String
(Body_Suffix
)
253 Last
:= Last
- Body_Suffix_Length
;
256 if Spec_Suffix
/= No_Name
257 and then not Truncated
258 and then Last
> Spec_Suffix_Length
259 and then Name
(Last
- Spec_Suffix_Length
+ 1 .. Last
) =
260 Get_Name_String
(Spec_Suffix
)
263 Last
:= Last
- Spec_Suffix_Length
;
268 Name_Buffer
(1 .. Name_Len
) := Name
(1 .. Last
);
273 Attribute_Or_Array_Name
=> Name_Executable
,
274 In_Package
=> Builder_Package
,
280 -- If we have found an Executable attribute, return its value,
281 -- possibly suffixed by the executable suffix.
283 if Executable
/= Nil_Variable_Value
284 and then Executable
.Value
/= No_Name
285 and then Length_Of_Name
(Executable
.Value
) /= 0
287 return Add_Suffix
(File_Name_Type
(Executable
.Value
));
291 Get_Name_String
(Main
);
293 -- If there is a body suffix or a spec suffix, remove this suffix,
294 -- otherwise remove any suffix ('.' followed by other characters), if
297 if Body_Suffix
/= No_Name
298 and then Name_Len
> Body_Suffix_Length
299 and then Name_Buffer
(Name_Len
- Body_Suffix_Length
+ 1 .. Name_Len
) =
300 Get_Name_String
(Body_Suffix
)
302 -- Found the body termination, remove it
304 Name_Len
:= Name_Len
- Body_Suffix_Length
;
306 elsif Spec_Suffix
/= No_Name
307 and then Name_Len
> Spec_Suffix_Length
309 Name_Buffer
(Name_Len
- Spec_Suffix_Length
+ 1 .. Name_Len
) =
310 Get_Name_String
(Spec_Suffix
)
312 -- Found the spec termination, remove it
314 Name_Len
:= Name_Len
- Spec_Suffix_Length
;
317 -- Remove any suffix, if there is one
319 Get_Name_String
(Strip_Suffix
(Main
));
322 return Add_Suffix
(Name_Find
);
344 if File
.Cursor
= File
.Buffer_Len
then
348 A
=> File
.Buffer
'Address,
349 N
=> File
.Buffer
'Length);
351 if File
.Buffer_Len
= 0 then
352 File
.End_Of_File_Reached
:= True;
359 File
.Cursor
:= File
.Cursor
+ 1;
363 -- Start of processing for Get_Line
367 Prj
.Com
.Fail
("Get_Line attempted on an invalid Text_File");
370 Last
:= Line
'First - 1;
372 if not File
.End_Of_File_Reached
then
374 C
:= File
.Buffer
(File
.Cursor
);
375 exit when C
= ASCII
.CR
or else C
= ASCII
.LF
;
380 if File
.End_Of_File_Reached
then
384 exit when Last
= Line
'Last;
387 if C
= ASCII
.CR
or else C
= ASCII
.LF
then
390 if File
.End_Of_File_Reached
then
396 and then File
.Buffer
(File
.Cursor
) = ASCII
.LF
407 function Is_Valid
(File
: Text_File
) return Boolean is
416 procedure Open
(File
: out Text_File
; Name
: String) is
417 FD
: File_Descriptor
;
418 File_Name
: String (1 .. Name
'Length + 1);
421 File_Name
(1 .. Name
'Length) := Name
;
422 File_Name
(File_Name
'Last) := ASCII
.NUL
;
423 FD
:= Open_Read
(Name
=> File_Name
'Address,
424 Fmode
=> GNAT
.OS_Lib
.Text
);
426 if FD
= Invalid_FD
then
430 File
:= new Text_File_Data
;
434 A
=> File
.Buffer
'Address,
435 N
=> File
.Buffer
'Length);
437 if File
.Buffer_Len
= 0 then
438 File
.End_Of_File_Reached
:= True;
450 (Into_List
: in out Name_List_Index
;
451 From_List
: String_List_Id
;
452 In_Tree
: Project_Tree_Ref
;
453 Lower_Case
: Boolean := False)
455 Current_Name
: Name_List_Index
;
456 List
: String_List_Id
;
457 Element
: String_Element
;
458 Last
: Name_List_Index
:=
459 Name_List_Table
.Last
(In_Tree
.Name_Lists
);
463 Current_Name
:= Into_List
;
464 while Current_Name
/= No_Name_List
465 and then In_Tree
.Name_Lists
.Table
(Current_Name
).Next
/= No_Name_List
467 Current_Name
:= In_Tree
.Name_Lists
.Table
(Current_Name
).Next
;
471 while List
/= Nil_String
loop
472 Element
:= In_Tree
.String_Elements
.Table
(List
);
473 Value
:= Element
.Value
;
476 Get_Name_String
(Value
);
477 To_Lower
(Name_Buffer
(1 .. Name_Len
));
481 Name_List_Table
.Append
482 (In_Tree
.Name_Lists
, (Name
=> Value
, Next
=> No_Name_List
));
486 if Current_Name
= No_Name_List
then
490 In_Tree
.Name_Lists
.Table
(Current_Name
).Next
:= Last
;
493 Current_Name
:= Last
;
495 List
:= Element
.Next
;
504 (Variable
: Variable_Value
;
505 Default
: String) return String
508 if Variable
.Kind
/= Single
509 or else Variable
.Default
510 or else Variable
.Value
= No_Name
514 return Get_Name_String
(Variable
.Value
);
520 In_Array
: Array_Element_Id
;
521 In_Tree
: Project_Tree_Ref
) return Name_Id
523 Current
: Array_Element_Id
;
524 Element
: Array_Element
;
525 Real_Index
: Name_Id
:= Index
;
530 if Current
= No_Array_Element
then
534 Element
:= In_Tree
.Array_Elements
.Table
(Current
);
536 if not Element
.Index_Case_Sensitive
then
537 Get_Name_String
(Index
);
538 To_Lower
(Name_Buffer
(1 .. Name_Len
));
539 Real_Index
:= Name_Find
;
542 while Current
/= No_Array_Element
loop
543 Element
:= In_Tree
.Array_Elements
.Table
(Current
);
545 if Real_Index
= Element
.Index
then
546 exit when Element
.Value
.Kind
/= Single
;
547 exit when Element
.Value
.Value
= Empty_String
;
548 return Element
.Value
.Value
;
550 Current
:= Element
.Next
;
559 Src_Index
: Int
:= 0;
560 In_Array
: Array_Element_Id
;
561 In_Tree
: Project_Tree_Ref
;
562 Force_Lower_Case_Index
: Boolean := False) return Variable_Value
564 Current
: Array_Element_Id
;
565 Element
: Array_Element
;
566 Real_Index_1
: Name_Id
;
567 Real_Index_2
: Name_Id
;
572 if Current
= No_Array_Element
then
573 return Nil_Variable_Value
;
576 Element
:= In_Tree
.Array_Elements
.Table
(Current
);
578 Real_Index_1
:= Index
;
580 if not Element
.Index_Case_Sensitive
or else Force_Lower_Case_Index
then
581 if Index
/= All_Other_Names
then
582 Get_Name_String
(Index
);
583 To_Lower
(Name_Buffer
(1 .. Name_Len
));
584 Real_Index_1
:= Name_Find
;
588 while Current
/= No_Array_Element
loop
589 Element
:= In_Tree
.Array_Elements
.Table
(Current
);
590 Real_Index_2
:= Element
.Index
;
592 if not Element
.Index_Case_Sensitive
593 or else Force_Lower_Case_Index
595 if Element
.Index
/= All_Other_Names
then
596 Get_Name_String
(Element
.Index
);
597 To_Lower
(Name_Buffer
(1 .. Name_Len
));
598 Real_Index_2
:= Name_Find
;
602 if Real_Index_1
= Real_Index_2
and then
603 Src_Index
= Element
.Src_Index
605 return Element
.Value
;
607 Current
:= Element
.Next
;
611 return Nil_Variable_Value
;
617 Attribute_Or_Array_Name
: Name_Id
;
618 In_Package
: Package_Id
;
619 In_Tree
: Project_Tree_Ref
;
620 Force_Lower_Case_Index
: Boolean := False) return Variable_Value
622 The_Array
: Array_Element_Id
;
623 The_Attribute
: Variable_Value
:= Nil_Variable_Value
;
626 if In_Package
/= No_Package
then
628 -- First, look if there is an array element that fits
632 (Name
=> Attribute_Or_Array_Name
,
633 In_Arrays
=> In_Tree
.Packages
.Table
(In_Package
).Decl
.Arrays
,
639 In_Array
=> The_Array
,
641 Force_Lower_Case_Index
=> Force_Lower_Case_Index
);
643 -- If there is no array element, look for a variable
645 if The_Attribute
= Nil_Variable_Value
then
648 (Variable_Name
=> Attribute_Or_Array_Name
,
649 In_Variables
=> In_Tree
.Packages
.Table
650 (In_Package
).Decl
.Attributes
,
655 return The_Attribute
;
661 In_Arrays
: Array_Id
;
662 In_Tree
: Project_Tree_Ref
) return Name_Id
665 The_Array
: Array_Data
;
668 Current
:= In_Arrays
;
669 while Current
/= No_Array
loop
670 The_Array
:= In_Tree
.Arrays
.Table
(Current
);
671 if The_Array
.Name
= In_Array
then
673 (Index
, In_Array
=> The_Array
.Value
, In_Tree
=> In_Tree
);
675 Current
:= The_Array
.Next
;
684 In_Arrays
: Array_Id
;
685 In_Tree
: Project_Tree_Ref
) return Array_Element_Id
688 The_Array
: Array_Data
;
691 Current
:= In_Arrays
;
692 while Current
/= No_Array
loop
693 The_Array
:= In_Tree
.Arrays
.Table
(Current
);
695 if The_Array
.Name
= Name
then
696 return The_Array
.Value
;
698 Current
:= The_Array
.Next
;
702 return No_Array_Element
;
707 In_Packages
: Package_Id
;
708 In_Tree
: Project_Tree_Ref
) return Package_Id
710 Current
: Package_Id
;
711 The_Package
: Package_Element
;
714 Current
:= In_Packages
;
715 while Current
/= No_Package
loop
716 The_Package
:= In_Tree
.Packages
.Table
(Current
);
717 exit when The_Package
.Name
/= No_Name
718 and then The_Package
.Name
= Name
;
719 Current
:= The_Package
.Next
;
726 (Variable_Name
: Name_Id
;
727 In_Variables
: Variable_Id
;
728 In_Tree
: Project_Tree_Ref
) return Variable_Value
730 Current
: Variable_Id
;
731 The_Variable
: Variable
;
734 Current
:= In_Variables
;
735 while Current
/= No_Variable
loop
737 In_Tree
.Variable_Elements
.Table
(Current
);
739 if Variable_Name
= The_Variable
.Name
then
740 return The_Variable
.Value
;
742 Current
:= The_Variable
.Next
;
746 return Nil_Variable_Value
;
755 Max_Length
: Positive;
756 Separator
: Character)
758 First
: Positive := S
'First;
759 Last
: Natural := S
'Last;
762 -- Nothing to do for empty strings
766 -- Start on a new line if current line is already longer than
769 if Positive (Column
) >= Max_Length
then
773 -- If length of remainder is longer than Max_Length, we need to
774 -- cut the remainder in several lines.
776 while Positive (Column
) + S
'Last - First
> Max_Length
loop
778 -- Try the maximum length possible
780 Last
:= First
+ Max_Length
- Positive (Column
);
782 -- Look for last Separator in the line
784 while Last
>= First
and then S
(Last
) /= Separator
loop
788 -- If we do not find a separator, we output the maximum length
792 Last
:= First
+ Max_Length
- Positive (Column
);
795 Write_Line
(S
(First
.. Last
));
797 -- Set the beginning of the new remainder
802 -- What is left goes to the buffer, without EOL
804 Write_Str
(S
(First
.. S
'Last));