------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUNTIME COMPONENTS                          --
--                                                                          --
--                      A D A . T E X T _ I O . A U X                       --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                            $Revision: 1.34 $                             --
--                                                                          --
--           Copyright (c) 1992,1993,1994 NYU, All Rights Reserved          --
--                                                                          --
-- The GNAT library is free software; you can redistribute it and/or modify --
-- it under terms of the GNU Library General Public License as published by --
-- the Free Software  Foundation; either version 2, or (at your option) any --
-- later version.  The GNAT library is distributed in the hope that it will --
-- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
-- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
-- Library  General  Public  License for  more  details.  You  should  have --
-- received  a copy of the GNU  Library  General Public License  along with --
-- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
-- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Finalization; use Ada.Finalization;
with System;           use System;
with System.Img_BIU;   use System.Img_BIU;
with System.Img_Int;   use System.Img_Int;
with System.Img_LLB;   use System.Img_LLB;
with System.Img_LLI;   use System.Img_LLI;
with System.Img_LLU;   use System.Img_LLU;
with System.Img_LLW;   use System.Img_LLW;
with System.Img_Real;  use System.Img_Real;
with System.Img_Uns;   use System.Img_Uns;
with System.Img_WIU;   use System.Img_WIU;
with System.Val_Int;   use System.Val_Int;
with System.Val_LLI;   use System.Val_LLI;
with System.Val_LLU;   use System.Val_LLU;
with System.Val_Real;  use System.Val_Real;
with System.Val_Uns;   use System.Val_Uns;

