------------------------------------------------------------------------------ -- -- -- GNAT RUNTIME COMPONENTS -- -- -- -- A D A . S T R E A M S . S T R E A M _ I O -- -- -- -- B o d y -- -- -- -- Copyright (C) 1992-2003, 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- -- -- 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 -- -- 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 -- -- 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. -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Interfaces.C_Streams; use Interfaces.C_Streams; with System; use System; with System.File_IO; with System.Soft_Links; with System.CRTL; with Unchecked_Conversion; with Unchecked_Deallocation; package body Ada.Streams.Stream_IO is package FIO renames System.File_IO; package SSL renames System.Soft_Links; subtype AP is FCB.AFCB_Ptr; function To_FCB is new Unchecked_Conversion (File_Mode, FCB.File_Mode); function To_SIO is new Unchecked_Conversion (FCB.File_Mode, File_Mode); use type FCB.File_Mode; use type FCB.Shared_Status_Type; ----------------------- -- Local Subprograms -- ----------------------- procedure Set_Position (File : in File_Type); -- Sets file position pointer according to value of current index ------------------- -- AFCB_Allocate -- ------------------- function AFCB_Allocate (Control_Block : Stream_AFCB) return FCB.AFCB_Ptr is pragma Warnings (Off, Control_Block); begin return new Stream_AFCB; end AFCB_Allocate; ---------------- -- AFCB_Close -- ---------------- -- No special processing required for closing Stream_IO file procedure AFCB_Close (File : access Stream_AFCB) is pragma Warnings (Off, File); begin null; end AFCB_Close; --------------- -- AFCB_Free -- --------------- procedure AFCB_Free (File : access Stream_AFCB) is type FCB_Ptr is access all Stream_AFCB; FT : FCB_Ptr := FCB_Ptr (File); procedure Free is new Unchecked_Deallocation (Stream_AFCB, FCB_Ptr); begin Free (FT); end AFCB_Free; ----------- -- Close -- ----------- procedure Close (File : in out File_Type) is begin FIO.Close (AP (File)); end Close; ------------ -- Create -- ------------ procedure Create (File : in out File_Type; Mode : in File_Mode := Out_File; Name : in String := ""; Form : in String := "") is Dummy_File_Control_Block : Stream_AFCB; pragma Warnings (Off, Dummy_File_Control_Block); -- Yes, we know this is never assigned a value, only the tag -- is used for dispatching purposes, so that's expected. begin FIO.Open (File_Ptr => AP (File), Dummy_FCB => Dummy_File_Control_Block, Mode => To_FCB (Mode), Name => Name, Form => Form, Amethod => 'S', Creat => True, Text => False); File.Last_Op := Op_Write; end Create; ------------ -- Delete -- ------------ procedure Delete (File : in out File_Type) is begin FIO.Delete (AP (File)); end Delete; ----------------- -- End_Of_File -- ----------------- function End_Of_File (File : in File_Type) return Boolean is begin FIO.Check_Read_Status (AP (File)); return Count (File.Index) > Size (File); end End_Of_File; ----------- -- Flush -- ----------- procedure Flush (File : File_Type) is begin FIO.Flush (AP (File)); end Flush; ---------- -- Form -- ---------- function Form (File : in File_Type) return String is begin return FIO.Form (AP (File)); end Form; ----------- -- Index -- ----------- function Index (File : in File_Type) return Positive_Count is begin FIO.Check_File_Open (AP (File)); return Count (File.Index); end Index; ------------- -- Is_Open -- ------------- function Is_Open (File : in File_Type) return Boolean is begin return FIO.Is_Open (AP (File)); end Is_Open; ---------- -- Mode -- ---------- function Mode (File : in File_Type) return File_Mode is begin return To_SIO (FIO.Mode (AP (File))); end Mode; ---------- -- Name -- ---------- function Name (File : in File_Type) return String is begin return FIO.Name (AP (File)); end Name; ---------- -- Open -- ---------- procedure Open (File : in out File_Type; Mode : in File_Mode; Name : in String; Form : in String := "") is Dummy_File_Control_Block : Stream_AFCB; pragma Warnings (Off, Dummy_File_Control_Block); -- Yes, we know this is never assigned a value, only the tag -- is used for dispatching purposes, so that's expected. begin FIO.Open (File_Ptr => AP (File), Dummy_FCB => Dummy_File_Control_Block, Mode => To_FCB (Mode), Name => Name, Form => Form, Amethod => 'S', Creat => False, Text => False); -- Ensure that the stream index is set properly (e.g., for Append_File) Reset (File, Mode); -- Set last operation. The purpose here is to ensure proper handling -- of the initial operation. In general, a write after a read requires -- resetting and doing a seek, so we set the last operation as Read -- for an In_Out file, but for an Out file we set the last operation -- to Op_Write, since in this case it is not necessary to do a seek -- (and furthermore there are situations (such as the case of writing -- a sequential Posix FIFO file) where the lseek would cause problems. if Mode = Out_File then File.Last_Op := Op_Write; else File.Last_Op := Op_Read; end if; end Open; ---------- -- Read -- ---------- procedure Read (File : in File_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset; From : in Positive_Count) is begin Set_Index (File, From); Read (File, Item, Last); end Read; procedure Read (File : in File_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is Nread : size_t; begin FIO.Check_Read_Status (AP (File)); -- If last operation was not a read, or if in file sharing mode, -- then reset the physical pointer of the file to match the index -- We lock out task access over the two operations in this case. if File.Last_Op /= Op_Read or else File.Shared_Status = FCB.Yes then Locked_Processing : begin SSL.Lock_Task.all; Set_Position (File); FIO.Read_Buf (AP (File), Item'Address, Item'Length, Nread); SSL.Unlock_Task.all; exception when others => SSL.Unlock_Task.all; raise; end Locked_Processing; else FIO.Read_Buf (AP (File), Item'Address, Item'Length, Nread); end if; File.Index := File.Index + Count (Nread); Last := Item'First + Stream_Element_Offset (Nread) - 1; File.Last_Op := Op_Read; end Read; -- This version of Read is the primitive operation on the underlying -- Stream type, used when a Stream_IO file is treated as a Stream procedure Read (File : in out Stream_AFCB; Item : out Ada.Streams.Stream_Element_Array; Last : out Ada.Streams.Stream_Element_Offset) is begin Read (File'Unchecked_Access, Item, Last); end Read; ----------- -- Reset -- ----------- procedure Reset (File : in out File_Type; Mode : in File_Mode) is begin FIO.Check_File_Open (AP (File)); -- Reset file index to start of file for read/write cases. For -- the append case, the Set_Mode call repositions the index. File.Index := 1; Set_Mode (File, Mode); end Reset; procedure Reset (File : in out File_Type) is begin Reset (File, To_SIO (File.Mode)); end Reset; --------------- -- Set_Index -- --------------- procedure Set_Index (File : in File_Type; To : in Positive_Count) is begin FIO.Check_File_Open (AP (File)); File.Index := Count (To); File.Last_Op := Op_Other; end Set_Index; -------------- -- Set_Mode -- -------------- procedure Set_Mode (File : in out File_Type; Mode : in File_Mode) is begin FIO.Check_File_Open (AP (File)); -- If we are switching from read to write, or vice versa, and -- we are not already open in update mode, then reopen in update -- mode now. Note that we can use Inout_File as the mode for the -- call since File_IO handles all modes for all file types. if ((File.Mode = FCB.In_File) /= (Mode = In_File)) and then not File.Update_Mode then FIO.Reset (AP (File), FCB.Inout_File); File.Update_Mode := True; end if; -- Set required mode and position to end of file if append mode File.Mode := To_FCB (Mode); FIO.Append_Set (AP (File)); if File.Mode = FCB.Append_File then File.Index := Count (ftell (File.Stream)) + 1; end if; File.Last_Op := Op_Other; end Set_Mode; ------------------ -- Set_Position -- ------------------ procedure Set_Position (File : in File_Type) is use type System.CRTL.long; begin if fseek (File.Stream, System.CRTL.long (File.Index) - 1, SEEK_SET) /= 0 then raise Use_Error; end if; end Set_Position; ---------- -- Size -- ---------- function Size (File : in File_Type) return Count is begin FIO.Check_File_Open (AP (File)); if File.File_Size = -1 then File.Last_Op := Op_Other; if fseek (File.Stream, 0, SEEK_END) /= 0 then raise Device_Error; end if; File.File_Size := Stream_Element_Offset (ftell (File.Stream)); end if; return Count (File.File_Size); end Size; ------------ -- Stream -- ------------ function Stream (File : in File_Type) return Stream_Access is begin FIO.Check_File_Open (AP (File)); return Stream_Access (File); end Stream; ----------- -- Write -- ----------- procedure Write (File : in File_Type; Item : in Stream_Element_Array; To : in Positive_Count) is begin Set_Index (File, To); Write (File, Item); end Write; procedure Write (File : in File_Type; Item : in Stream_Element_Array) is begin FIO.Check_Write_Status (AP (File)); -- If last operation was not a write, or if in file sharing mode, -- then reset the physical pointer of the file to match the index -- We lock out task access over the two operations in this case. if File.Last_Op /= Op_Write or else File.Shared_Status = FCB.Yes then Locked_Processing : begin SSL.Lock_Task.all; Set_Position (File); FIO.Write_Buf (AP (File), Item'Address, Item'Length); SSL.Unlock_Task.all; exception when others => SSL.Unlock_Task.all; raise; end Locked_Processing; else FIO.Write_Buf (AP (File), Item'Address, Item'Length); end if; File.Index := File.Index + Item'Length; File.Last_Op := Op_Write; File.File_Size := -1; end Write; -- This version of Write is the primitive operation on the underlying -- Stream type, used when a Stream_IO file is treated as a Stream procedure Write (File : in out Stream_AFCB; Item : in Ada.Streams.Stream_Element_Array) is begin Write (File'Unchecked_Access, Item); end Write; end Ada.Streams.Stream_IO;