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;
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 In_Tree : Project_Tree_Ref)
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 (In_Tree.Name_Lists);
141 New_Current := Name_List_Table.Last (In_Tree.Name_Lists);
143 In_Tree.Name_Lists.Table (New_Current) :=
144 (In_Tree.Name_Lists.Table (Old_Current).Name, No_Name_List);
147 Old_Current := In_Tree.Name_Lists.Table (Old_Current).Next;
148 exit when Old_Current = No_Name_List;
149 In_Tree.Name_Lists.Table (New_Current).Next := New_Current + 1;
150 Name_List_Table.Increment_Last (In_Tree.Name_Lists);
151 New_Current := New_Current + 1;
152 In_Tree.Name_Lists.Table (New_Current) :=
153 (In_Tree.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 In_Tree : Project_Tree_Ref;
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 In_Tree.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 Current_Name : Name_List_Index;
558 List : String_List_Id;
559 Element : String_Element;
560 Last : Name_List_Index :=
561 Name_List_Table.Last (In_Tree.Name_Lists);
565 Current_Name := Into_List;
566 while Current_Name /= No_Name_List
567 and then In_Tree.Name_Lists.Table (Current_Name).Next /= No_Name_List
569 Current_Name := In_Tree.Name_Lists.Table (Current_Name).Next;
573 while List /= Nil_String loop
574 Element := In_Tree.String_Elements.Table (List);
575 Value := Element.Value;
578 Get_Name_String (Value);
579 To_Lower (Name_Buffer (1 .. Name_Len));
583 Name_List_Table.Append
584 (In_Tree.Name_Lists, (Name => Value, Next => No_Name_List));
588 if Current_Name = No_Name_List then
592 In_Tree.Name_Lists.Table (Current_Name).Next := Last;
595 Current_Name := Last;
597 List := Element.Next;
601 procedure Put (File : Text_File; S : String) is
605 Prj.Com.Fail ("Attempted to write on an invalid Text_File");
607 elsif not File.Out_File then
608 Prj.Com.Fail ("Attempted to write an in Text_File");
611 if File.Buffer_Len + S'Length > File.Buffer'Last then
613 Len := Write (File.FD, File.Buffer'Address, File.Buffer_Len);
615 if Len /= File.Buffer_Len then
616 Prj.Com.Fail ("Failed to write to an out Text_File");
619 File.Buffer_Len := 0;
622 File.Buffer (File.Buffer_Len + 1 .. File.Buffer_Len + S'Length) := S;
623 File.Buffer_Len := File.Buffer_Len + S'Length;
630 procedure Put_Line (File : Text_File; Line : String) is
631 L : String (1 .. Line'Length + 1);
633 L (1 .. Line'Length) := Line;
634 L (L'Last) := ASCII.LF;
638 ---------------------------
639 -- Read_Source_Info_File --
640 ---------------------------
642 procedure Read_Source_Info_File (Tree : Project_Tree_Ref) is
644 Info : Source_Info_Iterator;
647 procedure Report_Error;
653 procedure Report_Error is
655 Write_Line ("errors in source info file """ &
656 Tree.Source_Info_File_Name.all & '"');
657 Tree.Source_Info_File_Exists := False;
661 Source_Info_Project_HTable.Reset;
662 Source_Info_Table.Init;
664 if Tree.Source_Info_File_Name = null then
665 Tree.Source_Info_File_Exists := False;
669 Open (File, Tree.Source_Info_File_Name.all);
671 if not Is_Valid (File) then
672 if Opt.Verbose_Mode then
673 Write_Line ("source info file " & Tree.Source_Info_File_Name.all &
677 Tree.Source_Info_File_Exists := False;
681 Tree.Source_Info_File_Exists := True;
683 if Opt.Verbose_Mode then
684 Write_Line ("Reading source info file " &
685 Tree.Source_Info_File_Name.all);
689 while not End_Of_File (File) loop
690 Info := (new Source_Info_Data, 0);
691 Source_Info_Table.Increment_Last;
694 Get_Line (File, Name_Buffer, Name_Len);
696 Info.Info.Project := Proj;
697 Info.Next := Source_Info_Project_HTable.Get (Proj);
698 Source_Info_Project_HTable.Set (Proj, Source_Info_Table.Last);
700 if End_Of_File (File) then
706 Get_Line (File, Name_Buffer, Name_Len);
707 Info.Info.Language := Name_Find;
709 if End_Of_File (File) then
715 Get_Line (File, Name_Buffer, Name_Len);
716 Info.Info.Kind := Source_Kind'Value (Name_Buffer (1 .. Name_Len));
718 if End_Of_File (File) then
724 Get_Line (File, Name_Buffer, Name_Len);
725 Info.Info.Display_Path_Name := Name_Find;
726 Info.Info.Path_Name := Info.Info.Display_Path_Name;
728 if End_Of_File (File) then
736 Get_Line (File, Name_Buffer, Name_Len);
737 exit Option_Loop when Name_Len = 0;
739 if Name_Len <= 2 then
744 if Name_Buffer (1 .. 2) = "P=" then
745 Name_Buffer (1 .. Name_Len - 2) :=
746 Name_Buffer (3 .. Name_Len);
747 Name_Len := Name_Len - 2;
748 Info.Info.Path_Name := Name_Find;
750 elsif Name_Buffer (1 .. 2) = "U=" then
751 Name_Buffer (1 .. Name_Len - 2) :=
752 Name_Buffer (3 .. Name_Len);
753 Name_Len := Name_Len - 2;
754 Info.Info.Unit_Name := Name_Find;
756 elsif Name_Buffer (1 .. 2) = "I=" then
757 Info.Info.Index := Int'Value (Name_Buffer (3 .. Name_Len));
759 elsif Name_Buffer (1 .. Name_Len) = "N=T" then
760 Info.Info.Naming_Exception := True;
767 end loop Option_Loop;
769 Source_Info_Table.Table (Source_Info_Table.Last) := Info;
770 end loop Source_Loop;
778 end Read_Source_Info_File;
784 function Source_Info_Of (Iter : Source_Info_Iterator) return Source_Info is
794 (Variable : Variable_Value;
795 Default : String) return String
798 if Variable.Kind /= Single
799 or else Variable.Default
800 or else Variable.Value = No_Name
804 return Get_Name_String (Variable.Value);
810 In_Array : Array_Element_Id;
811 In_Tree : Project_Tree_Ref) return Name_Id
813 Current : Array_Element_Id;
814 Element : Array_Element;
815 Real_Index : Name_Id := Index;
820 if Current = No_Array_Element then
824 Element := In_Tree.Array_Elements.Table (Current);
826 if not Element.Index_Case_Sensitive then
827 Get_Name_String (Index);
828 To_Lower (Name_Buffer (1 .. Name_Len));
829 Real_Index := Name_Find;
832 while Current /= No_Array_Element loop
833 Element := In_Tree.Array_Elements.Table (Current);
835 if Real_Index = Element.Index then
836 exit when Element.Value.Kind /= Single;
837 exit when Element.Value.Value = Empty_String;
838 return Element.Value.Value;
840 Current := Element.Next;
849 Src_Index : Int := 0;
850 In_Array : Array_Element_Id;
851 In_Tree : Project_Tree_Ref;
852 Force_Lower_Case_Index : Boolean := False;
853 Allow_Wildcards : Boolean := False) return Variable_Value
855 Current : Array_Element_Id;
856 Element : Array_Element;
857 Real_Index_1 : Name_Id;
858 Real_Index_2 : Name_Id;
863 if Current = No_Array_Element then
864 return Nil_Variable_Value;
867 Element := In_Tree.Array_Elements.Table (Current);
869 Real_Index_1 := Index;
871 if not Element.Index_Case_Sensitive or else Force_Lower_Case_Index then
872 if Index /= All_Other_Names then
873 Get_Name_String (Index);
874 To_Lower (Name_Buffer (1 .. Name_Len));
875 Real_Index_1 := Name_Find;
879 while Current /= No_Array_Element loop
880 Element := In_Tree.Array_Elements.Table (Current);
881 Real_Index_2 := Element.Index;
883 if not Element.Index_Case_Sensitive
884 or else Force_Lower_Case_Index
886 if Element.Index /= All_Other_Names then
887 Get_Name_String (Element.Index);
888 To_Lower (Name_Buffer (1 .. Name_Len));
889 Real_Index_2 := Name_Find;
893 if Src_Index = Element.Src_Index and then
894 (Real_Index_1 = Real_Index_2 or else
895 (Real_Index_2 /= All_Other_Names and then
896 Allow_Wildcards and then
897 Match (Get_Name_String (Real_Index_1),
898 Compile (Get_Name_String (Real_Index_2),
901 return Element.Value;
903 Current := Element.Next;
907 return Nil_Variable_Value;
913 Attribute_Or_Array_Name : Name_Id;
914 In_Package : Package_Id;
915 In_Tree : Project_Tree_Ref;
916 Force_Lower_Case_Index : Boolean := False;
917 Allow_Wildcards : Boolean := False) return Variable_Value
919 The_Array : Array_Element_Id;
920 The_Attribute : Variable_Value := Nil_Variable_Value;
923 if In_Package /= No_Package then
925 -- First, look if there is an array element that fits
929 (Name => Attribute_Or_Array_Name,
930 In_Arrays => In_Tree.Packages.Table (In_Package).Decl.Arrays,
936 In_Array => The_Array,
938 Force_Lower_Case_Index => Force_Lower_Case_Index,
939 Allow_Wildcards => Allow_Wildcards);
941 -- If there is no array element, look for a variable
943 if The_Attribute = Nil_Variable_Value then
946 (Variable_Name => Attribute_Or_Array_Name,
947 In_Variables => In_Tree.Packages.Table
948 (In_Package).Decl.Attributes,
953 return The_Attribute;
959 In_Arrays : Array_Id;
960 In_Tree : Project_Tree_Ref) return Name_Id
963 The_Array : Array_Data;
966 Current := In_Arrays;
967 while Current /= No_Array loop
968 The_Array := In_Tree.Arrays.Table (Current);
969 if The_Array.Name = In_Array then
971 (Index, In_Array => The_Array.Value, In_Tree => In_Tree);
973 Current := The_Array.Next;
982 In_Arrays : Array_Id;
983 In_Tree : Project_Tree_Ref) return Array_Element_Id
986 The_Array : Array_Data;
989 Current := In_Arrays;
990 while Current /= No_Array loop
991 The_Array := In_Tree.Arrays.Table (Current);
993 if The_Array.Name = Name then
994 return The_Array.Value;
996 Current := The_Array.Next;
1000 return No_Array_Element;
1005 In_Packages : Package_Id;
1006 In_Tree : Project_Tree_Ref) return Package_Id
1008 Current : Package_Id;
1009 The_Package : Package_Element;
1012 Current := In_Packages;
1013 while Current /= No_Package loop
1014 The_Package := In_Tree.Packages.Table (Current);
1015 exit when The_Package.Name /= No_Name
1016 and then The_Package.Name = Name;
1017 Current := The_Package.Next;
1024 (Variable_Name : Name_Id;
1025 In_Variables : Variable_Id;
1026 In_Tree : Project_Tree_Ref) return Variable_Value
1028 Current : Variable_Id;
1029 The_Variable : Variable;
1032 Current := In_Variables;
1033 while Current /= No_Variable loop
1035 In_Tree.Variable_Elements.Table (Current);
1037 if Variable_Name = The_Variable.Name then
1038 return The_Variable.Value;
1040 Current := The_Variable.Next;
1044 return Nil_Variable_Value;
1047 ----------------------------
1048 -- Write_Source_Info_File --
1049 ----------------------------
1051 procedure Write_Source_Info_File (Tree : Project_Tree_Ref) is
1052 Iter : Source_Iterator := For_Each_Source (Tree);
1053 Source : Prj.Source_Id;
1057 if Opt.Verbose_Mode then
1058 Write_Line ("Writing new source info file " &
1059 Tree.Source_Info_File_Name.all);
1062 Create (File, Tree.Source_Info_File_Name.all);
1064 if not Is_Valid (File) then
1065 Write_Line ("warning: unable to create source info file """ &
1066 Tree.Source_Info_File_Name.all & '"');
1071 Source := Element (Iter);
1072 exit when Source = No_Source;
1074 if not Source.Locally_Removed and then
1075 Source.Replaced_By = No_Source
1079 Put_Line (File, Get_Name_String (Source.Project.Name));
1083 Put_Line (File, Get_Name_String (Source.Language.Name));
1087 Put_Line (File, Source.Kind'Img);
1089 -- Display path name
1091 Put_Line (File, Get_Name_String (Source.Path.Display_Name));
1097 if Source.Path.Name /= Source.Path.Display_Name then
1099 Put_Line (File, Get_Name_String (Source.Path.Name));
1104 if Source.Unit /= No_Unit_Index then
1106 Put_Line (File, Get_Name_String (Source.Unit.Name));
1109 -- Multi-source index (I=)
1111 if Source.Index /= 0 then
1113 Put_Line (File, Source.Index'Img);
1116 -- Naming exception ("N=T");
1118 if Source.Naming_Exception then
1119 Put_Line (File, "N=T");
1122 -- Empty line to indicate end of info on this source
1124 Put_Line (File, "");
1131 end Write_Source_Info_File;
1139 Max_Length : Positive;
1140 Separator : Character)
1142 First : Positive := S'First;
1143 Last : Natural := S'Last;
1146 -- Nothing to do for empty strings
1148 if S'Length > 0 then
1150 -- Start on a new line if current line is already longer than
1153 if Positive (Column) >= Max_Length then
1157 -- If length of remainder is longer than Max_Length, we need to
1158 -- cut the remainder in several lines.
1160 while Positive (Column) + S'Last - First > Max_Length loop
1162 -- Try the maximum length possible
1164 Last := First + Max_Length - Positive (Column);
1166 -- Look for last Separator in the line
1168 while Last >= First and then S (Last) /= Separator loop
1172 -- If we do not find a separator, we output the maximum length
1175 if Last < First then
1176 Last := First + Max_Length - Positive (Column);
1179 Write_Line (S (First .. Last));
1181 -- Set the beginning of the new remainder
1186 -- What is left goes to the buffer, without EOL
1188 Write_Str (S (First .. S'Last));