1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2001-2011, 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;
29 with GNAT.Regexp; use GNAT.Regexp;
31 with Osint; use Osint;
32 with Output; use Output;
35 with Snames; use Snames;
37 with Targparm; use Targparm;
41 package body Prj.Util is
43 package Source_Info_Table is new Table.Table
44 (Table_Component_Type => Source_Info_Iterator,
45 Table_Index_Type => Natural,
48 Table_Increment => 100,
49 Table_Name => "Makeutl.Source_Info_Table");
51 package Source_Info_Project_HTable is new GNAT.HTable.Simple_HTable
52 (Header_Num => Prj.Header_Num,
59 procedure Free is new Ada.Unchecked_Deallocation
60 (Text_File_Data, Text_File);
66 procedure Close (File : in out Text_File) is
72 Prj.Com.Fail ("Close attempted on an invalid Text_File");
76 if File.Buffer_Len > 0 then
77 Len := Write (File.FD, File.Buffer'Address, File.Buffer_Len);
79 if Len /= File.Buffer_Len then
80 Prj.Com.Fail ("Unable to write to an out Text_File");
84 Close (File.FD, Status);
87 Prj.Com.Fail ("Unable to close an out Text_File");
92 -- Close in file, no need to test status, since this is a file that
93 -- we read, and the file was read successfully before we closed it.
105 procedure Create (File : out Text_File; Name : String) is
106 FD : File_Descriptor;
107 File_Name : String (1 .. Name'Length + 1);
110 File_Name (1 .. Name'Length) := Name;
111 File_Name (File_Name'Last) := ASCII.NUL;
112 FD := Create_File (Name => File_Name'Address,
113 Fmode => GNAT.OS_Lib.Text);
115 if FD = Invalid_FD then
119 File := new Text_File_Data;
121 File.Out_File := True;
122 File.End_Of_File_Reached := True;
131 (This : in out Name_List_Index;
132 Shared : Shared_Project_Tree_Data_Access)
134 Old_Current : Name_List_Index;
135 New_Current : Name_List_Index;
138 if This /= No_Name_List then
140 Name_List_Table.Increment_Last (Shared.Name_Lists);
141 New_Current := Name_List_Table.Last (Shared.Name_Lists);
143 Shared.Name_Lists.Table (New_Current) :=
144 (Shared.Name_Lists.Table (Old_Current).Name, No_Name_List);
147 Old_Current := Shared.Name_Lists.Table (Old_Current).Next;
148 exit when Old_Current = No_Name_List;
149 Shared.Name_Lists.Table (New_Current).Next := New_Current + 1;
150 Name_List_Table.Increment_Last (Shared.Name_Lists);
151 New_Current := New_Current + 1;
152 Shared.Name_Lists.Table (New_Current) :=
153 (Shared.Name_Lists.Table (Old_Current).Name, No_Name_List);
162 function End_Of_File (File : Text_File) return Boolean is
165 Prj.Com.Fail ("End_Of_File attempted on an invalid Text_File");
168 return File.End_Of_File_Reached;
175 function Executable_Of
176 (Project : Project_Id;
177 Shared : Shared_Project_Tree_Data_Access;
178 Main : File_Name_Type;
180 Ada_Main : Boolean := True;
181 Language : String := "";
182 Include_Suffix : Boolean := True) return File_Name_Type
184 pragma Assert (Project /= No_Project);
186 The_Packages : constant Package_Id := Project.Decl.Packages;
188 Builder_Package : constant Prj.Package_Id :=
190 (Name => Name_Builder,
191 In_Packages => The_Packages,
194 Executable : Variable_Value :=
196 (Name => Name_Id (Main),
198 Attribute_Or_Array_Name => Name_Executable,
199 In_Package => Builder_Package,
204 Spec_Suffix : Name_Id := No_Name;
205 Body_Suffix : Name_Id := No_Name;
207 Spec_Suffix_Length : Natural := 0;
208 Body_Suffix_Length : Natural := 0;
210 procedure Get_Suffixes
211 (B_Suffix : File_Name_Type;
212 S_Suffix : File_Name_Type);
213 -- Get the non empty suffixes in variables Spec_Suffix and Body_Suffix
215 function Add_Suffix (File : File_Name_Type) return File_Name_Type;
216 -- Return the name of the executable, based on File, and adding the
217 -- executable suffix if needed
223 procedure Get_Suffixes
224 (B_Suffix : File_Name_Type;
225 S_Suffix : File_Name_Type)
228 if B_Suffix /= No_File then
229 Body_Suffix := Name_Id (B_Suffix);
230 Body_Suffix_Length := Natural (Length_Of_Name (Body_Suffix));
233 if S_Suffix /= No_File then
234 Spec_Suffix := Name_Id (S_Suffix);
235 Spec_Suffix_Length := Natural (Length_Of_Name (Spec_Suffix));
243 function Add_Suffix (File : File_Name_Type) return File_Name_Type is
244 Saved_EEOT : constant Name_Id := Executable_Extension_On_Target;
245 Result : File_Name_Type;
246 Suffix_From_Project : Variable_Value;
248 if Include_Suffix then
249 if Project.Config.Executable_Suffix /= No_Name then
250 Executable_Extension_On_Target :=
251 Project.Config.Executable_Suffix;
254 Result := Executable_Name (File);
255 Executable_Extension_On_Target := Saved_EEOT;
258 elsif Builder_Package /= No_Package then
260 -- If the suffix is specified in the project itself, as opposed to
261 -- the config file, it needs to be taken into account. However,
262 -- when the project was processed, in both cases the suffix was
263 -- stored in Project.Config, so get it from the project again.
265 Suffix_From_Project :=
267 (Variable_Name => Name_Executable_Suffix,
269 Shared.Packages.Table (Builder_Package).Decl.Attributes,
272 if Suffix_From_Project /= Nil_Variable_Value
273 and then Suffix_From_Project.Value /= No_Name
275 Executable_Extension_On_Target := Suffix_From_Project.Value;
276 Result := Executable_Name (File);
277 Executable_Extension_On_Target := Saved_EEOT;
285 -- Start of processing for Executable_Of
289 Lang := Get_Language_From_Name (Project, "ada");
290 elsif Language /= "" then
291 Lang := Get_Language_From_Name (Project, Language);
296 (B_Suffix => Lang.Config.Naming_Data.Body_Suffix,
297 S_Suffix => Lang.Config.Naming_Data.Spec_Suffix);
300 if Builder_Package /= No_Package then
301 if Executable = Nil_Variable_Value and then Ada_Main then
302 Get_Name_String (Main);
304 -- Try as index the name minus the implementation suffix or minus
305 -- the specification suffix.
308 Name : constant String (1 .. Name_Len) :=
309 Name_Buffer (1 .. Name_Len);
310 Last : Positive := Name_Len;
312 Truncated : Boolean := False;
315 if Body_Suffix /= No_Name
316 and then Last > Natural (Length_Of_Name (Body_Suffix))
317 and then Name (Last - Body_Suffix_Length + 1 .. Last) =
318 Get_Name_String (Body_Suffix)
321 Last := Last - Body_Suffix_Length;
324 if Spec_Suffix /= No_Name
325 and then not Truncated
326 and then Last > Spec_Suffix_Length
327 and then Name (Last - Spec_Suffix_Length + 1 .. Last) =
328 Get_Name_String (Spec_Suffix)
331 Last := Last - Spec_Suffix_Length;
336 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
341 Attribute_Or_Array_Name => Name_Executable,
342 In_Package => Builder_Package,
348 -- If we have found an Executable attribute, return its value,
349 -- possibly suffixed by the executable suffix.
351 if Executable /= Nil_Variable_Value
352 and then Executable.Value /= No_Name
353 and then Length_Of_Name (Executable.Value) /= 0
355 return Add_Suffix (File_Name_Type (Executable.Value));
359 Get_Name_String (Main);
361 -- If there is a body suffix or a spec suffix, remove this suffix,
362 -- otherwise remove any suffix ('.' followed by other characters), if
365 if Body_Suffix /= No_Name
366 and then Name_Len > Body_Suffix_Length
367 and then Name_Buffer (Name_Len - Body_Suffix_Length + 1 .. Name_Len) =
368 Get_Name_String (Body_Suffix)
370 -- Found the body termination, remove it
372 Name_Len := Name_Len - Body_Suffix_Length;
374 elsif Spec_Suffix /= No_Name
375 and then Name_Len > Spec_Suffix_Length
377 Name_Buffer (Name_Len - Spec_Suffix_Length + 1 .. Name_Len) =
378 Get_Name_String (Spec_Suffix)
380 -- Found the spec termination, remove it
382 Name_Len := Name_Len - Spec_Suffix_Length;
385 -- Remove any suffix, if there is one
387 Get_Name_String (Strip_Suffix (Main));
390 return Add_Suffix (Name_Find);
412 if File.Cursor = File.Buffer_Len then
416 A => File.Buffer'Address,
417 N => File.Buffer'Length);
419 if File.Buffer_Len = 0 then
420 File.End_Of_File_Reached := True;
427 File.Cursor := File.Cursor + 1;
431 -- Start of processing for Get_Line
435 Prj.Com.Fail ("Get_Line attempted on an invalid Text_File");
437 elsif File.Out_File then
438 Prj.Com.Fail ("Get_Line attempted on an out file");
441 Last := Line'First - 1;
443 if not File.End_Of_File_Reached then
445 C := File.Buffer (File.Cursor);
446 exit when C = ASCII.CR or else C = ASCII.LF;
451 if File.End_Of_File_Reached then
455 exit when Last = Line'Last;
458 if C = ASCII.CR or else C = ASCII.LF then
461 if File.End_Of_File_Reached then
467 and then File.Buffer (File.Cursor) = ASCII.LF
479 (Iter : out Source_Info_Iterator;
480 For_Project : Name_Id)
482 Ind : constant Natural := Source_Info_Project_HTable.Get (For_Project);
485 Iter := (No_Source_Info, 0);
487 Iter := Source_Info_Table.Table (Ind);
495 function Is_Valid (File : Text_File) return Boolean is
504 procedure Next (Iter : in out Source_Info_Iterator) is
506 if Iter.Next = 0 then
507 Iter.Info := No_Source_Info;
510 Iter := Source_Info_Table.Table (Iter.Next);
518 procedure Open (File : out Text_File; Name : String) is
519 FD : File_Descriptor;
520 File_Name : String (1 .. Name'Length + 1);
523 File_Name (1 .. Name'Length) := Name;
524 File_Name (File_Name'Last) := ASCII.NUL;
525 FD := Open_Read (Name => File_Name'Address,
526 Fmode => GNAT.OS_Lib.Text);
528 if FD = Invalid_FD then
532 File := new Text_File_Data;
536 A => File.Buffer'Address,
537 N => File.Buffer'Length);
539 if File.Buffer_Len = 0 then
540 File.End_Of_File_Reached := True;
552 (Into_List : in out Name_List_Index;
553 From_List : String_List_Id;
554 In_Tree : Project_Tree_Ref;
555 Lower_Case : Boolean := False)
557 Shared : constant Shared_Project_Tree_Data_Access := In_Tree.Shared;
559 Current_Name : Name_List_Index;
560 List : String_List_Id;
561 Element : String_Element;
562 Last : Name_List_Index :=
563 Name_List_Table.Last (Shared.Name_Lists);
567 Current_Name := Into_List;
568 while Current_Name /= No_Name_List
569 and then Shared.Name_Lists.Table (Current_Name).Next /= No_Name_List
571 Current_Name := Shared.Name_Lists.Table (Current_Name).Next;
575 while List /= Nil_String loop
576 Element := Shared.String_Elements.Table (List);
577 Value := Element.Value;
580 Get_Name_String (Value);
581 To_Lower (Name_Buffer (1 .. Name_Len));
585 Name_List_Table.Append
586 (Shared.Name_Lists, (Name => Value, Next => No_Name_List));
590 if Current_Name = No_Name_List then
593 Shared.Name_Lists.Table (Current_Name).Next := Last;
596 Current_Name := Last;
598 List := Element.Next;
602 procedure Put (File : Text_File; S : String) is
606 Prj.Com.Fail ("Attempted to write on an invalid Text_File");
608 elsif not File.Out_File then
609 Prj.Com.Fail ("Attempted to write an in Text_File");
612 if File.Buffer_Len + S'Length > File.Buffer'Last then
614 Len := Write (File.FD, File.Buffer'Address, File.Buffer_Len);
616 if Len /= File.Buffer_Len then
617 Prj.Com.Fail ("Failed to write to an out Text_File");
620 File.Buffer_Len := 0;
623 File.Buffer (File.Buffer_Len + 1 .. File.Buffer_Len + S'Length) := S;
624 File.Buffer_Len := File.Buffer_Len + S'Length;
631 procedure Put_Line (File : Text_File; Line : String) is
632 L : String (1 .. Line'Length + 1);
634 L (1 .. Line'Length) := Line;
635 L (L'Last) := ASCII.LF;
639 ---------------------------
640 -- Read_Source_Info_File --
641 ---------------------------
643 procedure Read_Source_Info_File (Tree : Project_Tree_Ref) is
645 Info : Source_Info_Iterator;
648 procedure Report_Error;
654 procedure Report_Error is
656 Write_Line ("errors in source info file """ &
657 Tree.Source_Info_File_Name.all & '"');
658 Tree.Source_Info_File_Exists := False;
662 Source_Info_Project_HTable.Reset;
663 Source_Info_Table.Init;
665 if Tree.Source_Info_File_Name = null then
666 Tree.Source_Info_File_Exists := False;
670 Open (File, Tree.Source_Info_File_Name.all);
672 if not Is_Valid (File) then
673 if Opt.Verbose_Mode then
674 Write_Line ("source info file " & Tree.Source_Info_File_Name.all &
678 Tree.Source_Info_File_Exists := False;
682 Tree.Source_Info_File_Exists := True;
684 if Opt.Verbose_Mode then
685 Write_Line ("Reading source info file " &
686 Tree.Source_Info_File_Name.all);
690 while not End_Of_File (File) loop
691 Info := (new Source_Info_Data, 0);
692 Source_Info_Table.Increment_Last;
695 Get_Line (File, Name_Buffer, Name_Len);
697 Info.Info.Project := Proj;
698 Info.Next := Source_Info_Project_HTable.Get (Proj);
699 Source_Info_Project_HTable.Set (Proj, Source_Info_Table.Last);
701 if End_Of_File (File) then
707 Get_Line (File, Name_Buffer, Name_Len);
708 Info.Info.Language := Name_Find;
710 if End_Of_File (File) then
716 Get_Line (File, Name_Buffer, Name_Len);
717 Info.Info.Kind := Source_Kind'Value (Name_Buffer (1 .. Name_Len));
719 if End_Of_File (File) then
725 Get_Line (File, Name_Buffer, Name_Len);
726 Info.Info.Display_Path_Name := Name_Find;
727 Info.Info.Path_Name := Info.Info.Display_Path_Name;
729 if End_Of_File (File) then
737 Get_Line (File, Name_Buffer, Name_Len);
738 exit Option_Loop when Name_Len = 0;
740 if Name_Len <= 2 then
745 if Name_Buffer (1 .. 2) = "P=" then
746 Name_Buffer (1 .. Name_Len - 2) :=
747 Name_Buffer (3 .. Name_Len);
748 Name_Len := Name_Len - 2;
749 Info.Info.Path_Name := Name_Find;
751 elsif Name_Buffer (1 .. 2) = "U=" then
752 Name_Buffer (1 .. Name_Len - 2) :=
753 Name_Buffer (3 .. Name_Len);
754 Name_Len := Name_Len - 2;
755 Info.Info.Unit_Name := Name_Find;
757 elsif Name_Buffer (1 .. 2) = "I=" then
758 Info.Info.Index := Int'Value (Name_Buffer (3 .. Name_Len));
760 elsif Name_Buffer (1 .. Name_Len) = "N=Y" then
761 Info.Info.Naming_Exception := Yes;
763 elsif Name_Buffer (1 .. Name_Len) = "N=I" then
764 Info.Info.Naming_Exception := Inherited;
771 end loop Option_Loop;
773 Source_Info_Table.Table (Source_Info_Table.Last) := Info;
774 end loop Source_Loop;
782 end Read_Source_Info_File;
788 function Source_Info_Of (Iter : Source_Info_Iterator) return Source_Info is
798 (Variable : Variable_Value;
799 Default : String) return String
802 if Variable.Kind /= Single
803 or else Variable.Default
804 or else Variable.Value = No_Name
808 return Get_Name_String (Variable.Value);
814 In_Array : Array_Element_Id;
815 Shared : Shared_Project_Tree_Data_Access) return Name_Id
818 Current : Array_Element_Id;
819 Element : Array_Element;
820 Real_Index : Name_Id := Index;
825 if Current = No_Array_Element then
829 Element := Shared.Array_Elements.Table (Current);
831 if not Element.Index_Case_Sensitive then
832 Get_Name_String (Index);
833 To_Lower (Name_Buffer (1 .. Name_Len));
834 Real_Index := Name_Find;
837 while Current /= No_Array_Element loop
838 Element := Shared.Array_Elements.Table (Current);
840 if Real_Index = Element.Index then
841 exit when Element.Value.Kind /= Single;
842 exit when Element.Value.Value = Empty_String;
843 return Element.Value.Value;
845 Current := Element.Next;
854 Src_Index : Int := 0;
855 In_Array : Array_Element_Id;
856 Shared : Shared_Project_Tree_Data_Access;
857 Force_Lower_Case_Index : Boolean := False;
858 Allow_Wildcards : Boolean := False) return Variable_Value
860 Current : Array_Element_Id;
861 Element : Array_Element;
862 Real_Index_1 : Name_Id;
863 Real_Index_2 : Name_Id;
868 if Current = No_Array_Element then
869 return Nil_Variable_Value;
872 Element := Shared.Array_Elements.Table (Current);
874 Real_Index_1 := Index;
876 if not Element.Index_Case_Sensitive or else Force_Lower_Case_Index then
877 if Index /= All_Other_Names then
878 Get_Name_String (Index);
879 To_Lower (Name_Buffer (1 .. Name_Len));
880 Real_Index_1 := Name_Find;
884 while Current /= No_Array_Element loop
885 Element := Shared.Array_Elements.Table (Current);
886 Real_Index_2 := Element.Index;
888 if not Element.Index_Case_Sensitive
889 or else Force_Lower_Case_Index
891 if Element.Index /= All_Other_Names then
892 Get_Name_String (Element.Index);
893 To_Lower (Name_Buffer (1 .. Name_Len));
894 Real_Index_2 := Name_Find;
898 if Src_Index = Element.Src_Index and then
899 (Real_Index_1 = Real_Index_2 or else
900 (Real_Index_2 /= All_Other_Names and then
901 Allow_Wildcards and then
902 Match (Get_Name_String (Real_Index_1),
903 Compile (Get_Name_String (Real_Index_2),
906 return Element.Value;
908 Current := Element.Next;
912 return Nil_Variable_Value;
918 Attribute_Or_Array_Name : Name_Id;
919 In_Package : Package_Id;
920 Shared : Shared_Project_Tree_Data_Access;
921 Force_Lower_Case_Index : Boolean := False;
922 Allow_Wildcards : Boolean := False) return Variable_Value
924 The_Array : Array_Element_Id;
925 The_Attribute : Variable_Value := Nil_Variable_Value;
928 if In_Package /= No_Package then
930 -- First, look if there is an array element that fits
934 (Name => Attribute_Or_Array_Name,
935 In_Arrays => Shared.Packages.Table (In_Package).Decl.Arrays,
941 In_Array => The_Array,
943 Force_Lower_Case_Index => Force_Lower_Case_Index,
944 Allow_Wildcards => Allow_Wildcards);
946 -- If there is no array element, look for a variable
948 if The_Attribute = Nil_Variable_Value then
951 (Variable_Name => Attribute_Or_Array_Name,
952 In_Variables => Shared.Packages.Table
953 (In_Package).Decl.Attributes,
958 return The_Attribute;
964 In_Arrays : Array_Id;
965 Shared : Shared_Project_Tree_Data_Access) return Name_Id
968 The_Array : Array_Data;
971 Current := In_Arrays;
972 while Current /= No_Array loop
973 The_Array := Shared.Arrays.Table (Current);
974 if The_Array.Name = In_Array then
976 (Index, In_Array => The_Array.Value, Shared => Shared);
978 Current := The_Array.Next;
987 In_Arrays : Array_Id;
988 Shared : Shared_Project_Tree_Data_Access) return Array_Element_Id
991 The_Array : Array_Data;
994 Current := In_Arrays;
995 while Current /= No_Array loop
996 The_Array := Shared.Arrays.Table (Current);
998 if The_Array.Name = Name then
999 return The_Array.Value;
1001 Current := The_Array.Next;
1005 return No_Array_Element;
1010 In_Packages : Package_Id;
1011 Shared : Shared_Project_Tree_Data_Access) return Package_Id
1013 Current : Package_Id;
1014 The_Package : Package_Element;
1017 Current := In_Packages;
1018 while Current /= No_Package loop
1019 The_Package := Shared.Packages.Table (Current);
1020 exit when The_Package.Name /= No_Name
1021 and then The_Package.Name = Name;
1022 Current := The_Package.Next;
1029 (Variable_Name : Name_Id;
1030 In_Variables : Variable_Id;
1031 Shared : Shared_Project_Tree_Data_Access) return Variable_Value
1033 Current : Variable_Id;
1034 The_Variable : Variable;
1037 Current := In_Variables;
1038 while Current /= No_Variable loop
1039 The_Variable := Shared.Variable_Elements.Table (Current);
1041 if Variable_Name = The_Variable.Name then
1042 return The_Variable.Value;
1044 Current := The_Variable.Next;
1048 return Nil_Variable_Value;
1051 ----------------------------
1052 -- Write_Source_Info_File --
1053 ----------------------------
1055 procedure Write_Source_Info_File (Tree : Project_Tree_Ref) is
1056 Iter : Source_Iterator := For_Each_Source (Tree);
1057 Source : Prj.Source_Id;
1061 if Opt.Verbose_Mode then
1062 Write_Line ("Writing new source info file " &
1063 Tree.Source_Info_File_Name.all);
1066 Create (File, Tree.Source_Info_File_Name.all);
1068 if not Is_Valid (File) then
1069 Write_Line ("warning: unable to create source info file """ &
1070 Tree.Source_Info_File_Name.all & '"');
1075 Source := Element (Iter);
1076 exit when Source = No_Source;
1078 if not Source.Locally_Removed and then
1079 Source.Replaced_By = No_Source
1083 Put_Line (File, Get_Name_String (Source.Project.Name));
1087 Put_Line (File, Get_Name_String (Source.Language.Name));
1091 Put_Line (File, Source.Kind'Img);
1093 -- Display path name
1095 Put_Line (File, Get_Name_String (Source.Path.Display_Name));
1101 if Source.Path.Name /= Source.Path.Display_Name then
1103 Put_Line (File, Get_Name_String (Source.Path.Name));
1108 if Source.Unit /= No_Unit_Index then
1110 Put_Line (File, Get_Name_String (Source.Unit.Name));
1113 -- Multi-source index (I=)
1115 if Source.Index /= 0 then
1117 Put_Line (File, Source.Index'Img);
1120 -- Naming exception ("N=T");
1122 if Source.Naming_Exception = Yes then
1123 Put_Line (File, "N=Y");
1125 elsif Source.Naming_Exception = Inherited then
1126 Put_Line (File, "N=I");
1129 -- Empty line to indicate end of info on this source
1131 Put_Line (File, "");
1138 end Write_Source_Info_File;
1146 Max_Length : Positive;
1147 Separator : Character)
1149 First : Positive := S'First;
1150 Last : Natural := S'Last;
1153 -- Nothing to do for empty strings
1155 if S'Length > 0 then
1157 -- Start on a new line if current line is already longer than
1160 if Positive (Column) >= Max_Length then
1164 -- If length of remainder is longer than Max_Length, we need to
1165 -- cut the remainder in several lines.
1167 while Positive (Column) + S'Last - First > Max_Length loop
1169 -- Try the maximum length possible
1171 Last := First + Max_Length - Positive (Column);
1173 -- Look for last Separator in the line
1175 while Last >= First and then S (Last) /= Separator loop
1179 -- If we do not find a separator, we output the maximum length
1182 if Last < First then
1183 Last := First + Max_Length - Positive (Column);
1186 Write_Line (S (First .. Last));
1188 -- Set the beginning of the new remainder
1193 -- What is left goes to the buffer, without EOL
1195 Write_Str (S (First .. S'Last));