package body Ada.Text_IO.Aux is

   ----------------
   -- Local Data --
   ----------------

   Max_Num_Of_Files : constant := 60;

   Line_Feed : constant Character := Ascii.Lf;  --  Character'Val (16#0A#);
   Nul       : constant Character := Ascii.Nul; --  Character'Val (16#00#);
   Page_Mark : constant Character := Ascii.Ff;  --  Character'Val (16#0C#);

   --  The term "file" here is used in the same way as in the Ada Reference
   --  Manual, that is it refers to an object of some "file_type". Otherwise
   --  "external file" is used.

   Open_Files : array (1 .. Max_Num_Of_Files) of File_Type;
   --  Used to make sure we don't open too many files and that we do not
   --  open the same file twice.

   Scanning_From_File : Boolean;
   --  Determines if characters are read from a File (True) or String (False).

   type Temp_File_Rec;
   type Link is access Temp_File_Rec;

   type Temp_File_Rec is record
      File_Name : Pstring;
      Next      : Link;
   end record;

   Temp_Files : Link;

   type Work_String_Type is array (0 .. 1023) of Character;
   Work_String  : Work_String_Type;
   WS_Length    : Natural := 0;
   WS_Index1    : Natural := 0;
   WS_Index2    : Natural := 0;
   Tmp          : String (1 .. 1024);

   ------------------------
   --  Local Subprograms --
   ------------------------

   procedure Allocate_AFCB;
   --  Determine which AFCB in the Open_Files table is available to be used
   --  for the current file.

   function Alpha (C : Character) return Boolean;
   --  Predicate to test if Character argument is an upper or lower case
   --  letter, returns True if the argument is a letter, False if not.

   function Alphanum (C : Character) return Boolean;
   --  Predicate to test if Character is an upper or lower case letter
   --  or a digit. Returns True if the arguement is a letter or a digit,
   --  False if not.

   function Char1 return Character;
   function Char2 return Character;
   function Char3 return Character;
   --  Obtain indicated character of lookahead

   function Chars return Integer;
   --  Obtain count field from file

   procedure Check_Digit;
   --  Assert that the next Character is a digit otherwise raise Data_Error.

   procedure Check_Extended_Digit;
   --  Assert that the next Character is an extended digit otherwise raise
   --  Data_Error.

   procedure Check_File_Open;
   --  Check if the current file is open or not. If the file is not open,
   --  then Status_Error is raised. Otherwise control returns normally.

   procedure Check_Hash (C : Character);
   --  Determine if next Character is matching hash, raise Data_Error if not.
   --  Stores '#' in Work_String.

   procedure Check_Multiple_File_Opens;

   procedure Check_Opened_Ok;
   --  Check that an Fopen succeeded, raise Name_Error if not

   procedure Check_Status_And_Mode (C_Mode : File_Mode);
   --  If the current file is not open, then Status_Error is raised. If
   --  the file is open, then the mode is checked against the argument which
   --  is the desired mode for the operation. If it does not match, then
   --  Mode_Error is raised, otherwise control returns normally.

   procedure Check_Status_And_Mode (C_Mode1, C_Mode2 : File_Mode);
   --  If the current file is not open, then Status_Error is raised. If
   --  the file is open, then the mode is checked against the arguments which
   --  are the desired modes for the operation. If it does not match either
   --  one of them, Mode_Error is raised, otherwise control returns normally.

   procedure Close_File;
   --  Close file and deallocate the AFCB back to the pool.

   procedure Copy_Integer;
   --  This procedure copies a string with the syntax of "based_Integer" from
   --  the input to the Work_String. Underscores are allowed but not copied.

   procedure Copy_Based_Integer;
   --  This procedure copies a string with the syntax of "based_Integer" from
   --  the input to the Work_String. Underscores are allowed but not copied.

   procedure Copyc;
   --  Copy the next input Character to Work_String using WS_Index2

   function Digit (C : Character) return Boolean;
   --  Predicate if C corresponds to the digits 0 thru 9.

   function Extended_Digit (C : Character) return Boolean;
   --  Predicate if C corresponds to the digits 0 thru 9 or letters A thru F.

   function Graphic (C : Character) return Boolean;
   --  Predicate to test if the Character is a Latin-1 graphic letter.
   --  True if the argument is a Latin-1 graphic character, False otherwise.

   function Getcp return Character;
   --  Gets the next Character from the string or file being scanned according
   --  to the setting of Scanning_From_File. In string mode, WS_Index1 is
   --  updated. If no more Characters remain to be scanned, End_Error is
   --  raised.

   function Get_Char return Character;
   --  Get the next character from the current text input file. If no
   --  character is available, End_Error is raised.

   function  Is_Keyboard (F : Text_IO.File_Type) return Boolean;
   --  Indicates whether the input represents a tty (keyboard) rather than
   --  a stored file.
   pragma Inline (Is_Keyboard);

   procedure Make_Temp_File_Name;
   --  Generate a unique file name and use it for the name of the current file.

   function Nextc return Character;
   --  Return the next Character to be read from the string file being
   --  scanned, according to the setting of Scanning_From_File. In string
   --  mode WS_Index1 is updated. If we are currently at the end of string
   --  then a line feed is returned.

   function Page_Is_Not_Terminated return Boolean;
   --  Indicates whether the current page of current file is not terminated.

   procedure Put_Blanks (N : Integer);
   --  Write N blanks to the output. There is no check for line overflow, it
   --  is assumed that the caller has already checked for this.

   procedure Put_Buffer
     (Width    : Integer;
      Pad_Type : Character;
      Length : Integer);
   --  Need documentation ???

   procedure Put_Line1;
   --  Outputs a line feed to the current text file

   procedure Put_Page;
   --  Write a page mark to current text file.

   procedure Load_Look_Ahead (End_Of_File_Flag : Boolean);
   --  This procedure loads the lookahead for a TEXT_IO input file, leaving
   --  CHARS set to 3 (unless the file is less than 3 bytes long), and CHAR1
   --  CHAR2 and CHAR3 containing the initial characters of the file. A special
   --  exception occurs when the standard input file is the keyboard in which
   --  case we only read 1 character because of interactive I/O except when
   --  load_look_ahead is called in the case of END_OF_FILE where we want to
   --  read 2 characters to check for the EOT character. The parameter to this
   --  routine end_of_file_flag is TRUE when processing for and END_OF_FILE
   --  situation and is FALSE otherwise.

   procedure Range_Error;
   --  Procedure called if scanned number is out of range.

   function Scan_Based_Int (Base : Integer) return Integer;
   --  Need documentation ???

   procedure Scan_Blanks;
   --  Routine to scan past leading blanks to find first non-blank.
   --  Leaves WS_Index1 pointing to first non-blank character.

   procedure Scan_Enum (Last : out Natural);
   --  Procedure to scan an Ada enumeration literal, which maybe an identifier
   --  or a character literal. The input may be from a file or from a string
   --  depending the setting of the Scanning_From_File flag. The result is
   --  stored in Work_String.

   function Scan_Int return Integer;
   --  This routine scans an Integer value from the string pointed by the
   --  global Integer WS_Index2. On exit WS_Index2 is updated to point to
   --  the first
   --  non-digit. The result returned is always negative. This allows the
   --  largest negative Integer value to be properly stored and converted.
   --  A value of +1 returned indicated that overflow occured.

   procedure Scan_Integer (Width : Integer; Result : out Integer);
   --  Procedure to scan an Ada Integer value and return the Integer result
   --  The parameter Width specifies the width of the field (zero means an
   --  unlimited scan). The input is from the current TEXT_IO input file.

   procedure Scan_Integer_Val (Fixed_Field : Boolean; Result : out Integer);
   --  Procedure to scan an Ada Integer value and return the Integer result.

   function Scan_Float (Width : Natural) return LLF;
   --  Procedure to scan an Ada float value and return the float result.
   --  The width specifies the width of the field(zero = unlimited scan).
   --  For this case, the input is from the current TEXT_IO input file.

   function Scan_Float_Val (Fixed_Field : Boolean) return LLF;
   --  Procedure to scan an Ada float value and return the float result. The
   --  parameter num_type is a pointer to the type template for the float type.

   function Scan_Real_Val (Fixed_Field : Boolean) return LLF;
   --  Procedure to scan a real value and return the result as a double real.
   --  A range exception is signalled if the value is out of range of allowed
   --  Ada real values, but no other range check is made.

   procedure Set_Char1 (Val : Character);
   procedure Set_Char2 (Val : Character);
   procedure Set_Char3 (Val : Character);
   --  Set indicated character of lookahead to given character

   procedure Set_Chars (Val : Integer);
   --  Set count field of file to indicated value

   procedure Setup_Fixed_Field (Width : Integer);
   --  This procedure is used for numeric conversions where the field to be
   --  scanned has a fixed width (i.e. width parameter is non-zero).
   --  It acquires the field from the input file and copies it to Work_String.
   --  It returns to the caller ready to scan the data from work_string.

   function Sign (C : Character) return Boolean;
   --  Predicate indicating whether character C is '+' or '-'

   procedure Skipc;
   --  This procedure skips the next input Character.

   procedure Test_Fixed_Field_End;
   --  this procedure is called after scanning an item from a fixed length
   --  field to ensure that only blanks remain in the field. An exception
   --  is raised if there are any unexpected non-blank Characters left in
   --  the field.

   function Upper_Case (C : Character) return Character;
   --  Converts character C to upper case if necessary

   procedure Unimplemented (Message : String);
   --  Output message for unimplemented feature

   procedure Word_Mul
     (A : Integer;
      B : Integer;
      O : out Boolean;
      R : out Integer);
   --  Multiply with overflow check (use until trapping arithmetic works).

   procedure Word_Sub
     (A : Integer;
      B : Integer;
      O : out Boolean;
      R : out Integer);
   --  Subtraction with overflow check (use until trapping arithmetic works)

   --  Interface with system calls

   procedure C_Fgetc
     (F      : Text_IO.File_Ptr;
      C      : out Character;
      Is_Eof : out Boolean);

   procedure Fclose (P : Text_IO.File_Ptr);

   function  Fopen (Name : String; Typ : File_Mode) return Text_IO.File_Ptr;

   procedure Fputc (F : Text_IO.File_Ptr; C : Character);

   function  Isatty (F : Text_IO.File_Ptr) return Boolean;

   function  Stdin return Text_IO.File_Ptr;

   function  Stdout return Text_IO.File_Ptr;

   function  Stderr return Text_IO.File_Ptr;

   procedure Unlink (Name : String);

   -----------
   -- Chars --
   -----------

   function Chars return Integer is
   begin
      return The_File.Count;
   end Chars;

   ---------------
   -- Set_Chars --
   ---------------

   procedure Set_Chars (Val : Integer) is
   begin
      The_File.Count := Val;
   end Set_Chars;

   -----------
   -- Char1 --
   -----------

   function Char1 return Character is
   begin
      return The_File.Look_Ahead (1);
   end Char1;

   ---------------
   -- Set_Char1 --
   ---------------

   procedure Set_Char1 (Val : Character) is
   begin
      The_File.Look_Ahead (1) := Val;
   end Set_Char1;

   -----------
   -- Char2 --
   -----------

   function Char2 return Character is
   begin
      return The_File.Look_Ahead (2);
   end Char2;

   ---------------
   -- Set_Char2 --
   ---------------

   procedure Set_Char2 (Val : Character) is
   begin
      The_File.Look_Ahead (2) := Val;
   end Set_Char2;

   -----------
   -- Char3 --
   -----------

   function Char3 return Character is
   begin
      return The_File.Look_Ahead (3);
   end Char3;

   ---------------
   -- Set_Char3 --
   ---------------

   procedure Set_Char3 (Val : Character) is
   begin
      The_File.Look_Ahead (3) := Val;
   end Set_Char3;

   ------------
   -- Create --
   ------------

   procedure Create
     (File : in out File_Type;
      Mode : in File_Mode := Out_File;
      Name : in String := "";
      Form : in String := "") is

   begin
      The_File := File;

      if The_File /= null then
         raise Status_Error; --  File already open
      elsif Mode = In_File then
         raise Use_Error;    -- Unsupported file access
      end if;

      Allocate_AFCB;
      The_File.Name := new String'(Name);
      The_File.Form := new String'(Form);
      The_File.Mode := Mode;

      if Name'Length = 0 then
         Make_Temp_File_Name;
      end if;

      Check_Multiple_File_Opens;
      The_File.AFCB_In_Use := True;
      The_File.Desc := Fopen (The_File.Name.all, Mode);
      Check_Opened_Ok;

      The_File.Page := 1;
      The_File.Line := 1;
      The_File.Col := 1;
      The_File.Line_Length := 0;
      The_File.Page_Length := 0;
      File := The_File;
   end Create;

   ----------
   -- Open --
   ----------

   procedure Open
     (File : in out File_Type;
      Mode : in File_Mode;
      Name : in String;
      Form : in String := "")
   is
   begin
      The_File := File;

      if The_File /= null then
         raise Status_Error; --  File already open
      end if;

      Allocate_AFCB;
      The_File.Name := new String'(Name);
      The_File.Form := new String'(Form);
      The_File.Mode := Mode;

      if Name'Length = 0 then
         Make_Temp_File_Name;
      end if;

      Check_Multiple_File_Opens;
      The_File.AFCB_In_Use := True;

      The_File.Desc := Fopen (Name, Mode);
      Check_Opened_Ok;
      if Mode = In_File then
         Set_Chars (0);
      end if;

      The_File.Page := 1;
      The_File.Line := 1;
      The_File.Col := 1;
      The_File.Line_Length := 0;
      The_File.Page_Length := 0;
      The_File.Is_Keyboard := False;
      File := The_File;
   end Open;

   -----------
   -- Close --
   -----------

   procedure Close (File : in out File_Type) is
   begin
      The_File := File;
      Check_File_Open;

      if The_File.Mode = Out_File or else The_File.Mode = Append_File then

         --  Simulate effect of NEW_PAGE unless current page is terminated

         if Page_Is_Not_Terminated then
            if The_File.Col > 1
              or else (The_File.Col = 1 and then The_File.Line = 1)
            then
               Put_Line1;
            end if;

            Put_Page;
         end if;
      end if;

      --  If the file being closed is one of the default files, set the default
      --  file indicator to null to indicate that the file is closed.

      if The_File = Current_In then
         Current_In := null;
      elsif The_File = Current_Out then
         Current_Out := null;
      elsif The_File = Current_Err then
         Current_Err := null;
      end if;

      --  Sever the association between the given file and its associated
      --  external file. The given file is left closed. Do not perform system
      --  closes on the standard input, output and error files.

      if The_File /= Standard_In
        and then The_File /= Standard_Out
        and then The_File /= Standard_Err
      then
         Close_File;
      end if;

      The_File := null;
      File := The_File;
   end Close;

   ------------
   -- Delete --
   ------------

   procedure Delete (File : in out File_Type) is
      File_Name_To_Delete : Pstring;

   begin
      The_File := File;
      Check_File_Open;
      File_Name_To_Delete := new String'(The_File.Name.all);
      Close (The_File);
      Unlink (File_Name_To_Delete.all);
      File := The_File;
   end Delete;

   -----------
   -- Reset --
   -----------

   procedure Reset
     (File : in out File_Type;
      Mode : in File_Mode)
   is
   begin
      The_File := File;
      Check_File_Open;

      if (The_File = Current_In
           or else The_File = Current_Out
           or else The_File = Current_Err)
        and then The_File.Mode /= Mode
      then
         raise Mode_Error;  --  "Cannot change mode"
      end if;

      if The_File.Mode = Out_File or else The_File.Mode = Append_File then

         --  Simulate NEW_PAGE unless current page already terminated

         if Page_Is_Not_Terminated then
            if The_File.Col > 1
              or else (The_File.Col = 1 and then The_File.Line = 1)
            then
               Put_Line1;
            end if;

            Put_Page;
         end if;
      end if;

      Fclose (The_File.Desc);

      The_File.Desc := Fopen (The_File.Name.all, Mode);
      Check_Opened_Ok;

      if Mode /= In_File then
         The_File.Line_Length := 0;
         The_File.Page_Length := 0;
      end if;

      The_File.Mode := Mode;
      Set_Chars (0);
      The_File.Col  := 1;
      The_File.Line := 1;
      The_File.Page := 1;
      File := The_File;
   end Reset;

   ----------
   -- Mode --
   ----------

   function Mode (File : in File_Type) return File_Mode is
   begin
      The_File := File;
      Check_File_Open;
      return The_File.Mode;
   end Mode;

   ----------
   -- Name --
   ----------

   function Name (File : in File_Type) return String is
   begin
      The_File := File;
      Check_File_Open;
      return The_File.Name.all;
   end Name;

   ----------
   -- Form --
   ----------

   function Form (File : in File_Type) return String is
   begin
      The_File := File;
      Check_File_Open;
      return The_File.Form.all;
   end Form;

   -------------
   -- Is_Open --
   -------------

   function Is_Open (File : in File_Type) return Boolean is
   begin
      The_File := File;
      return The_File /= null;
   end Is_Open;

   ---------------
   -- Set_Input --
   ---------------

   procedure Set_Input (File : in File_Type) is
   begin
      The_File := File;
      Check_Status_And_Mode (In_File);
      Current_In := The_File;
   end Set_Input;

   ----------------
   -- Set_Output --
   ----------------

   procedure Set_Output (File : in File_Type) is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      Current_Out := The_File;
   end Set_Output;

   ---------------
   -- Set_Error --
   ---------------

   procedure Set_Error (File : in File_Type) is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      Current_Err := The_File;
   end Set_Error;

   --------------------
   -- Standard_Input --
   --------------------

   function Standard_Input return File_Type is
   begin
      return Standard_In;
   end Standard_Input;

   ---------------------
   -- Standard_Output --
   ---------------------

   function Standard_Output return File_Type is
   begin
      return Standard_Out;
   end Standard_Output;

   --------------------
   -- Standard_Error --
   --------------------

   function Standard_Error return File_Type is
   begin
      return Standard_Err;
   end Standard_Error;

   -------------------
   -- Current_Input --
   -------------------

   function Current_Input return File_Type is
   begin
      return Current_In;
   end Current_Input;

   --------------------
   -- Current_Output --
   --------------------

   function Current_Output return File_Type is
   begin
      return Current_Out;
   end Current_Output;

   -------------------
   -- Current_Error --
   -------------------

   function Current_Error return File_Type is
   begin
      return Current_Err;
   end Current_Error;

   ---------------------
   -- Set_Line_Length --
   ---------------------

   procedure Set_Line_Length (File : in File_Type; To : in Count) is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      The_File.Line_Length := To;
   end Set_Line_Length;

   -----------------
   -- Line_Length --
   -----------------

   function Line_Length (File : in File_Type) return Count is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      return The_File.Line_Length;
   end Line_Length;

   ---------------------
   -- Set_Page_Length --
   ---------------------

   procedure Set_Page_Length (File : in File_Type; To : in Count) is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      The_File.Page_Length := To;
   end Set_Page_Length;

   -----------------
   -- Page_Length --
   -----------------

   function Page_Length (File : in File_Type) return Count is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);
      return The_File.Page_Length;
   end Page_Length;

   --------------
   -- New_Line --
   --------------

   procedure New_Line
     (File    : in File_Type;
      Spacing : in Positive_Count := 1)
   is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);

      for J in 1 .. Spacing loop
         Put_Line1;
      end loop;
   end New_Line;

   ---------------
   -- Skip_Line --
   ---------------

   procedure Skip_Line
     (File    : in File_Type;
      Spacing : in Positive_Count := 1)
   is
      C : Character;

   begin
      The_File := File;
      Check_Status_And_Mode (In_File);

      for J in 1 .. Spacing loop
         loop
            Load_Look_Ahead (False);
            exit when Get_Char = Line_Feed;
         end loop;

         --  Ignore page marks when reading from a terminal.

         if Is_Keyboard (The_File) then
            return;
         end if;

         loop
            Load_Look_Ahead (False);
            exit when Char1 /= Page_Mark;
            C := Get_Char;
         end loop;
      end loop;
   end Skip_Line;

   -----------------
   -- End_Of_Line --
   -----------------

   function End_Of_Line (File : in File_Type) return boolean is
   begin
      The_File := File;
      Check_Status_And_Mode (In_File);
      Load_Look_Ahead (False);
      return Chars = 0 or else Char1 = Line_Feed;
   end End_Of_Line;

   --------------
   -- New_Page --
   --------------

   procedure New_Page (File : in File_Type) is
   begin
      The_File := File;
      Check_Status_And_Mode (Out_File, Append_File);

      if The_File.Col > 1
       or else (The_File.Col = 1 and then The_File.Line = 1)
      then
         Put_Line1;
      end if;

      Put_Page;
   end New_Page;

   ---------------
   -- Skip_Page --
   ---------------

   procedure Skip_Page (File : in File_Type) is
   begin
      The_File := File;
      Check_Status_And_Mode (In_File);

      while Get_Char /= Page_Mark loop
         null;
      end loop;
   end Skip_Page;

   -----------------
   -- End_Of_Page --
   -----------------

   function End_Of_Page (File : in File_Type) return Boolean is
   begin
      The_File := File;
      Check_Status_And_Mode (In_File);

      if Is_Keyboard (The_File) then
         return False;
      end if;

      Load_Look_Ahead (False);

      if Chars > 1 then
         return Char1 = Line_Feed and then Char2 = Page_Mark;
      elsif Chars = 1 then
         return Char1 = Line_Feed;
      else
         return True;
      end if;
   end End_Of_Page;

   -----------------
   -- End_Of_File --
   -----------------

   function End_Of_File (File : in File_Type) return Boolean is
   begin
      The_File := File;
      Check_Status_And_Mode (In_File);
      Load_Look_Ahead (True);

      if Is_Keyboard (The_File) then
         if Chars = 2 then
            return False;
         elsif Chars = 1 then
            return Char1 = Line_Feed;
         elsif Chars = 0 then
            return True;
         end if;
      else
         if Chars = 2 then
            return Char1 = Line_Feed and then Char2 = Page_Mark;
         elsif Chars = 1 then
            return Char1 = Line_Feed;
         elsif Chars = 0 then
            return True;
         else --  Chars = 3
            return False;
         end if;
      end if;
   end End_Of_File;

   -------------
   -- Set_Col --
   -------------

   procedure Set_Col (File : in File_Type; To : in Positive_Count) is
      C : Character;

   begin
      The_File := File;
      Check_File_Open;

      if The_File.Mode = In_File then

         --  SET_COL for file of mode In_File

         Load_Look_Ahead (False);

         while The_File.Col /= To
           or else Char1 = Line_Feed
           or else Char1 = Page_Mark
         loop
            C := Get_Char;
         end loop;

      else

         --  SET_COL for file of mode Out_File or Append_File

         if The_File.Line_Length > 0
           and then To > The_File.Line_Length
         then
            raise Layout_Error; --  "SET_COL past end of line"
         end if;

         if To > The_File.Col then
            Put_Blanks (Integer (To - The_File.Col));
            The_File.Col := To;
         elsif To < The_File.Col then
            Put_Line1;
            Put_Blanks (Integer (To - 1));
            The_File.Col := To;
         end if;
      end if;
   end Set_Col;

   --------------
   -- Set_Line --
   --------------

   procedure Set_Line (File : in File_Type; To : in Positive_Count) is
      C : Character;

   begin
      The_File := File;
      Check_File_Open;

      if The_File.Mode = In_File then

         --  SET_LINE for file of mode In_File

         Load_Look_Ahead (False);

         while The_File.Line /= To
           or else Char1 = Page_Mark
         loop
            C := Get_Char;
         end loop;

      else

         --  SET_LINE for file of mode Out_File or Append_File

         if The_File.Page_Length > 0
           and then To > The_File.Page_Length
         then
            raise Layout_Error;  --  "Set_Line > Page_Length"
         end if;

         if To > The_File.Line  then
            for I in 1 .. To - The_File.Line loop
               Put_Line1;
            end loop;

         elsif To < The_File.Line then
            if The_File.Col > 1
              or else (The_File.Col = 1 and then The_File.Line = 1)
            then
               Put_Line1;
            end if;

            Put_Page;

            for J in 1 .. To - 1 loop
               Put_Line1;
            end loop;
         end if;
      end if;
   end Set_Line;

   ---------
   -- Col --
   ---------

   function Col (File : in File_Type) return Positive_Count is
   begin
      The_File := File;
      Check_File_Open;

      if The_File.Col > Count'Last then
         raise Layout_Error; --  "Col > Count'Last"
      end if;

      return The_File.Col;
   end Col;

   ----------
   -- Line --
   ----------

   function Line (File : in File_Type) return Positive_Count is
   begin
      The_File := File;
      Check_File_Open;

      if The_File.Line > Count'Last then
         raise Layout_Error; --  "Line > Count'Last"
      end if;

      return The_File.Line;
   end Line;

   ----------
   -- Page --
   ----------

   function Page (File : in File_Type) return Positive_Count is
   begin
      The_File := File;
      Check_File_Open;

      if The_File.Page > Count'Last then
         raise Layout_Error; --  "Page > Count'Last"
      end if;

      return The_File.Page;
   end Page;

   ---------
   -- Get --
   ---------

   procedure Get (Item : out Character) is
   begin
      Check_Status_And_Mode (In_File);

      loop
         Item := Get_Char;
         exit when Item /= Page_Mark and then Item /= Line_Feed;
      end loop;
   end Get;

   ---------
   -- Put --
   ---------

   procedure Put (Item : in Character) is
   begin
      Check_Status_And_Mode (Out_File, Append_File);

      if The_File.Line_Length /= 0
        and then The_File.Col > The_File.Line_Length
      then
         Put_Line1;
      end if;

      Fputc (The_File.Desc, Item);
      The_File.Col := The_File.Col + 1;
   end Put;

   ---------
   -- Get --
   ---------

   procedure Get (Item : out String) is
      J : Integer := 0;
      C : Character;

   begin
      Check_Status_And_Mode (In_File);

      while J < Item'Length loop
         C := Get_Char;

         if C /= Line_Feed and then C /= Page_Mark then
            Item (Item'First + J) := C;
            J := J + 1;
         end if;
      end loop;
   end Get;

   ---------
   -- Put --
   ---------

   procedure Put (Item : in String) is
   begin
      for J in Item'Range loop
         Put (Item (J));
      end loop;
   end Put;

   --------------
   -- Put_Line --
   --------------

   procedure Put_Line (File : in File_Type; Item : in String) is
   begin
      The_File := File;
      Put (Item);
      New_Line (File, 1);
   end Put_Line;

   --------------
   -- Get_Line --
   --------------

   procedure Get_Line
     (File : in File_Type;
      Item : out String;
      Last : out Natural)
   is
      I_Length : Integer := Item'Length;
      Nstore   : Integer := 0;

   begin
      The_File := File;
      Check_Status_And_Mode (In_File);

      loop
         Load_Look_Ahead (False);
         exit when Nstore = I_Length;

         if Char1 = Line_Feed then
            Skip_Line (File, 1);
            exit;
         end if;

         Item (Item'First + Nstore) := Get_Char;
         Nstore := Nstore + 1;
      end loop;

      Last := Item'First + Nstore - 1;
   end Get_Line;

   -------------
   -- Get_Int --
   -------------

   procedure Get_Int
     (Item  : out Integer;
      Width : in Field := 0)
   is
   begin
      Check_Status_And_Mode (In_File);
      Scan_Integer (Width, Item);
   end Get_Int;

   -----------------
   -- Put_Integer --
   -----------------

   procedure Put_Integer
     (Item  : in Integer;
      Width : in Field;
      Base  : in Number_Base)
   is
   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := 0;

      if Base = 10 and then Width = 0 then
         Set_Image_Integer (Item, Tmp, WS_Length);
      elsif Base = 10 then
         Set_Image_Width_Integer (Item, Width, Tmp, WS_Length);
      else
         Set_Image_Based_Integer (Item, Base, Width, Tmp, WS_Length);
      end if;

      for J in 1 .. WS_Length loop
         Work_String (J - 1) := Tmp (J);
      end loop;

      Put_Buffer (Width, 'L', WS_Length);
   end Put_Integer;

   -------------
   -- Put_LLI --
   -------------

   procedure Put_LLI
     (Item  : in LLI;
      Width : in Field;
      Base  : in Number_Base)
   is
   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := 0;

      if Base = 10 and then Width = 0 then
         Set_Image_Long_Long_Integer (Item, Tmp, WS_Length);
      elsif Base = 10 then
         Set_Image_Width_Long_Long_Integer (Item, Width, Tmp, WS_Length);
      else
         Set_Image_Based_Long_Long_Integer (Item, Base, Width, Tmp, WS_Length);
      end if;

      for J in 1 .. WS_Length loop
         Work_String (J - 1) := Tmp (J);
      end loop;

      Put_Buffer (Width, 'L', WS_Length);
   end Put_LLI;

   -------------
   -- Get_LLI --
   -------------

   procedure Get_LLI
     (From : in  String;
      Item : out LLI;
      Last : out Positive;
      Size : in  Positive)
   is
      Pos : aliased Integer := From'First;

   begin
      if Size > Integer'Size then
         Item := Scan_Long_Long_Integer (From, Pos'Access, From'Last);
      else
         Item := LLI (Scan_Integer (From, Pos'Access, From'Last));
      end if;

      Last := Pos - 1;

   exception
      when Constraint_Error =>
         if Pos > From'Last then
            raise End_Error;
         else
            raise Data_Error;
         end if;
   end Get_LLI;

   -------------
   -- Get_LLU --
   -------------

   procedure Get_LLU
     (From : in String;
      Item : out LLU;
      Last : out Positive;
      Size : in Positive)
   is
      Pos : aliased Integer := From'First;

   begin
      if Size > Unsigned'Size then
         Item := Scan_Long_Long_Unsigned (From, Pos'Access, From'Last);
      else
         Item := LLU (Scan_Unsigned (From, Pos'Access, From'Last));
      end if;

      Last := Pos - 1;

   exception
      when Constraint_Error =>
         if Pos > From'Last then
            raise End_Error;
         else
            raise Data_Error;
         end if;
   end Get_LLU;

   -----------------
   -- Put_Integer --
   -----------------

   procedure Put_Integer
     (To   : out String;
      Item : in Integer;
      Base : in Number_Base)
   is
      Length : Natural := 0;
      To_Len : Natural := To'Length;

   begin
      if Base = 10 then
         Set_Image_Width_Integer (Item, To_Len, Tmp, Length);
      else
         Set_Image_Based_Integer (Item, Base, To_Len, Tmp, Length);
      end if;

      if Length > To_Len then
         raise Layout_Error;
      end if;

      for J in 1 .. Length loop
         To (To'First + J - 1) := Tmp (J);
      end loop;

   end Put_Integer;

   -------------
   -- Put_LLI --
   -------------

   procedure Put_LLI
     (To   : out String;
      Item : in LLI;
      Base : in Number_Base)
   is
      Length : Natural := 0;
      To_Len : Natural := To'Length;

   begin
      if Base = 10 then
         Set_Image_Width_Long_Long_Integer (Item, To_Len, Tmp, Length);
      else
         Set_Image_Based_Long_Long_Integer (Item, Base, To_Len, Tmp, Length);
      end if;

      if Length > To_Len then
         raise Layout_Error;
      end if;

      for J in 1 .. Length loop
         To (To'First + J - 1) := Tmp (J);
      end loop;

   end Put_LLI;

   ------------------
   -- Put_Unsigned --
   ------------------

   procedure Put_Unsigned
     (Item  : in Unsigned;
      Width : in Field;
      Base  : in Number_Base)
   is
   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := 0;

      if Base = 10 and then Width = 0 then
         Set_Image_Unsigned (Item, Tmp, WS_Length);
      elsif Base = 10 then
         Set_Image_Width_Unsigned (Item, Width, Tmp, WS_Length);
      else
         Set_Image_Based_Unsigned (Item, Base, Width, Tmp, WS_Length);
      end if;

      for J in 1 .. WS_Length loop
         Work_String (J - 1) := Tmp (J);
      end loop;

      Put_Buffer (Width, 'L', WS_Length);
   end Put_Unsigned;

   -------------
   -- Put_LLU --
   -------------

   procedure Put_LLU
     (Item  : in LLU;
      Width : in Field;
      Base  : in Number_Base)
   is
   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := 0;

      if Base = 10 and then Width = 0 then
         Set_Image_Long_Long_Unsigned (Item, Tmp, WS_Length);
      elsif Base = 10 then
         Set_Image_Width_Long_Long_Unsigned (Item, Width, Tmp, WS_Length);
      else
         Set_Image_Based_Long_Long_Unsigned
           (Item, Base, Width, Tmp, WS_Length);
      end if;

      for J in 1 .. WS_Length loop
         Work_String (J - 1) := Tmp (J);
      end loop;

      Put_Buffer (Width, 'L', WS_Length);
   end Put_LLU;

   ------------------
   -- Put_Unsigned --
   ------------------

   procedure Put_Unsigned
     (To   : out String;
      Item : in Unsigned;
      Base : in Number_Base)
   is
      Length : Natural := 0;
      To_Len : Natural := To'Length;

   begin
      if Base = 10 then
         Set_Image_Width_Unsigned (Item, To_Len, Tmp, Length);
      else
         Set_Image_Based_Unsigned (Item, Base, To_Len, Tmp, Length);
      end if;

      if Length > To_Len then
         raise Layout_Error;
      end if;

      for J in 1 .. Length loop
         To (To'First + J - 1) := Tmp (J);
      end loop;

   end Put_Unsigned;

   -------------
   -- Put_LLU --
   -------------

   procedure Put_LLU
     (To   : out String;
      Item : in LLU;
      Base : in Number_Base)
   is
      Length : Natural := 0;
      To_Len : Natural := To'Length;

   begin
      if Base = 10 then
         Set_Image_Width_Long_Long_Unsigned (Item, To_Len, Tmp, Length);
      else
         Set_Image_Based_Long_Long_Unsigned (Item, Base, To_Len, Tmp, Length);
      end if;

      if Length > To_Len then
         raise Layout_Error;
      end if;

      for J in 1 .. Length loop
         To (To'First + J - 1) := Tmp (J);
      end loop;
   end Put_LLU;

   ---------------
   -- Get_Float --
   ---------------

   procedure Get_Float
     (Item : out LLF;
      Width : in Field)
   is
   begin
      Check_Status_And_Mode (In_File);
      Item := Scan_Float (Width);
   end Get_Float;

   ---------------
   -- Put_Float --
   ---------------

   procedure Put_Float
     (Item : in LLF;
      Fore : in Field;
      Aft  : in Field;
      Exp  : in Field)
   is
      Temp : String (1 .. 1024);
   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := 0;
      Set_Image_Real (Item, Temp, WS_Length, Fore, Aft, Exp);

      for J in 1 .. WS_Length loop
         Work_String (J - 1) := Temp (J);
      end loop;

      Put_Buffer (WS_Length, 'L', WS_Length);
   end Put_Float;

   ---------------
   -- Get_Float --
   ---------------

   procedure Get_Float
     (From : in String;
      Item : out LLF;
      Last : out Positive)
   is
      Pos : aliased Natural := From'First;

   begin
      Item := Scan_Real (From, Pos'Access, From'Last);
      Last := Pos - 1;

   exception
      when Constraint_Error =>
         if Pos > From'Last then
            raise End_Error;
         else
            raise Data_Error;
         end if;
   end Get_Float;

   ---------------
   -- Put_Float --
   ---------------

   procedure Put_Float
     (To   : out String;
      Item : in LLF;
      Aft  : in Field;
      Exp  : in Field)
   is
      Length : Natural := 0;
      To_Len : Natural := To'Length;
      Temp   : String (1 .. 1024);
      --  ??? what is the 1024 here? should be symbolic

   begin
      Set_Image_Real (Item, Temp, Length, 0, Aft, Exp);

      if Length > To_Len then
         raise Layout_Error;
      end if;

      for J in 0 .. To_Len - Length - 1 loop
         To (To'First + J) :=  ' ';
      end loop;

      for J in 1 .. Length loop
         To (To'First + J - 1 + To_Len - Length) := Temp (J);
      end loop;

   end Put_Float;

   --------------
   -- Get_Enum --
   --------------

   procedure Get_Enum (Str : out String; Len : out Positive) is
      Last : Natural;

   begin
      Check_Status_And_Mode (In_File);
      Scanning_From_File := True;
      Scan_Enum (Last);

      for J in 1 .. WS_Length loop
         Str (J) := Upper_Case (Work_String (J - 1));
      end loop;

      Len := WS_Length;
   end Get_Enum;

   --------------
   -- Get_Enum --
   --------------

   procedure Get_Enum
     (Str  : out String;
      From : in String;
      Len  : out Positive;
      Last : out Positive)
   is
   begin
      WS_Length := From'Length;

      for J in 0 .. WS_Length - 1 loop
         Work_String (J) := From (From'First + J);
      end loop;

      WS_Index1 := 0;
      Scanning_From_File := False;
      Scan_Enum (Last);
      Last := From'First + Last - 1;

      for J in 1 .. WS_Length loop
         Str (J) := Upper_Case (Work_String (J - 1));
      end loop;

      Len := WS_Length;
   end Get_Enum;

   --------------
   -- Put_Enum --
   --------------

   procedure Put_Enum
     (Item  : in String;
      Width : in Field;
      Set   : in Type_Set)
   is
      C : Character;

   begin
      Check_Status_And_Mode (Out_File, Append_File);
      WS_Length := Item'Length;

      for J in 0 .. WS_Length - 1 loop
         C := Item (Item'First + J);

         --  This is wrong, must use proper casing stuff in strings packages
         --  ???

         if Set = Lower_Case and then C in 'A' .. 'Z' then
            Work_String (J) := Character'Val (Character'Pos (C) + 32);
         else
            Work_String (J) := C;
         end if;
      end loop;

      Put_Buffer (Width, 'T', WS_Length);
   end Put_Enum;

   --------------
   -- Put_Enum --
   --------------

   procedure Put_Enum
     (To   : out String;
      Item : in String;
      Set  : in Type_Set)
   is
      Length : Integer := Item'Length;
      C      : Character;

   begin
      if Length > To'Length then
         raise Layout_Error;
      else
         for J in 0 .. Length - 1 loop
            C := Item (Item'First + J);

            --  This is wrong, must use proper casing stuff in strings
            --  packages ???

            if Set = Lower_Case and then C in 'A' .. 'Z' then
               To (To'First + J) := Character'Val (Character'Pos (C) + 32);
            else
               To (To'First + J) := C;
            end if;
         end loop;

         for J in Length .. To'Length - 1 loop
            To (To'First + J) := ' ';
         end loop;
      end if;
   end Put_Enum;

   --------------
   -- Put_Page --
   --------------

   procedure Put_Page is
   begin
      Fputc (The_File.Desc, Page_Mark);
      The_File.Page := The_File.Page + 1;
      The_File.Line := 1;
      The_File.Col := 1;
   end Put_Page;

   ---------------
   -- Put_Line1 --
   ---------------

   procedure Put_Line1 is
   begin
      Fputc (The_File.Desc, Line_Feed);
      The_File.Col := 1;

      if The_File.Page_Length > 0
         and The_File.Line >= The_File.Page_Length
      then
         Put_Page;
      else
         The_File.Line := The_File.Line + 1;
      end if;
   end Put_Line1;

   ---------------------
   -- Check_Opened_Ok --
   ---------------------

   procedure Check_Opened_Ok is
   begin
      if The_File.Desc = File_Ptr (Null_Address) then
         raise Name_Error; --  Error opening file due to invalid name
      end if;
   end Check_Opened_Ok;

   ---------------------
   -- Check_File_Open --
   ---------------------

   procedure Check_File_Open is
   begin
      --  There are two ways a file can appear closed. Either it is null
      --  which indicates that it was not used as an argument of an Open_Create
      --  call or it is not null but its Is_Open field is False which indicates
      --  that the file was used in an Open/Create but subsequently was closed.

      if The_File = null then
         raise Status_Error; --  File not open
      end if;
   end Check_File_Open;

   ---------------------------
   -- Check_Status_And_Mode --
   ---------------------------

   procedure Check_Status_And_Mode (C_Mode : File_Mode) is
   begin
      Check_File_Open;

      if The_File.Mode /= C_Mode then
         raise Mode_Error;
      end if;
   end Check_Status_And_Mode;

   ---------------------------
   -- Check_Status_And_Mode --
   ---------------------------

   procedure Check_Status_And_Mode (C_Mode1, C_Mode2 : File_Mode) is
   begin
      Check_File_Open;

      if The_File.Mode /= C_Mode1 and then The_File.Mode /= C_Mode2 then
         raise Mode_Error;
      end if;
   end Check_Status_And_Mode;

   -------------------
   -- Allocate_AFCB --
   --------------------

   procedure Allocate_AFCB is
      File_Num : Integer := Open_Files'First;

   begin
      --  Loop through the array of AFCBs stopping at the first vacate spot
      --  that is not currently being used.

      while File_Num <= Max_Num_Of_Files
        and then Open_Files (File_Num) /= null
        and then Open_Files (File_Num).AFCB_In_Use
      loop
         File_Num := File_Num + 1;
      end loop;

      --  No vacant spots were available since too many file are open

      if File_Num > Max_Num_Of_Files then
         raise Use_Error;  --  Too many files open
      end if;

      if Open_Files (File_Num) = null then
         Open_Files (File_Num) := new AFCB;
      end if;

      The_File := Open_Files (File_Num);
   end Allocate_AFCB;

   -------------------------
   -- Make_Temp_File_Name --
   -------------------------

   procedure Make_Temp_File_Name is
      Temp_File_Name  : String (1 .. 14);
      --  The template for temporary file name creation using Mktemp.

      procedure mktemp (S : Address);
      pragma Import (C, mktemp);
      --  mktemp creates a unique temporary file name given the address of
      --  a null terminated template.

   begin
      --  Create a template string which the call to mktemp will fill in to
      --  generate unique name file name.

      Temp_File_Name (1 .. 13) := "ADATEMPXXXXXX";
      Temp_File_Name (14) := Ascii.Nul;
      mktemp (Temp_File_Name'Address);
      The_File.Name := new String'(Temp_File_Name (1 .. 13));

      --  Append the name of the temporary file to the beginning of the
      --  Temp_File list which will be used for deleting all the temporary
      --  files after completion of the main program.

      Temp_Files := new Temp_File_Rec'(The_File.Name, Temp_Files);
   end Make_Temp_File_Name;

   -------------------------------
   -- Check_Multiple_File_Opens --
   -------------------------------

   procedure Check_Multiple_File_Opens is
   begin
      --  Allow a several opens to read an external file, but not one open to
      --  read and another open to write a external file.

      for J in Open_Files'Range loop
         if Open_Files (J) /= null and then Open_Files (J).AFCB_In_Use then
            if The_File.Name.all = Open_Files (J).Name.all
              and then (The_File.Mode /= In_File
                         or else Open_Files (J).Mode /= In_File)
            then
               raise Use_Error; --  File already open
            end if;
         end if;
      end loop;
   end Check_Multiple_File_Opens;

   -----------------------------
   --  Page_Is_Not_Terminated --
   -----------------------------

   function Page_Is_Not_Terminated return Boolean is
   begin
      return not (The_File.Col = 1
        and then The_File.Line = 1
        and then The_File.Page /= 1);
   end Page_Is_Not_Terminated;

   ----------------
   -- Close_File --
   ----------------

   procedure Close_File is
      procedure Fclose (F : Text_IO.File_Ptr);
      pragma Import (C, fclose);

      File_Num : Integer := Open_Files'First;

   begin
      while File_Num <= Max_Num_Of_Files
         and then Open_Files (File_Num) /= The_File
      loop
         File_Num := File_Num + 1;
      end loop;

      if File_Num > Max_Num_Of_Files then
         raise Status_Error;
      end if;

      Fclose (The_File.Desc);
      The_File.AFCB_In_Use := False;

   end Close_File;

   ---------------------
   -- Load_Look_Ahead --
   ---------------------

   procedure Load_Look_Ahead (End_Of_File_Flag : Boolean) is
      C      : Character;
      Is_Eof : Boolean;

   begin
      --  Load first character of look ahead

      if Chars = 0 then
         Set_Char2 (Nul);
         Set_Char3 (Nul);
         C_Fgetc (The_File.Desc, C, Is_Eof);

         if Is_Eof then
            Set_Char1 (Nul);
            return;
         else
            Set_Char1 (C);
            Set_Chars (1);
         end if;
      end if;

      --  In the case where reading from the keyboard do not read more than
      --  1 character unless you are processing an end_of_file test.

      if Is_Keyboard (The_File) and then not End_Of_File_Flag then
         return;
      end if;

      --  Load second character of look ahead

      if Chars = 1 then
         Set_Char3 (Nul);
         C_Fgetc (The_File.Desc, C, Is_Eof);

         if Is_Eof then
            Set_Char2 (Nul);
            return;
         else
            Set_Char2 (C);
            Set_Chars (2);
         end if;
      end if;

      --  Leave lookahead with at most two characters loaded if standard
      --  input is the keyboard.

      if not Is_Keyboard (The_File) then

         --  Load third character of look ahead

         if Chars = 2 then
            C_Fgetc (The_File.Desc, C, Is_Eof);

            if Is_Eof then
               Set_Char3 (Nul);
               return;
            else
               Set_Char3 (C);
               Set_Chars (3);
            end if;
         end if;
      end if;
   end Load_Look_Ahead;

   --------------
   -- Get_Char --
   --------------

   function Get_Char return Character is
      C : Character;

   begin
      Load_Look_Ahead (False);

      if Chars = 0 then
         raise End_Error;  --  End of file on TEXT_IO input
      end if;

      C := Char1;

      --  Update lookahead

      Set_Char1 (Char2);
      Set_Char2 (Char3);
      Set_Char3 (Nul);
      Set_Chars (Chars - 1);

      --  Update PAGE and LINE counters if page mark or line feed read

      if C = Page_Mark then
         The_File.Page := The_File.Page + 1;
         The_File.Line := 1;
         The_File.Col := 1;
      elsif C = Line_Feed then
         The_File.Line := The_File.Line + 1;
         The_File.Col := 1;
      else
         The_File.Col := The_File.Col  + 1;
      end if;

      return C;
   end Get_Char;

   ----------------
   -- Upper_Case --
   ----------------

   function Upper_Case (C : Character) return Character is
      V : constant Integer := 32;

   begin
      if C in 'a' .. 'z' then
         return Character'Val (Character'Pos (C) - V);
      else
         return C;
      end if;
   end Upper_Case;

   --------------
   -- Word_Sub --
   --------------

   procedure Word_Sub
     (A : Integer;
      B : Integer;
      O : out Boolean;
      R : out Integer)
   is
   begin
      R := A - B;
      O := ((A < 0 and then B > 0) or else (A > 0 and then B < 0))
           and then ((A < 0 and then R > 0) or else (A > 0 and then R < 0));
   end Word_Sub;

   --------------
   -- Word_Mul --
   --------------

   procedure Word_Mul
     (A : Integer;
      B : Integer;
      O : out Boolean;
      R : out Integer)
   is
   begin
      if A /= 0 then
         R := A * B;
         O := (B /= R / A) or else (A = -1 and then B < 0 and then R < 0);
      else
         R := 0;
         O := False;
      end if;
   end Word_Mul;

   ----------------
   -- Put_Blanks --
   ----------------

   procedure Put_Blanks (N : Integer) is
   begin
      for J in 1 .. N loop
         Fputc (The_File.Desc, ' ');
      end loop;
   end Put_Blanks;

   ----------------
   -- Put_Buffer --
   ----------------

   procedure Put_Buffer
     (Width    : Integer;
      Pad_Type : Character;
      Length   : Integer)
   is
      Pad           : Character := Pad_Type;
      Target_Length : Integer;

   begin
      if Length >= Width then
         Target_Length := Length;
         Pad := ' ';
      else
         Target_Length := Width;
      end if;

      --  Ensure the buffer size does not exceed the line length

      if The_File.Line_Length > 0 then
         if Count (Target_Length) > The_File.Line_Length then
            raise Layout_Error; --  "Line too big"

         --  New line if does not fit on current line

         elsif The_File.Col +
           Count (Target_Length) - 1 > The_File.Line_Length
         then
            Put_Line1;
         end if;
      end if;

      --  Output data with the required padding

      if Pad = 'L' then
         Put_Blanks (Width - Length);
      end if;

      for N in 0 .. Length - 1 loop
         Fputc (The_File.Desc, Work_String (N));
      end loop;

      The_File.Col := The_File.Col + Count (Target_Length);

      if Pad = 'T' then
         Put_Blanks (Width - Length);
      end if;
   end Put_Buffer;

   -----------
   -- Getcp --
   -----------

   function Getcp return Character is
      C : Character;

   begin
      if Scanning_From_File then
         return Get_Char;
      else
         if WS_Index1 > WS_Length then
            raise End_Error;
         end if;

         WS_Index1 := WS_Index1 + 1;
         return Work_String (WS_Index1);
      end if;
   end Getcp;

   -----------
   -- Nextc --
   -----------

   function Nextc return Character is
   begin
      if Scanning_From_File then
         Load_Look_Ahead (False);
         return Char1;
      else
         if WS_Index1 < WS_Length then
            return Work_String (WS_Index1);
         else
            return Line_Feed;
         end if;
      end if;
   end Nextc;

   -----------
   -- Skipc --
   -----------

   procedure Skipc is
      C : Character;

   begin
      if Scanning_From_File then
         C := Get_Char;
      else
         WS_Index1 := WS_Index1 + 1;
      end if;
   end Skipc;

   -----------
   -- Copyc --
   -----------

   procedure Copyc is
      C : Character;

   begin
      if Scanning_From_File then
         C := Get_Char;
      else
         if WS_Index1 > WS_Length then
            raise Program_Error;
         else
            C := Work_String (WS_Index1);
            WS_Index1 := WS_Index1 + 1;
         end if;
      end if;

      Work_String (WS_Index2) := Upper_Case (C);
      WS_Index2 := WS_Index2 + 1;
   end Copyc;

   ------------------
   -- Copy_Integer --
   ------------------

   procedure Copy_Integer is
   begin
      Check_Digit;
      while Digit (Nextc) loop
         Copyc;

         if Nextc = '_' then
            Skipc;
            Check_Digit;
         end if;
      end loop;
   end Copy_Integer;

   ------------------------
   -- Copy_Based_Integer --
   ------------------------

   procedure Copy_Based_Integer is
   begin
      Check_Extended_Digit;

      while Extended_Digit (Nextc) loop
         Copyc;

         if Nextc = '_' then
            Skipc;
            Check_Extended_Digit;
         end if;
      end loop;
   end Copy_Based_Integer;

   -----------------
   -- Scan_Blanks --
   -----------------

   procedure Scan_Blanks is
      C : Character;
   begin
      if Scanning_From_File then
         loop
            Load_Look_Ahead (False);

            if Chars = 0 then
               raise End_Error;
            end if;

            C := Nextc;

            if C = ' '
              or else C = Ascii.HT
              or else C = Line_Feed
              or else C = Page_Mark
            then
               C := Getcp;
            else
               exit;
            end if;
         end loop;

      else
         while WS_Index1 <= WS_Length - 1 loop
            if Work_String (WS_Index1) = ' '
              or else Work_String (WS_Index1) = Ascii.HT
            then
               WS_Index1 := WS_Index1 + 1;
            else
               exit;
            end if;
         end loop;
      end if;
   end Scan_Blanks;

   ------------------------
   -- Setup_Fixed_Field --
   ------------------------

   procedure Setup_Fixed_Field (Width : Integer) is
      J : Integer := 0;

   begin
      loop
         Load_Look_Ahead (False);

         if Width /= J
           and then Chars /= 0
           and then Char1 /= Page_Mark
           and then Char1 /= Line_Feed
         then
            Work_String (J) := Get_Char;
            J := J + 1;
         else
            exit;
         end if;
      end loop;

      WS_Length := J;
      Scanning_From_File := False;
      WS_Index1 := 0;
   end Setup_Fixed_Field;

   --------------------------
   -- Test_Fixed_Field_End --
   --------------------------

   procedure Test_Fixed_Field_End is
   begin
      Scan_Blanks;

      if WS_Index1 < WS_Length then
         raise Data_Error;
      end if;
   end Test_Fixed_Field_End;

   -----------
   -- Alpha --
   -----------

   function Alpha (C : Character) return Boolean is
   begin
      return C in 'A' .. 'Z' or else C in 'a' .. 'z';
   end Alpha;

   --------------
   -- Alphanum --
   --------------

   function Alphanum (C : Character) return Boolean is
   begin
      return Alpha (C) or else C in '0' .. '9';
   end Alphanum;

   -------------
   -- Graphic --
   -------------

   function Graphic (C : Character) return Boolean is
   begin
      return Character'Pos (C) in 16#20# .. 16#7E#
        or else Character'Pos (C) in 16#A0# .. 16#FF#;
   end Graphic;

   -----------
   -- Digit --
   -----------

   function Digit (C : Character) return Boolean is
   begin
      return C in '0' .. '9';
   end Digit;

   --------------------
   -- Extended_Digit --
   --------------------

   function Extended_Digit (C : Character) return Boolean is
   begin
      return C in '0' .. '9' or else C in 'a' .. 'f' or else C in 'A' .. 'F';
   end Extended_Digit;

   ----------
   -- Sign --
   ----------

   function Sign (C : Character) return Boolean is
   begin
      return C = '-' or C = '+';
   end Sign;

   -----------------
   -- Check_Digit --
   -----------------

   procedure Check_Digit is
   begin
      if not (Nextc in '0' .. '9') then
         raise Data_Error;
      end if;
   end Check_Digit;

   ----------------
   -- Check_Hash --
   ----------------

   procedure Check_Hash (C : Character) is
   begin
      if Nextc /= C then
         raise Data_Error;
      end if;

      Skipc;
      Work_String (WS_Index2) := '#';
      WS_Index2 := WS_Index2 + 1;
   end Check_Hash;

   --------------------------
   -- Check_Extended_Digit --
   --------------------------

   procedure Check_Extended_Digit is
   begin
      if not Extended_Digit (Nextc) then
         raise Data_Error;
      end if;
   end Check_Extended_Digit;

   -----------------
   -- Range_Error --
   -----------------

   procedure Range_Error is
   begin
      raise Data_Error;
   end Range_Error;

   --------------
   -- Scan_Int --
   --------------

   function Scan_Int return Integer is
      Ival        : Integer := 0;
      Digit_Value : Integer;
      Overflow1   : Boolean;
      Overflow2   : Boolean;

   begin
      while WS_Index2 < WS_Length
        and then Digit (Work_String (WS_Index2))
      loop
         Digit_Value := Character'Pos (Work_String (WS_Index2))
                        - Character'Pos ('0');
         WS_Index2 := WS_Index2 + 1;
         Word_Mul (Ival, 10, Overflow1, Ival);
         Word_Sub (Ival, Digit_Value, Overflow2, Ival);

         if Overflow1 or else Overflow2 then
            while WS_Index2 < WS_Length
              and then Digit (Work_String (WS_Index2))
            loop
               WS_Index2 := WS_Index2 + 1;
            end loop;
            return 1;
         end if;
      end loop;

      return Ival;
   end Scan_Int;

   --------------------
   -- Scan_Based_Int --
   --------------------

   --  This routine scans a based Integer value fromt the string pointed by
   --  the global Integer WS_Index2. On exit WS_Index2 is updated to point
   --  to the first non-digit. The result returned is always negative. This
   --  allows the largest negative Integer value to be properly stored and
   --  converted. If overflow is detected, then the value +1 is returned to
   --  signal overflow.

   function Scan_Based_Int (Base : Integer) return Integer is
      Ival        : Integer := 0;
      Digit_Value : Integer;
      Overflow1   : Boolean;
      Overflow2   : Boolean;

   begin
      while WS_Index2 < WS_Length
        and then Extended_Digit (Work_String (WS_Index2))
      loop
         Word_Mul (Ival, Base, Overflow1, Ival);
         Digit_Value := Character'Pos (Work_String (WS_Index2))
                                       - Character'Pos ('0');
         WS_Index2 := WS_Index2 + 1;

         if Digit_Value > 9 then
            Digit_Value := Digit_Value - 7;
         end if;

         if Digit_Value >= Base then
            raise Data_Error;
         end if;

         Word_Sub (Ival, Digit_Value, Overflow2, Ival);

         if Overflow1 or else Overflow2 then
            while WS_Index2 < WS_Length
              and then Extended_Digit (Work_String (WS_Index2))
            loop
               WS_Index2 := WS_Index2 + 1;
            end loop;
            return 1;
         end if;
      end loop;

      return Ival;
   end Scan_Based_Int;

   ----------------------
   -- Scan_Integer_Val --
   ----------------------

   procedure Scan_Integer_Val (Fixed_Field : Boolean; Result : out Integer) is
      Ival     : Integer;
      Sign_Val : Character;
      C        : Character;
      Base     : Integer;
      Based    : Boolean;
      Exponent : Integer;
      Overflow : Boolean;

   begin
      --  First scan out item with the proper syntax and put it in Work_String

      WS_Index2 := 0;

      if Sign (Nextc) then
         Copyc;
      end if;

      Copy_Integer;
      C := Nextc;

      if C = '#' or else C = ':' then
         Skipc;
         Work_String (WS_Index2) := '#';
         WS_Index2 := WS_Index2 + 1;
         Copy_Based_Integer;
         Check_Hash (C);
         Based := True;
      else
         Based := False;
      end if;

      C := Nextc;

      if C = 'e' or else C = 'E' then
         Copyc;
         C := Nextc;

         if C = '+' or else C = '-' then
            Skipc;
         end if;

         Copy_Integer;

         if C = '-' then
            raise Data_Error;  --  Negative exponent in integer value
         end if;
      end if;

      if Fixed_Field then
         Test_Fixed_Field_End;
      end if;

      WS_Length := WS_Index2;
      Work_String (WS_Index2) := ' ';

      --  Now we have the Integer literal stored in Work_String

      WS_Index2 := 0;

      if Sign (Work_String (WS_Index2)) then
         Sign_Val := Work_String (WS_Index2);
         WS_Index2 := WS_Index2 + 1;
      else
         Sign_Val := '+';
      end if;

      if Based then
         Base := -Scan_Int;

         if not (Base in 2 .. 16) then
            raise Data_Error;
         end if;

         WS_Index2 := WS_Index2 + 1;
         Ival := Scan_Based_Int (Base);
         WS_Index2 := WS_Index2 + 1;

      else
         Ival := Scan_Int;
         Base := 10;
      end if;

      --  Number is in Ival (in negative form), deal with exponent.

      if Ival = 1 then
         Range_Error;
      end if;

      if Work_String (WS_Index2) = 'E' then
         WS_Index2 := WS_Index2 + 1;
         Exponent := Scan_Int;

         if Exponent < -64 or else Exponent = 1 then
            Range_Error;
         end if;

         while Exponent /= 0 loop
            Exponent := Exponent + 1;
            Word_Mul (Ival, Base, Overflow, Ival);

            if Overflow then
               Range_Error;
            end if;
         end loop;
      else
         WS_Index2 := WS_Index2 + 1;
      end if;

      if Sign_Val = '+' then
         Ival := -Ival;

         if Ival < 0 then
            Range_Error;
         end if;
      end if;

      Result := Ival;
   end Scan_Integer_Val;

   ------------------
   -- Scan_Integer --
   ------------------

   procedure Scan_Integer (Width : Integer; Result : out Integer) is
   begin
      if Width /= 0 then
         Setup_Fixed_Field (Width);
         Scan_Blanks;

         if WS_Index1 = WS_Length then
            raise Data_Error;  --  String is all blanks
         end if;

         Scan_Integer_Val (True, Result);
      else
         Scanning_From_File := True;
         Scan_Blanks;
         Scan_Integer_Val (False, Result);
      end if;
   end Scan_Integer;

   --------------------
   -- Scan_Real_Val --
   --------------------

   --  Procedure to scan a real value and return the result as a double real.
   --  A range exception is signalled if the value is out of range of allowed
   --  Ada real values, but no other range check is made.

   function Scan_Real_Val (Fixed_Field : Boolean) return LLF is
      Base         : Integer;        --  base as integer
      Based        : Boolean;        --  True if number is based
      Before_Point : Boolean;        --  True if before decimal point
      C            : Character;      --  character scanned
      Dbase        : LLF;            --  base as real
      Dig          : Integer;        --  next digit value
      Ddig         : LLF;            --  next digit as real
      Dval         : LLF;            --  value being scanned
      Exp_Sign_Val : Character;      --  sign of exponent
      Fraction     : LLF;            --  power of ten fraction after decimal pt
      Sign_Val     : Character;      --  sign of mantissa
      Exponent     : Integer;        --  value of exponent

   begin
      --  First scan out item with the proper syntax and put it in work_string

      WS_Index2 := 0;

      if Sign (Nextc) then
         Copyc;
      end if;

      Copy_Integer;
      C := Nextc;

      if C = '#' or else C = ':' then
         Skipc;
         Work_String (WS_Index2) := '#';
         WS_Index2 := WS_Index2 + 1;
         Copy_Based_Integer;

         if Nextc /= '.' then
            raise Data_Error; --  missing period in real value
         end if;

         Copyc;
         Copy_Based_Integer;
         Check_Hash (C);
         Based := True;

      else
         Based := False;

         if Nextc /= '.' then
            raise Data_Error; --  Missing period in real value
         end if;

         Copyc;
         Copy_Integer;
      end if;

      C := Nextc;

      if C = 'e' or else C = 'E' then
         Copyc;
         C := Nextc;

         if Sign (Nextc) then
            Copyc;
         end if;

         Copy_Integer;
      end if;

      if Fixed_Field then
         Test_Fixed_Field_End;
      end if;

      WS_Length := WS_Index2;

      --  Now we have the real literal stored in work_string, so prepare to
      --  convert the value, dealing first with setting the proper sign. Note
      --  that we can assume that the syntax of the literal is correct since
      --  we did all the checking above as we scanned it out.

      WS_Index2 := 0;

      if Sign (Work_String (WS_Index2)) then
         Sign_Val := Work_String (WS_Index2);
         WS_Index2 := WS_Index2 + 1;
      else
         Sign_Val := '+';
      end if;

      --  Acquire the proper base value. Note that scan_int returns the
      --  negative of the value scanned, with +1 indicating overflow which
      --  will be invalid.

      if Based then
         Base := Scan_Int;

         if Base not in -16 .. -2 then
            raise Data_Error;  --  Invalid base
         end if;

         Base := -Base;
         WS_Index2 := WS_Index2 + 1;
      else
         Base := 10;
      end if;

      Dbase := LLF (Base);

      --  Scan and convert digits

      Dval := 0.0;
      Before_Point := True;

      loop
         exit when WS_Index2 = WS_Length;

         if Work_String (WS_Index2) = '#' then
            WS_Index2 := WS_Index2 + 1;
            exit;
         end if;

         exit when (not Based) and then Work_String (WS_Index2) = 'E';
         C := Work_String (WS_Index2);
         WS_Index2 := WS_Index2 + 1;

         if C = '.' then
            Before_Point := False;
            Fraction := 1.0;
         else
            Dig := Character'Pos (C) - Character'Pos ('0');

            --  Convert hex digit

            if Dig > 9 then
               Dig := Dig - 7;
            end if;

            if Dig > Base then
               raise Data_Error; --  Digit > Base
            end if;

            Ddig := LLF (Dig);

            if Before_Point then
               Dval := Dval * Dbase + Ddig;
               --  ???
               --  if Dval > ADA_MAX_REAL then
               --     Range_Error;
               --  end if;
            else
               Fraction := Fraction / LLF (Base);
               Dval := Dval + Ddig * Fraction;
            end if;
         end if;
      end loop;

      --  Deal with exponent if present

      if Work_String (WS_Index2) = 'E' then
         WS_Index2 := WS_Index2 + 1;

         if Sign (Work_String (WS_Index2)) then
            Exp_Sign_Val := Work_String (WS_Index2);
            WS_Index2 := WS_Index2 + 1;
         else
            Exp_Sign_Val := '+';
         end if;

         Exponent := Scan_Int;

         --  A value of +1 in exponent means that scan_int detected overflow.
         --  This is not yet a range error. If the mantissa is 0 or 1, the
         --  effect is as if we had an exponent of 1.

         if Exponent = 1 then
            if Dval = 0.0 or else Dval = 1.0 then
               Exponent := 1;

            --  If we have a positive exponent, then if the mantissa is greater
            --  than 1.0, we do have an overflow, otherwise if the mantissa is
            --  less than 1.0, we have an underflow situation giving a result
            --  of zero.

            elsif Exp_Sign_Val = '+' then
               if Dval > 1.0 then
                  Range_Error;
               else
                  Dval := 0.0;
               end if;

            --  For a negative exponent, the situation is the other way round,
            --  since we want in effect the reciprocal of the value for the
            --  positive case.

            else
               if Dval > 1.0 then
                  Dval := 0.0;
               else
                  Range_Error;
               end if;
            end if;

         --  If no overflow, get abs value of exponent (scan_int returned -exp)

         else
            Exponent := -Exponent;
         end if;

         --  An optimization: if the mantissa is zero, save a lot of time
         --  in converting silly numbers like 0E+25000 by resetting exponent.

         if Dval = 0.0 then
            Exponent := 0;
         end if;

         --  Adjust mantissa by exponent, using proper exponent sign

         if Exp_Sign_Val = '+' then
            while Exponent > 0 loop
               Dval := Dval * Dbase;
               --  ???
               --  if Dval > ADA_MAX_REAL then
               --     Range_Error;
               --  end if;
               Exponent := Exponent - 1;
            end loop;
         else
            while Exponent > 0 loop
               Dval := Dval / Dbase;
               Exponent := Exponent - 1;
            end loop;
         end if;
      end if;

      --  Return scanned value with proper sign

      if Sign_Val = '+' then
         return Dval;
      else
         return -Dval;
      end if;
   end Scan_Real_Val;

   --------------------
   -- Scan_Float_Val --
   --------------------

   function Scan_Float_Val (Fixed_Field : Boolean) return LLF is
      Dval : LLF;

   begin
      Dval := Scan_Real_Val (Fixed_Field);
      --  ??? Check that value is in range. Unimplemented for now.
      return LLF (Dval);
   end Scan_Float_Val;

   ----------------
   -- Scan_Float --
   ----------------

   function Scan_Float (Width : Natural) return LLF is
      Result : LLF;

   begin
      if Width /= 0 then
         Setup_Fixed_Field (Width);
         Scan_Blanks;

         if WS_Index1 = WS_Length then
            raise Data_Error; --  String is all blanks
         end if;

         Result := Scan_Float_Val (True);
      else
         Scanning_From_File := True;
         Scan_Blanks;
         Result := Scan_Float_Val (False);
      end if;

      return Result;
   end Scan_Float;

   ---------------
   -- Scan_Enum --
   ---------------

   procedure Scan_Enum (Last : out Natural) is
   begin
      Scan_Blanks;

      if not Scanning_From_File and then WS_Index1 = WS_Length then
         raise End_Error;  --  String is all blanks
      end if;

      WS_Index2 := 0;

      --  Try identifier

      if Alpha (Nextc) then
         while Alphanum (Nextc) loop
            Copyc;

            if Nextc = '_' then
               Copyc;
            end if;
         end loop;

      elsif Nextc = ''' then

      --  Look for an ending quote.

         Copyc;

         if Graphic (Nextc) then
            Work_String (WS_Index2) := Getcp;
            WS_Index2 := WS_Index2 + 1;

            if Nextc = ''' then
               Copyc;
            end if;
         else
            raise Data_Error;
         end if;

      else
         raise Data_Error;
      end if;

      WS_Length := WS_Index2;
      Last := WS_Index1;
   end Scan_Enum;

   --  The closing of all open files and deletion of temporary files is an
   --  action which takes place at the end of execution of the main program.
   --  This action can be implemented using a library level object which
   --  gets finalized at the end of the main program execution. Below, a
   --  controlled type is introduced and an object is declared of this type
   --  for this purpose. The Finalize operation associated with this type
   --  will do all the necessary work.

   type Finalizable_Type is new Controlled with null record;
   procedure Finalize (V : in out Finalizable_Type);

   Finalizable_Object : Finalizable_Type;

   --------------
   -- Finalize --
   --------------

   procedure Finalize (V : in out Finalizable_Type) is
   begin
      --  Close all open files except stdin, stdout and stderr

      for J in 4 .. Open_Files'Last loop
         if Open_Files (J) /= null
           and then Open_Files (J).AFCB_In_Use
           and then Open_Files (J).Mode /= In_File
         then
            Close_File;
         end if;
      end loop;

      --  Delete temporary files upon completion of the main program

      while (Temp_Files /= null) loop
         Unlink (Temp_Files.File_Name.all);
         Temp_Files := Temp_Files.Next;
      end loop;
   end Finalize;

   -----------
   -- Fopen --
   -----------

   function Fopen (Name : String; Typ : File_Mode) return Text_IO.File_Ptr is
      function C_Fopen (Name, Typ : Address) return Text_IO.File_Ptr;
      pragma Import (C, C_Fopen, "fopen");

      Name1       : String (Name'First .. Name'Last + 1);
      Append_Only : constant String := "at" & Ascii.NUL;
      Read_Only   : constant String := "rt" & Ascii.NUL;
      Write_Only  : constant String := "wt" & Ascii.NUL;

   begin
      Name1 (Name'Range) := Name;
      Name1 (Name1'Last) := Nul;

      if Typ = In_File then
         return C_Fopen (Name1'Address, Read_Only'Address);
      elsif Typ = Out_File then
         return C_Fopen (Name1'Address, Write_Only'Address);
      else  --  Append_File
         return C_Fopen (Name1'Address, Append_Only'Address);
      end if;
   end Fopen;

   ------------
   -- Fclose --
   ------------

   procedure Fclose (P : Text_IO.File_Ptr) is
      procedure C_Fclose (P : Text_IO.File_Ptr);
      pragma Import (C, C_Fclose, "fclose");

   begin
      C_Fclose (P);
   end Fclose;

   ------------
   -- Unlink --
   ------------

   procedure Unlink (Name : String) is
      procedure C_Unlink (Name : Address);
      pragma Import (C, C_Unlink, "unlink");

      Name1 : String (Name'First .. Name'Last + 1);

   begin
      Name1 (Name'Range) := Name;
      Name1 (Name1'Last) := Nul;
      C_Unlink (Name1'Address);
   end Unlink;

   -----------------
   -- Is_Keyboard --
   -----------------

   function Is_Keyboard (F : Text_IO.File_Type) return Boolean is
   begin
      return F.Is_Keyboard;
   end Is_Keyboard;

   ------------
   -- Isatty --
   ------------

   function Isatty (F : Text_IO.File_Ptr) return Boolean is
      function C_Isatty (I : Integer) return Boolean;
      pragma Import (C, C_Isatty, "isatty");

      function C_Fileno (F : Text_IO.File_Ptr) return Integer;
      pragma Import (C, C_Fileno, "fileno");

   begin
      return C_Isatty (C_Fileno (F));
   end Isatty;

   -------------
   -- C_Fgetc --
   -------------

   procedure C_Fgetc
     (F      : Text_IO.File_Ptr;
      C      : out Character;
      Is_Eof : out Boolean)
   is
      I      : Integer;
      function Fgetc (F : Text_IO.File_Ptr) return Integer;
      pragma Import (C, Fgetc, "fgetc");

   begin
      I := Fgetc (F);
      Is_Eof := I = -1;

      if not Is_Eof then
         C := Character'Val (I);
      end if;
   end C_Fgetc;

   -------------
   -- C_Fputc --
   -------------

   procedure Fputc (F : Text_IO.File_Ptr; C : Character) is
      procedure C_Fputc (C : Character; F : Text_IO.File_Ptr);
      pragma Import (C, C_Fputc, "fputc");

   begin
      C_Fputc (C, F);
   end Fputc;

   -----------
   -- Stdin --
   ------------

   function Stdin return Text_IO.File_Ptr is
      function C_Stdin return Text_IO.File_Ptr;
      pragma Import (C, C_Stdin);

   begin
      return C_Stdin;
   end Stdin;

   ------------
   -- Stdout --
   ------------

   function Stdout return Text_IO.File_Ptr is
      function C_Stdout return Text_IO.File_Ptr;
      pragma Import (C, C_Stdout);

   begin
      return C_Stdout;
   end Stdout;

   ------------
   -- Stderr --
   ------------

   function Stderr return Text_IO.File_Ptr is
      function C_Stderr return Text_IO.File_Ptr;
      pragma Import (C, C_Stderr);

   begin
      return C_Stderr;
   end Stderr;

   -------------------
   -- Unimplemented --
   -------------------

   procedure Unimplemented (Message : String) is
   begin
      Text_IO.Put (Message);
      Text_IO.Put_Line (" not implemented yet");
   end Unimplemented;

begin
   --  Initialization of Standard Input

   Standard_In := new AFCB'(AFCB_In_Use => True,
     Desc => Stdin,
     Name => new String'("Standard_Input"),
     Form => new String'("rt"),
     Mode => In_File,
     Col  => 1,
     Line => 1,
     Page => 1,
     Line_Length => 0,
     Page_Length => 0,
     Count => 0,
     Is_Keyboard => Isatty (Stdin),
     Look_Ahead => "   ");

   --  Initialization of Standard Output

   Standard_Out := new AFCB'(AFCB_In_Use => True,
     Desc => Stdout,
     Name => new String'("Standard_Output"),
     Form => new String'("wt"),
     Mode => Out_File,
     Col  => 1,
     Line => 1,
     Page => 1,
     Line_Length => 0,
     Page_Length => 0,
     Count => 0,
     Is_Keyboard => False,
     Look_Ahead => "   ");

   --  Initialization of Standard Error

   Standard_Err := new AFCB'(AFCB_In_Use => True,
     Desc => Stderr,
     Name => new String'("Standard_Error"),
     Form => new String'("wt"),
     Mode => Out_File,
     Col  => 1,
     Line => 1,
     Page => 1,
     Line_Length => 0,
     Page_Length => 0,
     Count => 0,
     Is_Keyboard => False,
     Look_Ahead => "   ");

   Current_In  := Standard_In;
   Current_Out := Standard_Out;
   Current_Err := Standard_Err;

   Open_Files (Open_Files'First + 0) := Standard_In;
   Open_Files (Open_Files'First + 1) := Standard_Out;
   Open_Files (Open_Files'First + 2) := Standard_Err;

end Ada.Text_IO.Aux;
