File : a-textio.adb


     ------------------------------------------------------------------------------
     --                                                                          --
     --                         GNAT RUNTIME COMPONENTS                          --
     --                                                                          --
   5 --                          A D A . T E X T _ I O                           --
     --                                                                          --
     --                                 B o d y                                  --
     --                                                                          --
     --                            $Revision: 1.82 $
  10 --                                                                          --
     --          Copyright (C) 1992-2001 Free Software Foundation, Inc.          --
     --                                                                          --
     -- GNAT is free software;  you can  redistribute it  and/or modify it under --
     -- terms of the  GNU General Public License as published  by the Free Soft- --
  15 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
     -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
     -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
     -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
     -- for  more details.  You should have  received  a copy of the GNU General --
  20 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
     -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
     -- MA 02111-1307, USA.                                                      --
     --                                                                          --
     -- As a special exception,  if other files  instantiate  generics from this --
  25 -- unit, or you link  this unit with other files  to produce an executable, --
     -- this  unit  does not  by itself cause  the resulting  executable  to  be --
     -- covered  by the  GNU  General  Public  License.  This exception does not --
     -- however invalidate  any other reasons why  the executable file  might be --
     -- covered by the  GNU Public License.                                      --
  30 --                                                                          --
     -- GNAT was originally developed  by the GNAT team at  New York University. --
     -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
     --                                                                          --
     ------------------------------------------------------------------------------
  35 
     with Ada.Streams;          use Ada.Streams;
     with Interfaces.C_Streams; use Interfaces.C_Streams;
     with System;
     with System.File_IO;
  40 with Unchecked_Conversion;
     with Unchecked_Deallocation;
     
     pragma Elaborate_All (System.File_IO);
     --  Needed because of calls to Chain_File in package body elaboration
  45 
     package body Ada.Text_IO is
     
        package FIO renames System.File_IO;
     
  50    subtype AP is FCB.AFCB_Ptr;
     
        function To_FCB is new Unchecked_Conversion (File_Mode, FCB.File_Mode);
        function To_TIO is new Unchecked_Conversion (FCB.File_Mode, File_Mode);
        use type FCB.File_Mode;
  55 
        -------------------
        -- AFCB_Allocate --
        -------------------
     
  60    function AFCB_Allocate (Control_Block : Text_AFCB) return FCB.AFCB_Ptr is
           pragma Warnings (Off, Control_Block);
     
        begin
           return new Text_AFCB;
  65    end AFCB_Allocate;
     
        ----------------
        -- AFCB_Close --
        ----------------
  70 
        procedure AFCB_Close (File : access Text_AFCB) is
        begin
           --  If the file being closed is one of the current files, then close
           --  the corresponding current file. It is not clear that this action
  75       --  is required (RM A.10.3(23)) but it seems reasonable, and besides
           --  ACVC test CE3208A expects this behavior.
     
           if File_Type (File) = Current_In then
              Current_In := null;
  80       elsif File_Type (File) = Current_Out then
              Current_Out := null;
           elsif File_Type (File) = Current_Err then
              Current_Err := null;
           end if;
  85 
           Terminate_Line (File_Type (File));
        end AFCB_Close;
     
        ---------------
  90    -- AFCB_Free --
        ---------------
     
        procedure AFCB_Free (File : access Text_AFCB) is
           type FCB_Ptr is access all Text_AFCB;
  95       FT : FCB_Ptr := FCB_Ptr (File);
     
           procedure Free is new Unchecked_Deallocation (Text_AFCB, FCB_Ptr);
     
        begin
 100       Free (FT);
        end AFCB_Free;
     
        -----------
        -- Close --
 105    -----------
     
        procedure Close (File : in out File_Type) is
        begin
           FIO.Close (AP (File));
 110    end Close;
     
        ---------
        -- Col --
        ---------
 115 
        --  Note: we assume that it is impossible in practice for the column
        --  to exceed the value of Count'Last, i.e. no check is required for
        --  overflow raising layout error.
     
 120    function Col (File : in File_Type) return Positive_Count is
        begin
           FIO.Check_File_Open (AP (File));
           return File.Col;
        end Col;
 125 
        function Col return Positive_Count is
        begin
           return Col (Current_Out);
        end Col;
 130 
        ------------
        -- Create --
        ------------
     
 135    procedure Create
          (File : in out File_Type;
           Mode : in File_Mode := Out_File;
           Name : in String := "";
           Form : in String := "")
 140    is
           File_Control_Block : Text_AFCB;
     
        begin
           FIO.Open (File_Ptr  => AP (File),
 145                 Dummy_FCB => File_Control_Block,
                     Mode      => To_FCB (Mode),
                     Name      => Name,
                     Form      => Form,
                     Amethod   => 'T',
 150                 Creat     => True,
                     Text      => True);
     
           File.Self := File;
        end Create;
 155 
        -------------------
        -- Current_Error --
        -------------------
     
 160    function Current_Error return File_Type is
        begin
           return Current_Err;
        end Current_Error;
     
 165    function Current_Error return File_Access is
        begin
           return Current_Err.Self'Access;
        end Current_Error;
     
 170    -------------------
        -- Current_Input --
        -------------------
     
        function Current_Input return File_Type is
 175    begin
           return Current_In;
        end Current_Input;
     
        function Current_Input return File_Access is
 180    begin
           return Current_In.Self'Access;
        end Current_Input;
     
        --------------------
 185    -- Current_Output --
        --------------------
     
        function Current_Output return File_Type is
        begin
 190       return Current_Out;
        end Current_Output;
     
        function Current_Output return File_Access is
        begin
 195       return Current_Out.Self'Access;
        end Current_Output;
     
        ------------
        -- Delete --
 200    ------------
     
        procedure Delete (File : in out File_Type) is
        begin
           FIO.Delete (AP (File));
 205    end Delete;
     
        -----------------
        -- End_Of_File --
        -----------------
 210 
        function End_Of_File (File : in File_Type) return Boolean is
           ch  : int;
     
        begin
 215       FIO.Check_Read_Status (AP (File));
     
           if File.Before_LM then
     
              if File.Before_LM_PM then
 220             return Nextc (File) = EOF;
              end if;
     
           else
              ch := Getc (File);
 225 
              if ch = EOF then
                 return True;
     
              elsif ch /= LM then
 230             Ungetc (ch, File);
                 return False;
     
              else -- ch = LM
                 File.Before_LM := True;
 235          end if;
           end if;
     
           --  Here we are just past the line mark with Before_LM set so that we
           --  do not have to try to back up past the LM, thus avoiding the need
 240       --  to back up more than one character.
     
           ch := Getc (File);
     
           if ch = EOF then
 245          return True;
     
           elsif ch = PM and then File.Is_Regular_File then
              File.Before_LM_PM := True;
              return Nextc (File) = EOF;
 250 
           --  Here if neither EOF nor PM followed end of line
     
           else
              Ungetc (ch, File);
 255          return False;
           end if;
     
        end End_Of_File;
     
 260    function End_Of_File return Boolean is
        begin
           return End_Of_File (Current_In);
        end End_Of_File;
     
 265    -----------------
        -- End_Of_Line --
        -----------------
     
        function End_Of_Line (File : in File_Type) return Boolean is
 270       ch : int;
     
        begin
           FIO.Check_Read_Status (AP (File));
     
 275       if File.Before_LM then
              return True;
     
           else
              ch := Getc (File);
 280 
              if ch = EOF then
                 return True;
     
              else
 285             Ungetc (ch, File);
                 return (ch = LM);
              end if;
           end if;
        end End_Of_Line;
 290 
        function End_Of_Line return Boolean is
        begin
           return End_Of_Line (Current_In);
        end End_Of_Line;
 295 
        -----------------
        -- End_Of_Page --
        -----------------
     
 300    function End_Of_Page (File : in File_Type) return Boolean is
           ch  : int;
     
        begin
           FIO.Check_Read_Status (AP (File));
 305 
           if not File.Is_Regular_File then
              return False;
     
           elsif File.Before_LM then
 310          if File.Before_LM_PM then
                 return True;
              end if;
     
           else
 315          ch := Getc (File);
     
              if ch = EOF then
                 return True;
     
 320          elsif ch /= LM then
                 Ungetc (ch, File);
                 return False;
     
              else -- ch = LM
 325             File.Before_LM := True;
              end if;
           end if;
     
           --  Here we are just past the line mark with Before_LM set so that we
 330       --  do not have to try to back up past the LM, thus avoiding the need
           --  to back up more than one character.
     
           ch := Nextc (File);
     
 335       return ch = PM or else ch = EOF;
        end End_Of_Page;
     
        function End_Of_Page return Boolean is
        begin
 340       return End_Of_Page (Current_In);
        end End_Of_Page;
     
        -----------
        -- Flush --
 345    -----------
     
        procedure Flush (File : in File_Type) is
        begin
           FIO.Flush (AP (File));
 350    end Flush;
     
        procedure Flush is
        begin
           Flush (Current_Out);
 355    end Flush;
     
        ----------
        -- Form --
        ----------
 360 
        function Form (File : in File_Type) return String is
        begin
           return FIO.Form (AP (File));
        end Form;
 365 
        ---------
        -- Get --
        ---------
     
 370    procedure Get
          (File : in File_Type;
           Item : out Character)
        is
           ch : int;
 375 
        begin
           FIO.Check_Read_Status (AP (File));
     
           if File.Before_LM then
 380          File.Before_LM := False;
              File.Col := 1;
     
              if File.Before_LM_PM then
                 File.Line := 1;
 385             File.Page := File.Page + 1;
                 File.Before_LM_PM := False;
              else
                 File.Line := File.Line + 1;
              end if;
 390       end if;
     
           loop
              ch := Getc (File);
     
 395          if ch = EOF then
                 raise End_Error;
     
              elsif ch = LM then
                 File.Line := File.Line + 1;
 400             File.Col := 1;
     
              elsif ch = PM and then File.Is_Regular_File then
                 File.Page := File.Page + 1;
                 File.Line := 1;
 405 
              else
                 Item := Character'Val (ch);
                 File.Col := File.Col + 1;
                 return;
 410          end if;
           end loop;
        end Get;
     
        procedure Get (Item : out Character) is
 415    begin
           Get (Current_In, Item);
        end Get;
     
        procedure Get
 420      (File : in File_Type;
           Item : out String)
        is
           ch : int;
           J  : Natural;
 425 
        begin
           FIO.Check_Read_Status (AP (File));
     
           if File.Before_LM then
 430          File.Before_LM := False;
              File.Before_LM_PM := False;
              File.Col := 1;
     
              if File.Before_LM_PM then
 435             File.Line := 1;
                 File.Page := File.Page + 1;
                 File.Before_LM_PM := False;
     
              else
 440             File.Line := File.Line + 1;
              end if;
           end if;
     
           J := Item'First;
 445       while J <= Item'Last loop
              ch := Getc (File);
     
              if ch = EOF then
                 raise End_Error;
 450 
              elsif ch = LM then
                 File.Line := File.Line + 1;
                 File.Col := 1;
     
 455          elsif ch = PM and then File.Is_Regular_File then
                 File.Page := File.Page + 1;
                 File.Line := 1;
     
              else
 460             Item (J) := Character'Val (ch);
                 J := J + 1;
                 File.Col := File.Col + 1;
              end if;
           end loop;
 465    end Get;
     
        procedure Get (Item : out String) is
        begin
           Get (Current_In, Item);
 470    end Get;
     
        -------------------
        -- Get_Immediate --
        -------------------
 475 
        --  More work required here ???
     
        procedure Get_Immediate
          (File : in File_Type;
 480       Item : out Character)
        is
           ch          : int;
           end_of_file : int;
     
 485       procedure getc_immediate
             (stream : FILEs; ch : out int; end_of_file : out int);
           pragma Import (C, getc_immediate, "getc_immediate");
     
        begin
 490       FIO.Check_Read_Status (AP (File));
     
           if File.Before_LM then
              File.Before_LM := False;
              File.Before_LM_PM := False;
 495          ch := LM;
     
           else
              getc_immediate (File.Stream, ch, end_of_file);
     
 500          if ferror (File.Stream) /= 0 then
                 raise Device_Error;
              elsif end_of_file /= 0 then
                 raise End_Error;
              end if;
 505       end if;
     
           Item := Character'Val (ch);
     
        end Get_Immediate;
 510 
        procedure Get_Immediate
          (Item : out Character)
        is
        begin
 515       Get_Immediate (Current_In, Item);
        end Get_Immediate;
     
        procedure Get_Immediate
          (File      : in File_Type;
 520       Item      : out Character;
           Available : out Boolean)
        is
           ch          : int;
           end_of_file : int;
 525       avail       : int;
     
           procedure getc_immediate_nowait
             (stream      : FILEs;
              ch          : out int;
 530          end_of_file : out int;
              avail       : out int);
           pragma Import (C, getc_immediate_nowait, "getc_immediate_nowait");
     
        begin
 535       FIO.Check_Read_Status (AP (File));
     
           --  If we are logically before an end of line, but physically after it,
           --  then we just return the end of line character, no I/O is necessary.
     
 540       if File.Before_LM then
              File.Before_LM := False;
              File.Before_LM_PM := False;
     
              Available := True;
 545          Item := Character'Val (LM);
     
           --  Normal case where a read operation is required
     
           else
 550          getc_immediate_nowait (File.Stream, ch, end_of_file, avail);
     
              if ferror (File.Stream) /= 0 then
                 raise Device_Error;
     
 555          elsif end_of_file /= 0 then
                 raise End_Error;
     
              elsif avail = 0 then
                 Available := False;
 560             Item := ASCII.NUL;
     
              else
                 Available := True;
                 Item := Character'Val (ch);
 565          end if;
           end if;
     
        end Get_Immediate;
     
 570    procedure Get_Immediate
          (Item      : out Character;
           Available : out Boolean)
        is
        begin
 575       Get_Immediate (Current_In, Item, Available);
        end Get_Immediate;
     
        --------------
        -- Get_Line --
 580    --------------
     
        procedure Get_Line
          (File : in File_Type;
           Item : out String;
 585       Last : out Natural)
        is
           ch : int;
     
        begin
 590       FIO.Check_Read_Status (AP (File));
           Last := Item'First - 1;
     
           --  Immediate exit for null string, this is a case in which we do not
           --  need to test for end of file and we do not skip a line mark under
 595       --  any circumstances.
     
           if Last >= Item'Last then
              return;
           end if;
 600 
           --  Here we have at least one character, if we are immediately before
           --  a line mark, then we will just skip past it storing no characters.
     
           if File.Before_LM then
 605          File.Before_LM := False;
              File.Before_LM_PM := False;
     
           --  Otherwise we need to read some characters
     
 610       else
              ch := Getc (File);
     
              --  If we are at the end of file now, it means we are trying to
              --  skip a file terminator and we raise End_Error (RM A.10.7(20))
 615 
              if ch = EOF then
                 raise End_Error;
              end if;
     
 620          --  Loop through characters. Don't bother if we hit a page mark,
              --  since in normal files, page marks can only follow line marks
              --  in any case and we only promise to treat the page nonsense
              --  correctly in the absense of such rogue page marks.
     
 625          loop
                 --  Exit the loop if read is terminated by encountering line mark
     
                 exit when ch = LM;
     
 630             --  Otherwise store the character, note that we know that ch is
                 --  something other than LM or EOF. It could possibly be a page
                 --  mark if there is a stray page mark in the middle of a line,
                 --  but this is not an official page mark in any case, since
                 --  official page marks can only follow a line mark. The whole
 635             --  page business is pretty much nonsense anyway, so we do not
                 --  want to waste time trying to make sense out of non-standard
                 --  page marks in the file! This means that the behavior of
                 --  Get_Line is different from repeated Get of a character, but
                 --  that's too bad. We only promise that page numbers etc make
 640             --  sense if the file is formatted in a standard manner.
     
                 --  Note: we do not adjust the column number because it is quicker
                 --  to adjust it once at the end of the operation than incrementing
                 --  it each time around the loop.
 645 
                 Last := Last + 1;
                 Item (Last) := Character'Val (ch);
     
                 --  All done if the string is full, this is the case in which
 650             --  we do not skip the following line mark. We need to adjust
                 --  the column number in this case.
     
                 if Last = Item'Last then
                    File.Col := File.Col + Count (Item'Length);
 655                return;
                 end if;
     
                 --  Otherwise read next character. We also exit from the loop if
                 --  we read an end of file. This is the case where the last line
 660             --  is not terminated with a line mark, and we consider that there
                 --  is an implied line mark in this case (this is a non-standard
                 --  file, but it is nice to treat it reasonably).
     
                 ch := Getc (File);
 665             exit when ch = EOF;
              end loop;
           end if;
     
           --  We have skipped past, but not stored, a line mark. Skip following
 670       --  page mark if one follows, but do not do this for a non-regular
           --  file (since otherwise we get annoying wait for an extra character)
     
           File.Line := File.Line + 1;
           File.Col := 1;
 675 
           if File.Before_LM_PM then
              File.Line := 1;
              File.Before_LM_PM := False;
              File.Page := File.Page + 1;
 680 
           elsif File.Is_Regular_File then
              ch := Getc (File);
     
              if ch = PM and then File.Is_Regular_File then
 685             File.Line := 1;
                 File.Page := File.Page + 1;
              else
                 Ungetc (ch, File);
              end if;
 690       end if;
        end Get_Line;
     
        procedure Get_Line
          (Item : out String;
 695       Last : out Natural)
        is
        begin
           Get_Line (Current_In, Item, Last);
        end Get_Line;
 700 
        ----------
        -- Getc --
        ----------
     
 705    function Getc (File : File_Type) return int is
           ch : int;
     
        begin
           ch := fgetc (File.Stream);
 710 
           if ch = EOF and then ferror (File.Stream) /= 0 then
              raise Device_Error;
           else
              return ch;
 715       end if;
        end Getc;
     
        -------------
        -- Is_Open --
 720    -------------
     
        function Is_Open (File : in File_Type) return Boolean is
        begin
           return FIO.Is_Open (AP (File));
 725    end Is_Open;
     
        ----------
        -- Line --
        ----------
 730 
        --  Note: we assume that it is impossible in practice for the line
        --  to exceed the value of Count'Last, i.e. no check is required for
        --  overflow raising layout error.
     
 735    function Line (File : in File_Type) return Positive_Count is
        begin
           FIO.Check_File_Open (AP (File));
           return File.Line;
        end Line;
 740 
        function Line return Positive_Count is
        begin
           return Line (Current_Out);
        end Line;
 745 
        -----------------
        -- Line_Length --
        -----------------
     
 750    function Line_Length (File : in File_Type) return Count is
        begin
           FIO.Check_Write_Status (AP (File));
           return File.Line_Length;
        end Line_Length;
 755 
        function Line_Length return Count is
        begin
           return Line_Length (Current_Out);
        end Line_Length;
 760 
        ----------------
        -- Look_Ahead --
        ----------------
     
 765    procedure Look_Ahead
          (File        : in File_Type;
           Item        : out Character;
           End_Of_Line : out Boolean)
        is
 770       ch : int;
     
        begin
           FIO.Check_Read_Status (AP (File));
     
 775       if File.Before_LM then
              End_Of_Line := True;
              Item := ASCII.NUL;
     
           else
 780          ch := Nextc (File);
     
              if ch = LM
                or else ch = EOF
                or else (ch = PM and then File.Is_Regular_File)
 785          then
                 End_Of_Line := True;
                 Item := ASCII.NUL;
              else
                 End_Of_Line := False;
 790             Item := Character'Val (ch);
              end if;
           end if;
        end Look_Ahead;
     
 795    procedure Look_Ahead
          (Item        : out Character;
           End_Of_Line : out Boolean)
        is
        begin
 800       Look_Ahead (Current_In, Item, End_Of_Line);
        end Look_Ahead;
     
        ----------
        -- Mode --
 805    ----------
     
        function Mode (File : in File_Type) return File_Mode is
        begin
           return To_TIO (FIO.Mode (AP (File)));
 810    end Mode;
     
        ----------
        -- Name --
        ----------
 815 
        function Name (File : in File_Type) return String is
        begin
           return FIO.Name (AP (File));
        end Name;
 820 
        --------------
        -- New_Line --
        --------------
     
 825    procedure New_Line
          (File    : in File_Type;
           Spacing : in Positive_Count := 1)
        is
        begin
 830       --  Raise Constraint_Error if out of range value. The reason for this
           --  explicit test is that we don't want junk values around, even if
           --  checks are off in the caller.
     
           if Spacing not in Positive_Count then
 835          raise Constraint_Error;
           end if;
     
           FIO.Check_Write_Status (AP (File));
     
 840       for K in 1 .. Spacing loop
              Putc (LM, File);
              File.Line := File.Line + 1;
     
              if File.Page_Length /= 0
 845            and then File.Line > File.Page_Length
              then
                 Putc (PM, File);
                 File.Line := 1;
                 File.Page := File.Page + 1;
 850          end if;
           end loop;
     
           File.Col := 1;
        end New_Line;
 855 
        procedure New_Line (Spacing : in Positive_Count := 1) is
        begin
           New_Line (Current_Out, Spacing);
        end New_Line;
 860 
        --------------
        -- New_Page --
        --------------
     
 865    procedure New_Page (File : in File_Type) is
        begin
           FIO.Check_Write_Status (AP (File));
     
           if File.Col /= 1 or else File.Line = 1 then
 870          Putc (LM, File);
           end if;
     
           Putc (PM, File);
           File.Page := File.Page + 1;
 875       File.Line := 1;
           File.Col := 1;
        end New_Page;
     
        procedure New_Page is
 880    begin
           New_Page (Current_Out);
        end New_Page;
     
        -----------
 885    -- Nextc --
        -----------
     
        function Nextc (File : File_Type) return int is
           ch : int;
 890 
        begin
           ch := fgetc (File.Stream);
     
           if ch = EOF then
 895          if ferror (File.Stream) /= 0 then
                 raise Device_Error;
              end if;
     
           else
 900          if ungetc (ch, File.Stream) = EOF then
                 raise Device_Error;
              end if;
           end if;
     
 905       return ch;
        end Nextc;
     
        ----------
        -- Open --
 910    ----------
     
        procedure Open
          (File : in out File_Type;
           Mode : in File_Mode;
 915       Name : in String;
           Form : in String := "")
        is
           File_Control_Block : Text_AFCB;
     
 920    begin
           FIO.Open (File_Ptr  => AP (File),
                     Dummy_FCB => File_Control_Block,
                     Mode      => To_FCB (Mode),
                     Name      => Name,
 925                 Form      => Form,
                     Amethod   => 'T',
                     Creat     => False,
                     Text      => True);
     
 930       File.Self := File;
        end Open;
     
        ----------
        -- Page --
 935    ----------
     
        --  Note: we assume that it is impossible in practice for the page
        --  to exceed the value of Count'Last, i.e. no check is required for
        --  overflow raising layout error.
 940 
        function Page (File : in File_Type) return Positive_Count is
        begin
           FIO.Check_File_Open (AP (File));
           return File.Page;
 945    end Page;
     
        function Page return Positive_Count is
        begin
           return Page (Current_Out);
 950    end Page;
     
        -----------------
        -- Page_Length --
        -----------------
 955 
        function Page_Length (File : in File_Type) return Count is
        begin
           FIO.Check_Write_Status (AP (File));
           return File.Page_Length;
 960    end Page_Length;
     
        function Page_Length return Count is
        begin
           return Page_Length (Current_Out);
 965    end Page_Length;
     
        ---------
        -- Put --
        ---------
 970 
        procedure Put
          (File : in File_Type;
           Item : in Character)
        is
 975    begin
           FIO.Check_Write_Status (AP (File));
     
           if File.Line_Length /= 0 and then File.Col > File.Line_Length then
              New_Line (File);
 980       end if;
     
           if fputc (Character'Pos (Item), File.Stream) = EOF then
              raise Device_Error;
           end if;
 985 
           File.Col := File.Col + 1;
        end Put;
     
        procedure Put (Item : in Character) is
 990    begin
           FIO.Check_Write_Status (AP (Current_Out));
     
           if Current_Out.Line_Length /= 0
             and then Current_Out.Col > Current_Out.Line_Length
 995       then
              New_Line (Current_Out);
           end if;
     
           if fputc (Character'Pos (Item), Current_Out.Stream) = EOF then
1000          raise Device_Error;
           end if;
     
           Current_Out.Col := Current_Out.Col + 1;
        end Put;
1005 
        ---------
        -- Put --
        ---------
     
1010    procedure Put
          (File : in File_Type;
           Item : in String)
        is
        begin
1015       FIO.Check_Write_Status (AP (File));
     
           if Item'Length > 0 then
     
              --  If we have bounded lines, then do things character by
1020          --  character (this seems a rare case anyway!)
     
              if File.Line_Length /= 0 then
                 for J in Item'Range loop
                    Put (File, Item (J));
1025             end loop;
     
              --  Otherwise we can output the entire string at once. Note that if
              --  there are LF or FF characters in the string, we do not bother to
              --  count them as line or page terminators.
1030 
              else
                 FIO.Write_Buf (AP (File), Item'Address, Item'Length);
                 File.Col := File.Col + Item'Length;
              end if;
1035       end if;
        end Put;
     
        procedure Put (Item : in String) is
        begin
1040       Put (Current_Out, Item);
        end Put;
     
        --------------
        -- Put_Line --
1045    --------------
     
        procedure Put_Line
          (File : in File_Type;
           Item : in String)
1050    is
        begin
           FIO.Check_Write_Status (AP (File));
     
           --  If we have bounded lines, then just do a put and a new line. In
1055       --  this case we will end up doing things character by character in
           --  any case, and it is a rare situation.
     
           if File.Line_Length /= 0 then
              Put (File, Item);
1060          New_Line (File);
              return;
           end if;
     
           --  We setup a single string that has the necessary terminators and
1065       --  then write it with a single call. The reason for doing this is
           --  that it gives better behavior for the use of Put_Line in multi-
           --  tasking programs, since often the OS will treat the entire put
           --  operation as an atomic operation.
     
1070       declare
              Ilen   : constant Natural := Item'Length;
              Buffer : String (1 .. Ilen + 2);
              Plen   : size_t;
     
1075       begin
              Buffer (1 .. Ilen) := Item;
              Buffer (Ilen + 1) := Character'Val (LM);
     
              if File.Page_Length /= 0
1080            and then File.Line > File.Page_Length
              then
                 Buffer (Ilen + 2) := Character'Val (PM);
                 Plen := size_t (Ilen) + 2;
                 File.Line := 1;
1085             File.Page := File.Page + 1;
     
              else
                 Plen := size_t (Ilen) + 1;
                 File.Line := File.Line + 1;
1090          end if;
     
              FIO.Write_Buf (AP (File), Buffer'Address, Plen);
     
              File.Col := 1;
1095       end;
        end Put_Line;
     
        procedure Put_Line (Item : in String) is
        begin
1100       Put_Line (Current_Out, Item);
        end Put_Line;
     
        ----------
        -- Putc --
1105    ----------
     
        procedure Putc (ch : int; File : File_Type) is
        begin
           if fputc (ch, File.Stream) = EOF then
1110          raise Device_Error;
           end if;
        end Putc;
     
        ----------
1115    -- Read --
        ----------
     
        --  This is the primitive Stream Read routine, used when a Text_IO file
        --  is treated directly as a stream using Text_IO.Streams.Stream.
1120 
        procedure Read
          (File : in out Text_AFCB;
           Item : out Stream_Element_Array;
           Last : out Stream_Element_Offset)
1125    is
           ch : int;
     
        begin
           if File.Mode /= FCB.In_File then
1130          raise Mode_Error;
           end if;
     
           --  Deal with case where our logical and physical position do not match
           --  because of being after an LM or LM-PM sequence when in fact we are
1135       --  logically positioned before it.
     
           if File.Before_LM then
     
              --  If we are before a PM, then it is possible for a stream read
1140          --  to leave us after the LM and before the PM, which is a bit
              --  odd. The easiest way to deal with this is to unget the PM,
              --  so we are indeed positioned between the characters. This way
              --  further stream read operations will work correctly, and the
              --  effect on text processing is a little weird, but what can
1145          --  be expected if stream and text input are mixed this way?
     
              if File.Before_LM_PM then
                 ch := ungetc (PM, File.Stream);
                 File.Before_LM_PM := False;
1150          end if;
     
              File.Before_LM := False;
     
              Item (Item'First) := Stream_Element (Character'Pos (ASCII.LF));
1155 
              if Item'Length = 1 then
                 Last := Item'Last;
     
              else
1160             Last :=
                   Item'First +
                     Stream_Element_Offset
                       (fread (buffer => Item'Address,
                               index  => size_t (Item'First + 1),
1165                           size   => 1,
                               count  => Item'Length - 1,
                               stream => File.Stream));
              end if;
     
1170          return;
           end if;
     
           --  Now we do the read. Since this is a text file, it is normally in
           --  text mode, but stream data must be read in binary mode, so we
1175       --  temporarily set binary mode for the read, resetting it after.
           --  These calls have no effect in a system (like Unix) where there is
           --  no distinction between text and binary files.
     
           set_binary_mode (fileno (File.Stream));
1180 
           Last :=
             Item'First +
               Stream_Element_Offset
                 (fread (Item'Address, 1, Item'Length, File.Stream)) - 1;
1185 
           if Last < Item'Last then
              if ferror (File.Stream) /= 0 then
                 raise Device_Error;
              end if;
1190       end if;
     
           set_text_mode (fileno (File.Stream));
        end Read;
     
1195    -----------
        -- Reset --
        -----------
     
        procedure Reset
1200      (File : in out File_Type;
           Mode : in File_Mode)
        is
        begin
           --  Don't allow change of mode for current file (RM A.10.2(5))
1205 
           if (File = Current_In or else
               File = Current_Out  or else
               File = Current_Error)
             and then To_FCB (Mode) /= File.Mode
1210       then
              raise Mode_Error;
           end if;
     
           Terminate_Line (File);
1215       FIO.Reset (AP (File), To_FCB (Mode));
           File.Page := 1;
           File.Line := 1;
           File.Col  := 1;
           File.Line_Length := 0;
1220       File.Page_Length := 0;
           File.Before_LM := False;
           File.Before_LM_PM := False;
        end Reset;
     
1225    procedure Reset (File : in out File_Type) is
        begin
           Terminate_Line (File);
           FIO.Reset (AP (File));
           File.Page := 1;
1230       File.Line := 1;
           File.Col  := 1;
           File.Line_Length := 0;
           File.Page_Length := 0;
           File.Before_LM := False;
1235       File.Before_LM_PM := False;
        end Reset;
     
        -------------
        -- Set_Col --
1240    -------------
     
        procedure Set_Col
          (File : in File_Type;
           To   : in Positive_Count)
1245    is
           ch : int;
     
        begin
           --  Raise Constraint_Error if out of range value. The reason for this
1250       --  explicit test is that we don't want junk values around, even if
           --  checks are off in the caller.
     
           if To not in Positive_Count then
              raise Constraint_Error;
1255       end if;
     
           FIO.Check_File_Open (AP (File));
     
           if To = File.Col then
1260          return;
           end if;
     
           if Mode (File) >= Out_File then
              if File.Line_Length /= 0 and then To > File.Line_Length then
1265             raise Layout_Error;
              end if;
     
              if To < File.Col then
                 New_Line (File);
1270          end if;
     
              while File.Col < To loop
                 Put (File, ' ');
              end loop;
1275 
           else
              loop
                 ch := Getc (File);
     
1280             if ch = EOF then
                    raise End_Error;
     
                 elsif ch = LM then
                    File.Line := File.Line + 1;
1285                File.Col := 1;
     
                 elsif ch = PM and then File.Is_Regular_File then
                    File.Page := File.Page + 1;
                    File.Line := 1;
1290                File.Col := 1;
     
                 elsif To = File.Col then
                    Ungetc (ch, File);
                    return;
1295 
                 else
                    File.Col := File.Col + 1;
                 end if;
              end loop;
1300       end if;
        end Set_Col;
     
        procedure Set_Col (To : in Positive_Count) is
        begin
1305       Set_Col (Current_Out, To);
        end Set_Col;
     
        ---------------
        -- Set_Error --
1310    ---------------
     
        procedure Set_Error (File : in File_Type) is
        begin
           FIO.Check_Write_Status (AP (File));
1315       Current_Err := File;
        end Set_Error;
     
        ---------------
        -- Set_Input --
1320    ---------------
     
        procedure Set_Input (File : in File_Type) is
        begin
           FIO.Check_Read_Status (AP (File));
1325       Current_In := File;
        end Set_Input;
     
        --------------
        -- Set_Line --
1330    --------------
     
        procedure Set_Line
          (File : in File_Type;
           To   : in Positive_Count)
1335    is
        begin
           --  Raise Constraint_Error if out of range value. The reason for this
           --  explicit test is that we don't want junk values around, even if
           --  checks are off in the caller.
1340 
           if To not in Positive_Count then
              raise Constraint_Error;
           end if;
     
1345       FIO.Check_File_Open (AP (File));
     
           if To = File.Line then
              return;
           end if;
1350 
           if Mode (File) >= Out_File then
              if File.Page_Length /= 0 and then To > File.Page_Length then
                 raise Layout_Error;
              end if;
1355 
              if To < File.Line then
                 New_Page (File);
              end if;
     
1360          while File.Line < To loop
                 New_Line (File);
              end loop;
     
           else
1365          while To /= File.Line loop
                 Skip_Line (File);
              end loop;
           end if;
        end Set_Line;
1370 
        procedure Set_Line (To : in Positive_Count) is
        begin
           Set_Line (Current_Out, To);
        end Set_Line;
1375 
        ---------------------
        -- Set_Line_Length --
        ---------------------
     
1380    procedure Set_Line_Length (File : in File_Type; To : in Count) is
        begin
           --  Raise Constraint_Error if out of range value. The reason for this
           --  explicit test is that we don't want junk values around, even if
           --  checks are off in the caller.
1385 
           if To not in Count then
              raise Constraint_Error;
           end if;
     
1390       FIO.Check_Write_Status (AP (File));
           File.Line_Length := To;
        end Set_Line_Length;
     
        procedure Set_Line_Length (To : in Count) is
1395    begin
           Set_Line_Length (Current_Out, To);
        end Set_Line_Length;
     
        ----------------
1400    -- Set_Output --
        ----------------
     
        procedure Set_Output (File : in File_Type) is
        begin
1405       FIO.Check_Write_Status (AP (File));
           Current_Out := File;
        end Set_Output;
     
        ---------------------
1410    -- Set_Page_Length --
        ---------------------
     
        procedure Set_Page_Length (File : in File_Type; To : in Count) is
        begin
1415       --  Raise Constraint_Error if out of range value. The reason for this
           --  explicit test is that we don't want junk values around, even if
           --  checks are off in the caller.
     
           if To not in Count then
1420          raise Constraint_Error;
           end if;
     
           FIO.Check_Write_Status (AP (File));
           File.Page_Length := To;
1425    end Set_Page_Length;
     
        procedure Set_Page_Length (To : in Count) is
        begin
           Set_Page_Length (Current_Out, To);
1430    end Set_Page_Length;
     
        ---------------
        -- Skip_Line --
        ---------------
1435 
        procedure Skip_Line
          (File    : in File_Type;
           Spacing : in Positive_Count := 1)
        is
1440       ch : int;
     
        begin
           --  Raise Constraint_Error if out of range value. The reason for this
           --  explicit test is that we don't want junk values around, even if
1445       --  checks are off in the caller.
     
           if Spacing not in Positive_Count then
              raise Constraint_Error;
           end if;
1450 
           FIO.Check_Read_Status (AP (File));
     
           for L in 1 .. Spacing loop
              if File.Before_LM then
1455             File.Before_LM := False;
                 File.Before_LM_PM := False;
     
              else
                 ch := Getc (File);
1460 
                 --  If at end of file now, then immediately raise End_Error. Note
                 --  that we can never be positioned between a line mark and a page
                 --  mark, so if we are at the end of file, we cannot logically be
                 --  before the implicit page mark that is at the end of the file.
1465 
                 --  For the same reason, we do not need an explicit check for a
                 --  page mark. If there is a FF in the middle of a line, the file
                 --  is not in canonical format and we do not care about the page
                 --  numbers for files other than ones in canonical format.
1470 
                 if ch = EOF then
                    raise End_Error;
                 end if;
     
1475             --  If not at end of file, then loop till we get to an LM or EOF.
                 --  The latter case happens only in non-canonical files where the
                 --  last line is not terminated by LM, but we don't want to blow
                 --  up for such files, so we assume an implicit LM in this case.
     
1480             loop
                    exit when ch = LM or ch = EOF;
                    ch := Getc (File);
                 end loop;
              end if;
1485 
              --  We have got past a line mark, now, for a regular file only,
              --  see if a page mark immediately follows this line mark and
              --  if so, skip past the page mark as well. We do not do this
              --  for non-regular files, since it would cause an undesirable
1490          --  wait for an additional character.
     
              File.Col := 1;
              File.Line := File.Line + 1;
     
1495          if File.Before_LM_PM then
                 File.Page := File.Page + 1;
                 File.Line := 1;
                 File.Before_LM_PM := False;
     
1500          elsif File.Is_Regular_File then
                 ch := Getc (File);
     
                 --  Page mark can be explicit, or implied at the end of the file
     
1505             if (ch = PM or else ch = EOF)
                   and then File.Is_Regular_File
                 then
                    File.Page := File.Page + 1;
                    File.Line := 1;
1510             else
                    Ungetc (ch, File);
                 end if;
              end if;
     
1515       end loop;
        end Skip_Line;
     
        procedure Skip_Line (Spacing : in Positive_Count := 1) is
        begin
1520       Skip_Line (Current_In, Spacing);
        end Skip_Line;
     
        ---------------
        -- Skip_Page --
1525    ---------------
     
        procedure Skip_Page (File : in File_Type) is
           ch : int;
     
1530    begin
           FIO.Check_Read_Status (AP (File));
     
           --  If at page mark already, just skip it
     
1535       if File.Before_LM_PM then
              File.Before_LM := False;
              File.Before_LM_PM := False;
              File.Page := File.Page + 1;
              File.Line := 1;
1540          File.Col  := 1;
              return;
           end if;
     
           --  This is a bit tricky, if we are logically before an LM then
1545       --  it is not an error if we are at an end of file now, since we
           --  are not really at it.
     
           if File.Before_LM then
              File.Before_LM := False;
1550          File.Before_LM_PM := False;
              ch := Getc (File);
     
           --  Otherwise we do raise End_Error if we are at the end of file now
     
1555       else
              ch := Getc (File);
     
              if ch = EOF then
                 raise End_Error;
1560          end if;
           end if;
     
           --  Now we can just rumble along to the next page mark, or to the
           --  end of file, if that comes first. The latter case happens when
1565       --  the page mark is implied at the end of file.
     
           loop
              exit when ch = EOF
                or else (ch = PM and then File.Is_Regular_File);
1570          ch := Getc (File);
           end loop;
     
           File.Page := File.Page + 1;
           File.Line := 1;
1575       File.Col  := 1;
        end Skip_Page;
     
        procedure Skip_Page is
        begin
1580       Skip_Page (Current_In);
        end Skip_Page;
     
        --------------------
        -- Standard_Error --
1585    --------------------
     
        function Standard_Error return File_Type is
        begin
           return Standard_Err;
1590    end Standard_Error;
     
        function Standard_Error return File_Access is
        begin
           return Standard_Err'Access;
1595    end Standard_Error;
     
        --------------------
        -- Standard_Input --
        --------------------
1600 
        function Standard_Input return File_Type is
        begin
           return Standard_In;
        end Standard_Input;
1605 
        function Standard_Input return File_Access is
        begin
           return Standard_In'Access;
        end Standard_Input;
1610 
        ---------------------
        -- Standard_Output --
        ---------------------
     
1615    function Standard_Output return File_Type is
        begin
           return Standard_Out;
        end Standard_Output;
     
1620    function Standard_Output return File_Access is
        begin
           return Standard_Out'Access;
        end Standard_Output;
     
1625    --------------------
        -- Terminate_Line --
        --------------------
     
        procedure Terminate_Line (File : File_Type) is
1630    begin
           FIO.Check_File_Open (AP (File));
     
           --  For file other than In_File, test for needing to terminate last line
     
1635       if Mode (File) /= In_File then
     
              --  If not at start of line definition need new line
     
              if File.Col /= 1 then
1640             New_Line (File);
     
              --  For files other than standard error and standard output, we
              --  make sure that an empty file has a single line feed, so that
              --  it is properly formatted. We avoid this for the standard files
1645          --  because it is too much of a nuisance to have these odd line
              --  feeds when nothing has been written to the file.
     
              elsif (File /= Standard_Err and then File /= Standard_Out)
                and then (File.Line = 1 and then File.Page = 1)
1650          then
                 New_Line (File);
              end if;
           end if;
        end Terminate_Line;
1655 
        ------------
        -- Ungetc --
        ------------
     
1660    procedure Ungetc (ch : int; File : File_Type) is
        begin
           if ch /= EOF then
              if ungetc (ch, File.Stream) = EOF then
                 raise Device_Error;
1665          end if;
           end if;
        end Ungetc;
     
        -----------
1670    -- Write --
        -----------
     
        --  This is the primitive Stream Write routine, used when a Text_IO file
        --  is treated directly as a stream using Text_IO.Streams.Stream.
1675 
        procedure Write
          (File : in out Text_AFCB;
           Item : in Stream_Element_Array)
        is
1680 
           function Has_Translated_Characters return Boolean;
           --  return True if Item array contains a character which will be
           --  translated under the text file mode. There is only one such
           --  character under DOS based systems which is character 10.
1685 
           text_translation_required : Boolean;
           pragma Import (C, text_translation_required,
                          "__gnat_text_translation_required");
     
1690       Siz : constant size_t := Item'Length;
     
           function Has_Translated_Characters return Boolean is
           begin
              for K in Item'Range loop
1695             if Item (K) = 10 then
                    return True;
                 end if;
              end loop;
              return False;
1700       end Has_Translated_Characters;
     
           Needs_Binary_Write : constant Boolean :=
             text_translation_required and then Has_Translated_Characters;
     
1705    begin
           if File.Mode = FCB.In_File then
              raise Mode_Error;
           end if;
     
1710       --  Now we do the write. Since this is a text file, it is normally in
           --  text mode, but stream data must be written in binary mode, so we
           --  temporarily set binary mode for the write, resetting it after. This
           --  is done only if needed (i.e. there is some characters in Item which
           --  needs to be written using the binary mode).
1715       --  These calls have no effect in a system (like Unix) where there is
           --  no distinction between text and binary files.
     
           --  Since the character translation is done at the time the buffer is
           --  written (this is true under Windows) we first flush current buffer
1720       --  with text mode if needed.
     
           if Needs_Binary_Write then
     
              if fflush (File.Stream) = -1 then
1725             raise Device_Error;
              end if;
     
              set_binary_mode (fileno (File.Stream));
           end if;
1730 
           if fwrite (Item'Address, 1, Siz, File.Stream) /= Siz then
              raise Device_Error;
           end if;
     
1735       --  At this point we need to flush the buffer using the binary mode then
           --  we reset to text mode.
     
           if Needs_Binary_Write then
     
1740          if fflush (File.Stream) = -1 then
                 raise Device_Error;
              end if;
     
              set_text_mode (fileno (File.Stream));
1745       end if;
        end Write;
     
        --  Use "preallocated" strings to avoid calling "new" during the
        --  elaboration of the run time. This is needed in the tasking case to
1750    --  avoid calling Task_Lock too early. A filename is expected to end with a
        --  null character in the runtime, here the null characters are added just
        --  to have a correct filename length.
     
        Err_Name : aliased String := "*stderr" & ASCII.Nul;
1755    In_Name  : aliased String := "*stdin" & ASCII.Nul;
        Out_Name : aliased String := "*stdout" & ASCII.Nul;
     begin
        -------------------------------
        -- Initialize Standard Files --
1760    -------------------------------
     
        --  Note: the names in these files are bogus, and probably it would be
        --  better for these files to have no names, but the ACVC test insist!
        --  We use names that are bound to fail in open etc.
1765 
        Standard_Err.Stream            := stderr;
        Standard_Err.Name              := Err_Name'Access;
        Standard_Err.Form              := Null_Str'Unrestricted_Access;
        Standard_Err.Mode              := FCB.Out_File;
1770    Standard_Err.Is_Regular_File   := is_regular_file (fileno (stderr)) /= 0;
        Standard_Err.Is_Temporary_File := False;
        Standard_Err.Is_System_File    := True;
        Standard_Err.Is_Text_File      := True;
        Standard_Err.Access_Method     := 'T';
1775    Standard_Err.Self              := Standard_Err;
     
        Standard_In.Stream             := stdin;
        Standard_In.Name               := In_Name'Access;
        Standard_In.Form               := Null_Str'Unrestricted_Access;
1780    Standard_In.Mode               := FCB.In_File;
        Standard_In.Is_Regular_File    := is_regular_file (fileno (stdin)) /= 0;
        Standard_In.Is_Temporary_File  := False;
        Standard_In.Is_System_File     := True;
        Standard_In.Is_Text_File       := True;
1785    Standard_In.Access_Method      := 'T';
        Standard_In.Self               := Standard_In;
     
        Standard_Out.Stream            := stdout;
        Standard_Out.Name              := Out_Name'Access;
1790    Standard_Out.Form              := Null_Str'Unrestricted_Access;
        Standard_Out.Mode              := FCB.Out_File;
        Standard_Out.Is_Regular_File   := is_regular_file (fileno (stdout)) /= 0;
        Standard_Out.Is_Temporary_File := False;
        Standard_Out.Is_System_File    := True;
1795    Standard_Out.Is_Text_File      := True;
        Standard_Out.Access_Method     := 'T';
        Standard_Out.Self              := Standard_Out;
     
        FIO.Chain_File (AP (Standard_In));
1800    FIO.Chain_File (AP (Standard_Out));
        FIO.Chain_File (AP (Standard_Err));
     
        FIO.Make_Unbuffered (AP (Standard_Out));
        FIO.Make_Unbuffered (AP (Standard_Err));
1805 
     end Ada.Text_IO;