OSDN Git Service

2008-08-08 Robert Dewar <dewar@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-fileio.adb
index 9028fd6..7c20fb1 100644 (file)
@@ -6,7 +6,7 @@
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---          Copyright (C) 1992-2003 Free Software Foundation, Inc.          --
+--          Copyright (C) 1992-2008, 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- --
@@ -16,8 +16,8 @@
 -- 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.                                                      --
+-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
+-- Boston, MA 02110-1301, 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, --
 
 with Ada.Finalization;            use Ada.Finalization;
 with Ada.IO_Exceptions;           use Ada.IO_Exceptions;
+with Interfaces.C;
 with Interfaces.C_Streams;        use Interfaces.C_Streams;
+
+with System.CRTL;
+with System.Case_Util;            use System.Case_Util;
+with System.OS_Constants;
+with System.OS_Lib;
 with System.Soft_Links;
-with Unchecked_Deallocation;
+
+with Ada.Unchecked_Deallocation;
 
 package body System.File_IO is
 
@@ -43,6 +50,9 @@ package body System.File_IO is
 
    package SSL renames System.Soft_Links;
 
+   use type Interfaces.C.int;
+   use type System.CRTL.size_t;
+
    ----------------------
    -- Global Variables --
    ----------------------
@@ -57,7 +67,7 @@ package body System.File_IO is
    type Temp_File_Record_Ptr is access all Temp_File_Record;
 
    type Temp_File_Record is record
-      Name : String (1 .. L_tmpnam + 1);
+      Name : String (1 .. max_path_len + 1);
       Next : Temp_File_Record_Ptr;
    end record;
    --  One of these is allocated for each temporary file created
@@ -74,7 +84,7 @@ package body System.File_IO is
    --  a controlled type introduced for this purpose.
 
    procedure Finalize (V : in out File_IO_Clean_Up_Type);
-   --  This is the finalize operation that is used to do the cleanup.
+   --  This is the finalize operation that is used to do the cleanup
 
    File_IO_Clean_Up_Object : File_IO_Clean_Up_Type;
    pragma Warnings (Off, File_IO_Clean_Up_Object);
@@ -83,15 +93,24 @@ package body System.File_IO is
    --  environment task is finalized.
 
    text_translation_required : Boolean;
+   for text_translation_required'Size use Character'Size;
    pragma Import
      (C, text_translation_required, "__gnat_text_translation_required");
-   --  If true, add appropriate suffix to control string for Open.
+   --  If true, add appropriate suffix to control string for Open
+
+   function Get_Case_Sensitive return Integer;
+   pragma Import (C, Get_Case_Sensitive,
+                  "__gnat_get_file_names_case_sensitive");
+   File_Names_Case_Sensitive : constant Boolean := Get_Case_Sensitive /= 0;
+   --  Set to indicate whether the operating system convention is for file
+   --  names to be case sensitive (e.g., in Unix, set True), or non case
+   --  sensitive (e.g., in OS/2, set False).
 
    -----------------------
    -- Local Subprograms --
    -----------------------
 
-   procedure Free_String is new Unchecked_Deallocation (String, Pstring);
+   procedure Free_String is new Ada.Unchecked_Deallocation (String, Pstring);
 
    subtype Fopen_String is String (1 .. 4);
    --  Holds open string (longest is "w+b" & nul)
@@ -191,18 +210,19 @@ package body System.File_IO is
    -- Close --
    -----------
 
-   procedure Close (File : in out AFCB_Ptr) is
+   procedure Close (File_Ptr : access AFCB_Ptr) is
       Close_Status : int := 0;
       Dup_Strm     : Boolean := False;
+      File         : AFCB_Ptr renames File_Ptr.all;
 
    begin
-      Check_File_Open (File);
-      AFCB_Close (File);
-
       --  Take a task lock, to protect the global data value Open_Files
 
       SSL.Lock_Task.all;
 
+      Check_File_Open (File);
+      AFCB_Close (File);
+
       --  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 and also do
@@ -281,7 +301,8 @@ package body System.File_IO is
    -- Delete --
    ------------
 
-   procedure Delete (File : in out AFCB_Ptr) is
+   procedure Delete (File_Ptr : access AFCB_Ptr) is
+      File : AFCB_Ptr renames File_Ptr.all;
    begin
       Check_File_Open (File);
 
@@ -293,7 +314,7 @@ package body System.File_IO is
          Filename : aliased constant String := File.Name.all;
 
       begin
-         Close (File);
+         Close (File_Ptr);
 
          --  Now unlink the external file. Note that we use the full name
          --  in this unlink, because the working directory may have changed
