home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / gnat-2.06-src.tgz / tar.out / fsf / gnat / ada / s-fileio.adb < prev    next >
Text File  |  1996-09-28  |  27KB  |  911 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUN-TIME COMPONENTS                         --
  4. --                                                                          --
  5. --                       S Y S T E M . F I L E _ I O                        --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.23 $                             --
  10. --                                                                          --
  11. --        Copyright (c) 1992,1993,1994,1995 NYU, All Rights Reserved        --
  12. --                                                                          --
  13. -- The GNAT library is free software; you can redistribute it and/or modify --
  14. -- it under terms of the GNU Library General Public License as published by --
  15. -- the Free Software  Foundation; either version 2, or (at your option) any --
  16. -- later version.  The GNAT library is distributed in the hope that it will --
  17. -- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
  18. -- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
  19. -- Library  General  Public  License for  more  details.  You  should  have --
  20. -- received  a copy of the GNU  Library  General Public License  along with --
  21. -- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
  22. -- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
  23. --                                                                          --
  24. ------------------------------------------------------------------------------
  25.  
  26. with Ada.Finalization;            use Ada.Finalization;
  27. with Ada.IO_Exceptions;           use Ada.IO_Exceptions;
  28. with Ada.Streams;                 use Ada.Streams;
  29. with Interfaces.C_Streams;        use Interfaces.C_Streams;
  30. with System.Tasking_Soft_Links;   use System.Tasking_Soft_Links;
  31. with Unchecked_Deallocation;
  32.  
  33. package body System.File_IO is
  34.  
  35.    ----------------------
  36.    -- Global Variables --
  37.    ----------------------
  38.  
  39.    Open_Files : AFCB_Ptr;
  40.    --  This points to a list of AFCB's for all open files. This is a doubly
  41.    --  linked list, with the Prev pointer of the first entry, and the Next
  42.    --  pointer of the last entry containing null.
  43.  
  44.    type Temp_File_Record;
  45.    type Temp_File_Record_Ptr is access all Temp_File_Record;
  46.  
  47.    Temp_Base : constant String := "ADA_TEMP_";
  48.  
  49.    Temp_Len : constant := Temp_Base'Length + 6;
  50.    --  Length of temporary file name (6 = length of suffix added by mktemp)
  51.    --  This does not include the terminating NUL character.
  52.  
  53.    type Temp_File_Record is record
  54.       Name : String (1 .. Temp_Len + 1);
  55.       Next : Temp_File_Record_Ptr;
  56.    end record;
  57.    --  One of these is allocated for each temporary file created
  58.  
  59.    Temp_Files : Temp_File_Record_Ptr;
  60.    --  Points to list of names of temporary files
  61.  
  62.    type File_IO_Clean_Up_Type is new Controlled with null record;
  63.    --  The closing of all open files and deletion of temporary files is an
  64.    --  action which takes place at the end of execution of the main program.
  65.    --  This action can be implemented using a library level object which
  66.    --  gets finalized at the end of the main program execution. The above is
  67.    --  a controlled type introduced for this purpose.
  68.  
  69.    procedure Finalize (V : in out File_IO_Clean_Up_Type);
  70.    --  This is the finalize operation that is used to do the cleanup.
  71.  
  72.    File_IO_Clean_Up_Object : File_IO_Clean_Up_Type;
  73.    --  This is the single object of the type that triggers the finalization
  74.    --  call. Since it is at the library level, this happens just before the
  75.    --  environment task is finalized.
  76.  
  77.    -----------------------
  78.    -- Local Subprograms --
  79.    -----------------------
  80.  
  81.    procedure Free_String is new Unchecked_Deallocation (String, Pstring);
  82.  
  83.    subtype Fopen_String is String (1 .. 4);
  84.    --  Holds open string (longest is "w+b" & nul)
  85.  
  86.    procedure Fopen_Mode
  87.      (Mode   : File_Mode;
  88.       Text   : Boolean;
  89.       Creat  : Boolean;
  90.       Fopstr : out Fopen_String);
  91.    --  Determines proper open mode for a file to be opened in the given
  92.    --  Ada mode. Text is true for a text file and false otherwise, and
  93.    --  Creat is true for a create call, and False for an open call. The
  94.    --  value stored in Fopstr is a nul-terminated string suitable for a
  95.    --  call to fopen or freopen.
  96.  
  97.    ---------------------
  98.    -- Check_File_Open --
  99.    ---------------------
  100.  
  101.    procedure Check_File_Open (File : AFCB_Ptr) is
  102.    begin
  103.       if File = null then
  104.          raise Status_Error;
  105.       end if;
  106.    end Check_File_Open;
  107.  
  108.    ----------------
  109.    -- Append_Set --
  110.    ----------------
  111.  
  112.    procedure Append_Set (File : AFCB_Ptr) is
  113.    begin
  114.       if File.Mode = Append_File then
  115.          if fseek (File.Stream, 0, SEEK_END) /= 0 then
  116.             raise Device_Error;
  117.          end if;
  118.       end if;
  119.    end Append_Set;
  120.  
  121.    ----------------
  122.    -- Chain_File --
  123.    ----------------
  124.  
  125.    procedure Chain_File (File : AFCB_Ptr) is
  126.    begin
  127.       File.Next := Open_Files;
  128.       File.Prev := null;
  129.       Open_Files := File;
  130.  
  131.       if File.Next /= null then
  132.          File.Next.Prev := File;
  133.       end if;
  134.    end Chain_File;
  135.  
  136.    -----------------------
  137.    -- Check_Read_Status --
  138.    -----------------------
  139.  
  140.    procedure Check_Read_Status (File : AFCB_Ptr) is
  141.    begin
  142.       if File = null then
  143.          raise Status_Error;
  144.       elsif File.Mode > Inout_File then
  145.          raise Mode_Error;
  146.       end if;
  147.    end Check_Read_Status;
  148.  
  149.    ------------------------
  150.    -- Check_Write_Status --
  151.    ------------------------
  152.  
  153.    procedure Check_Write_Status (File : AFCB_Ptr) is
  154.    begin
  155.       if File = null then
  156.          raise Status_Error;
  157.       elsif File.Mode = In_File then
  158.          raise Mode_Error;
  159.       end if;
  160.    end Check_Write_Status;
  161.  
  162.    -----------
  163.    -- Close --
  164.    -----------
  165.  
  166.    procedure Close (File : in out AFCB_Ptr) is
  167.       Close_Status : int := 0;
  168.  
  169.    begin
  170.       Check_File_Open (File);
  171.       AFCB_Close (File);
  172.  
  173.       --  Sever the association between the given file and its associated
  174.       --  external file. The given file is left closed. Do not perform system
  175.       --  closes on the standard input, output and error files and also do
  176.       --  not attempt to close a stream that does not exist (signalled by a
  177.       --  null stream value -- happens in some error situations).
  178.  
  179.       if not File.Is_System_File
  180.         and then File.Stream /= NULL_Stream
  181.       then
  182.          Close_Status := fclose (File.Stream);
  183.       end if;
  184.  
  185.       --  Dechain file from list of open files and then free the storage
  186.       --  Since this is a global data structure, we have to protect against
  187.       --  multiple tasks attempting to access this list.
  188.  
  189.       Lock_Task;
  190.  
  191.       if File.Prev = null then
  192.          Open_Files := File.Next;
  193.       else
  194.          File.Prev.Next := File.Next;
  195.       end if;
  196.  
  197.       if File.Next /= null then
  198.          File.Next.Prev := File.Prev;
  199.       end if;
  200.  
  201.       Unlock_Task;
  202.  
  203.       --  Deallocate some parts of the file structure that were kept in heap
  204.       --  storage with the exception of system files (standard input, output
  205.       --  and error) since they had some information allocated in the stack.
  206.  
  207.       if not File.Is_System_File then
  208.          Free_String (File.Name);
  209.          Free_String (File.Form);
  210.          AFCB_Free (File);
  211.       end if;
  212.  
  213.       File := null;
  214.  
  215.       if Close_Status /= 0 then
  216.          raise Device_Error;
  217.       end if;
  218.    end Close;
  219.  
  220.    ------------
  221.    -- Delete --
  222.    ------------
  223.  
  224.    procedure Delete (File : in out AFCB_Ptr) is
  225.    begin
  226.       Check_File_Open (File);
  227.  
  228.       if not File.Is_Regular_File then
  229.          raise Use_Error;
  230.       end if;
  231.  
  232.       declare
  233.          Filename : aliased constant String := File.Name.all;
  234.  
  235.       begin
  236.          Close (File);
  237.  
  238.          if unlink (Filename'Address) = -1 then
  239.             raise Use_Error;
  240.          end if;
  241.       end;
  242.    end Delete;
  243.  
  244.    -----------------
  245.    -- End_Of_File --
  246.    -----------------
  247.  
  248.    function End_Of_File (File : AFCB_Ptr) return Boolean is
  249.    begin
  250.       Check_File_Open (File);
  251.  
  252.       if feof (File.Stream) /= 0 then
  253.          return True;
  254.  
  255.       else
  256.          Check_Read_Status (File);
  257.  
  258.          if ungetc (fgetc (File.Stream), File.Stream) = EOF then
  259.             clearerr (File.Stream);
  260.             return True;
  261.          else
  262.             return False;
  263.          end if;
  264.       end if;
  265.    end End_Of_File;
  266.  
  267.    --------------
  268.    -- Finalize --
  269.    --------------
  270.  
  271.    --  Note: we do not need to worry about locking against multiple task
  272.    --  access in this routine, since it is called only from the environment
  273.    --  task just before terminating execution.
  274.  
  275.    procedure Finalize (V : in out File_IO_Clean_Up_Type) is
  276.       Discard : int;
  277.       Fptr1   : AFCB_Ptr;
  278.       Fptr2   : AFCB_Ptr;
  279.    begin
  280.       --  First close all open files (the slightly complex form of this loop
  281.       --  is required because Close as a side effect nulls out its argument)
  282.  
  283.       Fptr1 := Open_Files;
  284.       while Fptr1 /= null loop
  285.          Fptr2 := Fptr1.Next;
  286.          Close (Fptr1);
  287.          Fptr1 := Fptr2;
  288.       end loop;
  289.  
  290.       --  Now unlink all temporary files. We do not bother to free the
  291.       --  blocks because we are just about to terminate the program. We
  292.       --  also ignore any errors while attempting these unlink operations.
  293.  
  294.       while Temp_Files /= null loop
  295.          Discard := unlink (Temp_Files.Name'Address);
  296.          Temp_Files := Temp_Files.Next;
  297.       end loop;
  298.  
  299.    end Finalize;
  300.  
  301.    -----------
  302.    -- Flush --
  303.    -----------
  304.  
  305.    procedure Flush (File : AFCB_Ptr) is
  306.    begin
  307.       Check_Write_Status (File);
  308.  
  309.       if fflush (File.Stream) = 0 then
  310.          return;
  311.       else
  312.          raise Device_Error;
  313.       end if;
  314.    end Flush;
  315.  
  316.    ----------------
  317.    -- Fopen_Mode --
  318.    ----------------
  319.  
  320.    --  The fopen mode to be used is shown by the following table:
  321.  
  322.    --                        OPEN         CREATE
  323.    --     Append_File        "r+"           "w+"
  324.    --     In_File            "r"            "w+"
  325.    --     Out_File           "w"            "w"
  326.    --     Inout_File         "r+"           "w+"
  327.  
  328.    --  Note: we do not use "a" or "a+" for Append_File, since this would not
  329.    --  work in the case of stream files, where even if in append file mode,
  330.    --  you can reset to earlier points in the file. The caller must use the
  331.    --  Append_Set routine to deal with the necessary positioning.
  332.  
  333.    --  Note: in several cases, the fopen mode used allows reading and
  334.    --  writing, but the setting of the Ada mode is more restrictive. For
  335.    --  instance, Create in In_File mode uses "r+" which allows writing,
  336.    --  but the Ada mode In_File will cause any write operations to be
  337.    --  rejected with Mode_Error in any case.
  338.  
  339.    --  Note: for the Out_File/Open cases, an initial call will be made by
  340.    --  the caller to first open the file in "r" mode to be sure that it
  341.    --  exists. The real open, in "w" mode, will then destroy this file.
  342.    --  This is peculiar, but that's what Ada semantics require!
  343.  
  344.    --  If text file translation is required, then either b or t is
  345.    --  added to the mode, depending on the setting of Text.
  346.  
  347.    procedure Fopen_Mode
  348.      (Mode   : File_Mode;
  349.       Text   : Boolean;
  350.       Creat  : Boolean;
  351.       Fopstr : out Fopen_String)
  352.    is
  353.       Fptr  : Positive;
  354.  
  355.       text_translation_required : Boolean;
  356.       pragma Import (C, text_translation_required);
  357.  
  358.       Stream : FILEs;
  359.  
  360.    begin
  361.       case Mode is
  362.          when In_File =>
  363.             if Creat then
  364.                Fopstr (1) := 'w';
  365.                Fopstr (2) := '+';
  366.                Fptr := 3;
  367.             else
  368.                Fopstr (1) := 'r';
  369.                Fptr := 2;
  370.             end if;
  371.  
  372.          when Out_File =>
  373.             Fopstr (1) := 'w';
  374.             Fptr := 2;
  375.  
  376.          when Inout_File | Append_File =>
  377.             if Creat then
  378.                Fopstr (1) := 'w';
  379.             else
  380.                Fopstr (1) := 'r';
  381.             end if;
  382.  
  383.             Fopstr (2) := '+';
  384.             Fptr := 3;
  385.  
  386.       end case;
  387.  
  388.       --  If text_translation_required is true then we need to append
  389.       --  either a t or b to the string to get the right mode
  390.  
  391.       if text_translation_required then
  392.          if Text then
  393.             Fopstr (Fptr) := 't';
  394.          else
  395.             Fopstr (Fptr) := 'b';
  396.          end if;
  397.  
  398.          Fptr := Fptr + 1;
  399.       end if;
  400.  
  401.       Fopstr (Fptr) := Ascii.NUL;
  402.    end Fopen_Mode;
  403.  
  404.    ----------
  405.    -- Form --
  406.    ----------
  407.  
  408.    function Form (File : in AFCB_Ptr) return String is
  409.    begin
  410.       if File = null then
  411.          raise Status_Error;
  412.       else
  413.          return File.Form.all (1 .. File.Form'Length - 1);
  414.       end if;
  415.    end Form;
  416.  
  417.    ------------------
  418.    -- Form_Boolean --
  419.    ------------------
  420.  
  421.    function Form_Boolean
  422.      (Form    : String;
  423.       Keyword : String;
  424.       Default : Boolean)
  425.       return    Boolean
  426.    is
  427.       V1, V2 : Natural;
  428.  
  429.    begin
  430.       Form_Parameter (Form, Keyword, V1, V2);
  431.  
  432.       if V1 = 0 then
  433.          return Default;
  434.  
  435.       elsif Form (V1) = 'y' then
  436.          return True;
  437.  
  438.       elsif Form (V1) = 'n' then
  439.          return False;
  440.  
  441.       else
  442.          raise Use_Error;
  443.       end if;
  444.    end Form_Boolean;
  445.  
  446.    ------------------
  447.    -- Form_Integer --
  448.    ------------------
  449.  
  450.    function Form_Integer
  451.      (Form    : String;
  452.       Keyword : String;
  453.       Default : Integer)
  454.       return    Integer
  455.    is
  456.       V1, V2 : Natural;
  457.       V      : Integer;
  458.  
  459.    begin
  460.       Form_Parameter (Form, Keyword, V1, V2);
  461.  
  462.       if V1 = 0 then
  463.          return Default;
  464.  
  465.       else
  466.          V := 0;
  467.  
  468.          for J in V1 .. V2 loop
  469.             if Form (J) not in '0' .. '9' then
  470.                raise Use_Error;
  471.             else
  472.                V := V * 10 + Character'Pos (Form (J)) - Character'Pos ('0');
  473.             end if;
  474.  
  475.             if V > 999_999 then
  476.                raise Use_Error;
  477.             end if;
  478.          end loop;
  479.  
  480.          return V;
  481.       end if;
  482.    end Form_Integer;
  483.  
  484.    --------------------
  485.    -- Form_Parameter --
  486.    --------------------
  487.  
  488.    procedure Form_Parameter
  489.      (Form    : String;
  490.       Keyword : String;
  491.       Start   : out Natural;
  492.       Stop    : out Natural)
  493.   is
  494.  
  495.       Klen : constant Integer := Keyword'Length;
  496.  
  497.    --  Start of processing for Form_Parameter
  498.  
  499.    begin
  500.       for J in Form'First + Klen .. Form'Last - 1 loop
  501.          if Form (J) = '='
  502.            and then Form (J - Klen .. J - 1) = Keyword
  503.          then
  504.             Start := J + 1;
  505.             Stop := Start - 1;
  506.  
  507.             while Form (Stop + 1) /= Ascii.NUL
  508.               and then Form (Stop + 1) /= ','
  509.             loop
  510.                Stop := Stop + 1;
  511.             end loop;
  512.  
  513.             return;
  514.          end if;
  515.       end loop;
  516.  
  517.       Start := 0;
  518.    end Form_Parameter;
  519.  
  520.    -------------
  521.    -- Is_Open --
  522.    -------------
  523.  
  524.    function Is_Open (File : in AFCB_Ptr) return Boolean is
  525.    begin
  526.       return (File /= null);
  527.    end Is_Open;
  528.  
  529.    ----------
  530.    -- Mode --
  531.    ----------
  532.  
  533.    function Mode (File : in AFCB_Ptr) return File_Mode is
  534.    begin
  535.       if File = null then
  536.          raise Status_Error;
  537.       else
  538.          return File.Mode;
  539.       end if;
  540.    end Mode;
  541.  
  542.    ----------
  543.    -- Name --
  544.    ----------
  545.  
  546.    function Name (File : in AFCB_Ptr) return String is
  547.    begin
  548.       if File = null then
  549.          raise Status_Error;
  550.       else
  551.          return File.Name.all (1 .. File.Name'Length - 1);
  552.       end if;
  553.    end Name;
  554.  
  555.    ----------
  556.    -- Open --
  557.    ----------
  558.  
  559.    procedure Open
  560.      (File_Ptr  : in out AFCB_Ptr;
  561.       Dummy_FCB : in out AFCB'Class;
  562.       Mode      : File_Mode;
  563.       Name      : String;
  564.       Form      : String;
  565.       Amethod   : Character;
  566.       Creat     : Boolean;
  567.       Text      : Boolean;
  568.       C_Stream  : FILEs := NULL_Stream)
  569.    is
  570.       Stream : FILEs := C_Stream;
  571.       --  Stream which we open in response to this request
  572.  
  573.       Shared : Shared_Status_Type;
  574.       --  Setting of Shared_Status field for file
  575.  
  576.       Fopstr : aliased Fopen_String;
  577.       --  Mode string used in fopen call
  578.  
  579.       Fmoder : aliased constant String (1 .. 2) := "r" & Ascii.NUL;
  580.       --  Used for test open to see if file exists
  581.  
  582.       Formstr : aliased String (1 .. Form'Length + 1);
  583.       --  Form string with Ascii.NUL appended, folded to lower case
  584.  
  585.       Tempfile : constant Boolean := (Name'Length = 0);
  586.       --  Indicates temporary file case
  587.  
  588.       Namelen : constant Integer := Integer'Max (Temp_Len, Name'Length);
  589.       --  Length required for file name, not including final Ascii.NUL
  590.  
  591.       Namestr : aliased String (1 .. Namelen + 1);
  592.       --  Name as given or temporary file name with Ascii.NUL appended
  593.  
  594.       Fullname : aliased String (1 .. max_path_len);
  595.       --  Full name (as required for Name function, and as stored in the
  596.       --  control block in the Name field) with Ascii.NUL appended.
  597.  
  598.       Full_Name_Len : Integer;
  599.       --  Length of name actually stored in Fullname
  600.  
  601.    begin
  602.       if File_Ptr /= null then
  603.          raise Status_Error;
  604.       end if;
  605.  
  606.       --  Acquire form string, setting required NUL terminator
  607.  
  608.       Formstr (1 .. Form'Length) := Form;
  609.       Formstr (Formstr'Last) := Ascii.NUL;
  610.  
  611.       --  Convert form string to lower case
  612.  
  613.       for J in Formstr'Range loop
  614.          if Formstr (J) in 'A' .. 'Z' then
  615.             Formstr (J) := Character'Val (Character'Pos (Formstr (J)) + 32);
  616.          end if;
  617.       end loop;
  618.  
  619.       --  Acquire setting of shared parameter
  620.  
  621.       declare
  622.          V1, V2 : Natural;
  623.  
  624.       begin
  625.          Form_Parameter (Formstr, "shared", V1, V2);
  626.  
  627.          if V1 = 0 then
  628.             Shared := None;
  629.  
  630.          elsif Form (V1 .. V2) = "yes" then
  631.             Shared := Yes;
  632.  
  633.          elsif Form (V1 .. V2) = "no" then
  634.             Shared := No;
  635.  
  636.          else
  637.             raise Use_Error;
  638.          end if;
  639.       end;
  640.  
  641.       --  Remaining processing is done with tasking locked out. This ensures
  642.       --  that the global data structures (temporary file chain and the open
  643.       --  file chain) retain their integrity.
  644.  
  645.       Lock_Task;
  646.  
  647.       --  If we were given a stream (call from xxx.C_Streams.Open), then set
  648.       --  full name to null and that is all we have to do in this case so
  649.       --  skip to end of processing.
  650.  
  651.       if Stream /= NULL_Stream then
  652.          Fullname (1) := Ascii.Nul;
  653.          Full_Name_Len := 1;
  654.  
  655.       --  Normal case of Open or Create
  656.  
  657.       else
  658.          --  If temporary file case, get temporary file name and add
  659.          --  to the list of temporary files to be deleted on exit.
  660.  
  661.          if Tempfile then
  662.             if not Creat then
  663.                Unlock_Task;
  664.                raise Name_Error;
  665.             end if;
  666.  
  667.             Namestr := Temp_Base & "XXXXXX" & Ascii.NUL;
  668.             mktemp (Namestr'Address);
  669.  
  670.             if Namestr (1) = Ascii.NUL then
  671.                Unlock_Task;
  672.                raise Use_Error;
  673.             end if;
  674.  
  675.             Temp_Files :=
  676.               new Temp_File_Record'(Name => Namestr, Next => Temp_Files);
  677.  
  678.          --  Normal case of non-null name given
  679.  
  680.          else
  681.             Namestr (1 .. Name'Length) := Name;
  682.             Namestr (Name'Length + 1)  := Ascii.NUL;
  683.          end if;
  684.  
  685.          --  Get full name in accordance with the advice of RM A.8.2(22).
  686.  
  687.          full_name (Namestr'Address, Fullname'Address);
  688.  
  689.          for J in Fullname'Range loop
  690.             if Fullname (J) = Ascii.NUL then
  691.                Full_Name_Len := J;
  692.                exit;
  693.             end if;
  694.          end loop;
  695.  
  696.          --  If Shared=None or Shared=Yes, then check for the existence
  697.          --  of another file with exactly the same full name.
  698.  
  699.          if Shared /= No then
  700.             declare
  701.                P : AFCB_Ptr;
  702.  
  703.             begin
  704.                P := Open_Files;
  705.                while P /= null loop
  706.                   if Fullname (1 .. Full_Name_Len) = P.Name.all then
  707.  
  708.                      --  If we get a match, and either file has Shared=None,
  709.                      --  then raise Use_Error, since we don't allow two files
  710.                      --  of the same name to be opened unless they specify the
  711.                      --  required sharing mode.
  712.  
  713.                      if Shared = None
  714.                        or else P.Shared_Status = None
  715.                      then
  716.                         Unlock_Task;
  717.                         raise Use_Error;
  718.  
  719.                      --  If both files have Shared=Yes, then we acquire the
  720.                      --  stream from the located file to use as our stream.
  721.  
  722.                      elsif Shared = Yes
  723.                        and then P.Shared_Status = Yes
  724.                      then
  725.                         Stream := P.Stream;
  726.                         exit;
  727.  
  728.                      --  Otherwise one of the files has Shared=Yes and one
  729.                      --  has Shared=No. If the current file has Shared=No
  730.                      --  then all is well but we don't want to share any
  731.                      --  other file's stream. If the current file has
  732.                      --  Shared=Yes, we would like to share a stream, but
  733.                      --  not from a file that has Shared=No, so in either
  734.                      --  case we just keep going on the search.
  735.  
  736.                      else
  737.                         null;
  738.                      end if;
  739.                   end if;
  740.  
  741.                   P := P.Next;
  742.                end loop;
  743.             end;
  744.          end if;
  745.  
  746.          --  Open specified file if we did not find an existing stream
  747.  
  748.          if Stream = NULL_Stream then
  749.             Fopen_Mode (Mode, Text, Creat, Fopstr);
  750.  
  751.             --  A special case, if we are opening (OPEN case) a file and
  752.             --  the mode returned by Fopen_Mode is not "r" or "r+", then
  753.             --  we first do an open in "r" mode to make sure that the file
  754.             --  exists as required by Ada semantics, we then reopen in the
  755.             --  required mode.
  756.  
  757.             if Creat = False and then Fopstr (1) /= 'r' then
  758.                Stream := fopen (Namestr'Address, Fmoder'Address);
  759.  
  760.                if Stream = NULL_Stream then
  761.                   Unlock_Task;
  762.                   raise Name_Error;
  763.                else
  764.                   Stream := freopen (Namestr'Address, Fopstr'Address, Stream);
  765.                end if;
  766.  
  767.             --  Normal case, we can open the file directly with the given mode
  768.  
  769.             else
  770.                Stream := fopen (Namestr'Address, Fopstr'Address);
  771.             end if;
  772.  
  773.             if Stream = NULL_Stream then
  774.                Unlock_Task;
  775.                raise Name_Error;
  776.             end if;
  777.          end if;
  778.       end if;
  779.  
  780.       --  Stream has been successfully located or opened, so now we are
  781.       --  committed to completing the opening of the file. Allocate block
  782.       --  on heap and fill in its fields.
  783.  
  784.       File_Ptr := AFCB_Allocate (Dummy_FCB);
  785.  
  786.       File_Ptr.Is_Regular_File   := (is_regular_file (fileno (Stream)) /= 0);
  787.       File_Ptr.Is_System_File    := False;
  788.       File_Ptr.Is_Text_File      := Text;
  789.       File_Ptr.Shared_Status     := Shared;
  790.       File_Ptr.Access_Method     := Amethod;
  791.       File_Ptr.Stream            := Stream;
  792.       File_Ptr.Form              := new String'(Formstr);
  793.       File_Ptr.Name              := new String'(Fullname (1 .. Full_Name_Len));
  794.       File_Ptr.Mode              := Mode;
  795.       File_Ptr.Is_Temporary_File := False;
  796.  
  797.       Chain_File (File_Ptr);
  798.       Unlock_Task;
  799.       Append_Set (File_Ptr);
  800.    end Open;
  801.  
  802.    --------------
  803.    -- Read_Buf --
  804.    --------------
  805.  
  806.    procedure Read_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
  807.       Nread : size_t;
  808.  
  809.    begin
  810.       Nread := fread (Buf, 1, Siz, File.Stream);
  811.  
  812.       if Nread = Siz then
  813.          return;
  814.  
  815.       elsif ferror (File.Stream) /= 0 then
  816.          raise Device_Error;
  817.  
  818.       elsif Nread = 0 then
  819.          raise End_Error;
  820.  
  821.       else -- 0 < Nread < Siz
  822.          raise Data_Error;
  823.       end if;
  824.  
  825.    end Read_Buf;
  826.  
  827.    procedure Read_Buf
  828.      (File  : AFCB_Ptr;
  829.       Buf   : Address;
  830.       Siz   : in Interfaces.C_Streams.size_t;
  831.       Count : out Interfaces.C_Streams.size_t)
  832.    is
  833.    begin
  834.       Count := fread (Buf, 1, Siz, File.Stream);
  835.  
  836.       if Count = 0 and then ferror (File.Stream) /= 0 then
  837.          raise Device_Error;
  838.       end if;
  839.    end Read_Buf;
  840.  
  841.    -----------
  842.    -- Reset --
  843.    -----------
  844.  
  845.    --  The reset which does not change the mode simply does a rewind.
  846.  
  847.    procedure Reset (File : in out AFCB_Ptr) is
  848.    begin
  849.       Check_File_Open (File);
  850.       rewind (File.Stream);
  851.    end Reset;
  852.  
  853.    --  The reset with a change in mode is done using freopen, and is
  854.    --  not permitted except for regular files (since otherwise there
  855.    --  is no name for the freopen, and in any case it seems meaningless)
  856.  
  857.    procedure Reset (File : in out AFCB_Ptr; Mode : in File_Mode) is
  858.       Fopstr : aliased Fopen_String;
  859.  
  860.    begin
  861.       Check_File_Open (File);
  862.  
  863.       --  If mode is not really changing, then we simply rewind the stream
  864.       --  this is permitted in all cases except for non-regular files, where
  865.       --  rewind can't work.
  866.  
  867.       if Mode = File.Mode and then File.Is_Regular_File then
  868.          rewind (File.Stream);
  869.  
  870.       --  Change of mode not allowed for shared file or file with no name
  871.       --  or file that is not a regular file, or for a system file.
  872.  
  873.       elsif File.Shared_Status = Yes
  874.         or else File.Name'Length <= 1
  875.         or else File.Is_System_File
  876.         or else (not File.Is_Regular_File)
  877.       then
  878.          raise Use_Error;
  879.  
  880.       --  Here the change of mode is permitted, we do it by reopening the
  881.       --  file in the new mode and replacing the stream with a new stream.
  882.  
  883.       else
  884.          Fopen_Mode (Mode, File.Is_Text_File, False, Fopstr);
  885.          File.Stream :=
  886.            freopen (File.Name.all'Address, Fopstr'Address, File.Stream);
  887.  
  888.          if File.Stream = NULL_Stream then
  889.             Close (File);
  890.             raise Use_Error;
  891.  
  892.          else
  893.             File.Mode := Mode;
  894.             Append_Set (File);
  895.          end if;
  896.       end if;
  897.    end Reset;
  898.  
  899.    ---------------
  900.    -- Write_Buf --
  901.    ---------------
  902.  
  903.    procedure Write_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
  904.    begin
  905.       if fwrite (Buf, 1, Siz, File.Stream) /= Siz then
  906.          raise Device_Error;
  907.       end if;
  908.    end Write_Buf;
  909.  
  910. end System.File_IO;
  911.