@@ -339,7 +360,7 @@ package body System.File_IO is
    procedure Finalize (V : in out File_IO_Clean_Up_Type) is
       pragma Warnings (Off, V);
 
-      Fptr1   : AFCB_Ptr;
+      Fptr1   : aliased AFCB_Ptr;
       Fptr2   : AFCB_Ptr;
 
       Discard : int;
@@ -356,7 +377,7 @@ package body System.File_IO is
       Fptr1 := Open_Files;
       while Fptr1 /= null loop
          Fptr2 := Fptr1.Next;
-         Close (Fptr1);
+         Close (Fptr1'Access);
          Fptr1 := Fptr2;
       end loop;
 
@@ -432,7 +453,7 @@ package body System.File_IO is
       Amethod : Character;
       Fopstr  : out Fopen_String)
    is
-      Fptr  : Positive;
+      Fptr : Positive;
 
    begin
       case Mode is
@@ -488,7 +509,7 @@ package body System.File_IO is
    -- Form --
    ----------
 
-   function Form (File : in AFCB_Ptr) return String is
+   function Form (File : AFCB_Ptr) return String is
    begin
       if File = null then
          raise Status_Error;
@@ -508,6 +529,7 @@ package body System.File_IO is
       return    Boolean
    is
       V1, V2 : Natural;
+      pragma Unreferenced (V2);
 
    begin
       Form_Parameter (Form, Keyword, V1, V2);
@@ -604,9 +626,15 @@ package body System.File_IO is
    -- Is_Open --
    -------------
 
-   function Is_Open (File : in AFCB_Ptr) return Boolean is
+   function Is_Open (File : AFCB_Ptr) return Boolean is
    begin
-      return (File /= null);
+      --  We return True if the file is open, and the underlying file stream is
+      --  usable. In particular on Windows an application linked with -mwindows
+      --  option set does not have a console attached. In this case standard
+      --  files (Current_Output, Current_Error, Current_Input) are not created.
+      --  We want Is_Open (Current_Output) to return False in this case.
+
+      return File /= null and then fileno (File.Stream) /= -1;
    end Is_Open;
 
    -------------------
@@ -655,7 +683,7 @@ package body System.File_IO is
    -- Mode --
    ----------
 
-   function Mode (File : in AFCB_Ptr) return File_Mode is
+   function Mode (File : AFCB_Ptr) return File_Mode is
    begin
       if File = null then
          raise Status_Error;
@@ -668,7 +696,7 @@ package body System.File_IO is
    -- Name --
    ----------
 
-   function Name (File : in AFCB_Ptr) return String is
+   function Name (File : AFCB_Ptr) return String is
    begin
       if File = null then
          raise Status_Error;
@@ -683,7 +711,7 @@ package body System.File_IO is
 
    procedure Open
      (File_Ptr  : in out AFCB_Ptr;
-      Dummy_FCB : in AFCB'Class;
+      Dummy_FCB : AFCB'Class;
       Mode      : File_Mode;
       Name      : String;
       Form      : String;
@@ -698,7 +726,7 @@ package body System.File_IO is
 
       procedure Tmp_Name (Buffer : Address);
       pragma Import (C, Tmp_Name, "__gnat_tmp_name");
-      --  set buffer (a String address) with a temporary filename.
+      --  set buffer (a String address) with a temporary filename
 
       Stream : FILEs := C_Stream;
       --  Stream which we open in response to this request
@@ -730,6 +758,9 @@ package body System.File_IO is
       Full_Name_Len : Integer;
       --  Length of name actually stored in Fullname
 
+      Encoding : System.CRTL.Filename_Encoding;
+      --  Filename encoding specified into the form parameter
+
    begin
       if File_Ptr /= null then
          raise Status_Error;
@@ -770,19 +801,41 @@ package body System.File_IO is
          end if;
       end;
 
+      --  Acquire setting of shared parameter
+
+      declare
+         V1, V2 : Natural;
+
+      begin
+         Form_Parameter (Formstr, "encoding", V1, V2);
+
+         if V1 = 0 then
+            Encoding := System.CRTL.UTF8;
+
+         elsif Formstr (V1 .. V2) = "utf8" then
+            Encoding := System.CRTL.UTF8;
+
+         elsif Formstr (V1 .. V2) = "8bits" then
+            Encoding := System.CRTL.ASCII_8bits;
+
+         else
+            raise Use_Error;
+         end if;
+      end;
+
       --  If we were given a stream (call from xxx.C_Streams.Open), then set
-      --  full name to null and that is all we have to do in this case so
-      --  skip to end of processing.
+      --  the full name to the given one, and skip to end of processing.
 
       if Stream /= NULL_Stream then
-         Fullname (1) := ASCII.Nul;
-         Full_Name_Len := 1;
+         Full_Name_Len := Name'Length + 1;
+         Fullname (1 .. Full_Name_Len - 1) := Name;
+         Fullname (Full_Name_Len) := ASCII.NUL;
 
       --  Normal case of Open or Create
 
       else
-         --  If temporary file case, get temporary file name and add
-         --  to the list of temporary files to be deleted on exit.
+         --  If temporary file case, get temporary file name and add to the
+         --  list of temporary files to be deleted on exit.
 
          if Tempfile then
             if not Creat then
@@ -812,11 +865,15 @@ package body System.File_IO is
          --  Normal case of non-null name given
 
          else
+            if Name'Length > Namelen then
+               raise Name_Error;
+            end if;
+
             Namestr (1 .. Name'Length) := Name;
             Namestr (Name'Length + 1)  := ASCII.NUL;
          end if;
 
-         --  Get full name in accordance with the advice of RM A.8.2(22).
+         --  Get full name in accordance with the advice of RM A.8.2(22)
 
          full_name (Namestr'Address, Fullname'Address);
 
@@ -831,6 +888,17 @@ package body System.File_IO is
             Full_Name_Len := Full_Name_Len + 1;
          end loop;
 
+         --  Fullname is generated by calling system's full_name. The problem
+         --  is, full_name does nothing about the casing, so a file name
+         --  comparison may generally speaking not be valid on non-case
+         --  sensitive systems, and in particular we get unexpected failures
+         --  on Windows/Vista because of this. So we use s-casuti to force
+         --  the name to lower case.
+
+         if not File_Names_Case_Sensitive then
+            To_Lower (Fullname (1 .. Full_Name_Len));
+         end if;
+
          --  If Shared=None or Shared=Yes, then check for the existence
          --  of another file with exactly the same full name.
 
@@ -850,9 +918,9 @@ package body System.File_IO is
                   if Fullname (1 .. Full_Name_Len) = P.Name.all then
 
                      --  If we get a match, and either file has Shared=None,
-                     --  then raise Use_Error, since we don't allow two
-                     --  files of the same name to be opened unless they
-                     --  specify the required sharing mode.
+                     --  then raise Use_Error, since we don't allow two files
+                     --  of the same name to be opened unless they specify the
+                     --  required sharing mode.
 
                      if Shared = None
                        or else P.Shared_Status = None
@@ -868,13 +936,12 @@ package body System.File_IO is
                         Stream := P.Stream;
                         exit;
 
-                     --  Otherwise one of the files has Shared=Yes and one
-                     --  has Shared=No. If the current file has Shared=No
-                     --  then all is well but we don't want to share any
-                     --  other file's stream. If the current file has
-                     --  Shared=Yes, we would like to share a stream, but
-                     --  not from a file that has Shared=No, so in either
-                     --  case we just keep going on the search.
+                     --  Otherwise one of the files has Shared=Yes and one has
+                     --  Shared=No. If the current file has Shared=No then all
+                     --  is well but we don't want to share any other file's
+                     --  stream. If the current file has Shared=Yes, we would
+                     --  like to share a stream, but not from a file that has
+                     --  Shared=No, so either way, we just continue the search.
 
                      else
                         null;
@@ -898,33 +965,36 @@ package body System.File_IO is
          if Stream = NULL_Stream then
             Fopen_Mode (Mode, Text, Creat, Amethod, Fopstr);
 
-            --  A special case, if we are opening (OPEN case) a file and
-            --  the mode returned by Fopen_Mode is not "r" or "r+", then
-            --  we first make sure that the file exists as required by
-            --  Ada semantics.
+            --  A special case, if we are opening (OPEN case) a file and the
+            --  mode returned by Fopen_Mode is not "r" or "r+", then we first
+            --  make sure that the file exists as required by Ada semantics.
 
-            if Creat = False and then Fopstr (1) /= 'r' then
+            if not Creat and then Fopstr (1) /= 'r' then
                if file_exists (Namestr'Address) = 0 then
                   raise Name_Error;
                end if;
             end if;
 
-            --  Now open the file. Note that we use the name as given
-            --  in the original Open call for this purpose, since that
-            --  seems the clearest implementation of the intent. It
-            --  would presumably work to use the full name here, but
-            --  if there is any difference, then we should use the
-            --  name used in the call.
+            --  Now open the file. Note that we use the name as given in the
+            --  original Open call for this purpose, since that seems the
+            --  clearest implementation of the intent. It would presumably
+            --  work to use the full name here, but if there is any difference,
+            --  then we should use the name used in the call.
 
-            --  Note: for a corresponding delete, we will use the
-            --  full name, since by the time of the delete, the
-            --  current working directory may have changed and
-            --  we do not want to delete a different file!
+            --  Note: for a corresponding delete, we will use the full name,
+            --  since by the time of the delete, the current working directory
+            --  may have changed and we do not want to delete a different file!
 
-            Stream := fopen (Namestr'Address, Fopstr'Address);
+            Stream := fopen (Namestr'Address, Fopstr'Address, Encoding);
 
             if Stream = NULL_Stream then
-               if file_exists (Namestr'Address) = 0 then
+
+               --  Raise Name_Error if trying to open a non-existent file.
+               --  Otherwise raise Use_Error.
+
+               --  Should we raise Device_Error for ENOSPC???
+
+               if System.OS_Lib.Errno = System.OS_Constants.ENOENT then
                   raise Name_Error;
                else
                   raise Use_Error;
@@ -939,18 +1009,17 @@ package body System.File_IO is
 
       File_Ptr := AFCB_Allocate (Dummy_FCB);
 
-      File_Ptr.Is_Regular_File   := (is_regular_file
-                                      (fileno (Stream)) /= 0);
+      File_Ptr.Is_Regular_File   := (is_regular_file (fileno (Stream)) /= 0);
       File_Ptr.Is_System_File    := False;
       File_Ptr.Is_Text_File      := Text;
       File_Ptr.Shared_Status     := Shared;
       File_Ptr.Access_Method     := Amethod;
       File_Ptr.Stream            := Stream;
       File_Ptr.Form              := new String'(Formstr);
-      File_Ptr.Name              := new String'(Fullname
-                                                 (1 .. Full_Name_Len));
+      File_Ptr.Name              := new String'(Fullname (1 .. Full_Name_Len));
       File_Ptr.Mode              := Mode;
       File_Ptr.Is_Temporary_File := Tempfile;
+      File_Ptr.Encoding          := Encoding;
 
       Chain_File (File_Ptr);
       Append_Set (File_Ptr);
@@ -984,7 +1053,7 @@ package body System.File_IO is
    procedure Read_Buf
      (File  : AFCB_Ptr;
       Buf   : Address;
-      Siz   : in Interfaces.C_Streams.size_t;
+      Siz   : Interfaces.C_Streams.size_t;
       Count : out Interfaces.C_Streams.size_t)
    is
    begin
@@ -999,31 +1068,35 @@ package body System.File_IO is
    -- Reset --
    -----------
 
-   --  The reset which does not change the mode simply does a rewind.
+   --  The reset which does not change the mode simply does a rewind
 
-   procedure Reset (File : in out AFCB_Ptr) is
+   procedure Reset (File_Ptr : access AFCB_Ptr) is
+      File : AFCB_Ptr renames File_Ptr.all;
    begin
       Check_File_Open (File);
-      Reset (File, File.Mode);
+      Reset (File_Ptr, File.Mode);
    end Reset;
 
    --  The reset with a change in mode is done using freopen, and is
    --  not permitted except for regular files (since otherwise there
    --  is no name for the freopen, and in any case it seems meaningless)
 
-   procedure Reset (File : in out AFCB_Ptr; Mode : in File_Mode) is
+   procedure Reset (File_Ptr : access AFCB_Ptr; Mode : File_Mode) is
+      File   : AFCB_Ptr renames File_Ptr.all;
       Fopstr : aliased Fopen_String;
 
    begin
       Check_File_Open (File);
 
-      --  Change of mode not allowed for shared file or file with no name
-      --  or file that is not a regular file, or for a system file.
+      --  Change of mode not allowed for shared file or file with no name or
+      --  file that is not a regular file, or for a system file. Note that we
+      --  allow the "change" of mode if it is not in fact doing a change.
 
-      if File.Shared_Status = Yes
-        or else File.Name'Length <= 1
-        or else File.Is_System_File
-        or else (not File.Is_Regular_File)
+      if Mode /= File.Mode
+        and then (File.Shared_Status = Yes
+                   or else File.Name'Length <= 1
+                   or else File.Is_System_File
+                   or else not File.Is_Regular_File)
       then
          raise Use_Error;
 
@@ -1043,11 +1116,11 @@ package body System.File_IO is
          Fopen_Mode
            (Mode, File.Is_Text_File, False, File.Access_Method, Fopstr);
 
-         File.Stream :=
-           freopen (File.Name.all'Address, Fopstr'Address, File.Stream);
+         File.Stream := freopen
+           (File.Name.all'Address, Fopstr'Address, File.Stream, File.Encoding);
 
          if File.Stream = NULL_Stream then
-            Close (File);
+            Close (File_Ptr);
             raise Use_Error;
 
          else