OSDN Git Service

Fix aliasing bug that also caused memory usage problems.
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-fileio.adb
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 --          Copyright (C) 1992-2004 Free Software Foundation, Inc.          --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
20 -- MA 02111-1307, USA.                                                      --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.Finalization;            use Ada.Finalization;
35 with Ada.IO_Exceptions;           use Ada.IO_Exceptions;
36 with Interfaces.C_Streams;        use Interfaces.C_Streams;
37 with System.CRTL;
38 with System.Soft_Links;
39 with Unchecked_Deallocation;
40
41 package body System.File_IO is
42
43    use System.File_Control_Block;
44
45    package SSL renames System.Soft_Links;
46
47    use type System.CRTL.size_t;
48
49    ----------------------
50    -- Global Variables --
51    ----------------------
52
53    Open_Files : AFCB_Ptr;
54    --  This points to a list of AFCB's for all open files. This is a doubly
55    --  linked list, with the Prev pointer of the first entry, and the Next
56    --  pointer of the last entry containing null. Note that this global
57    --  variable must be properly protected to provide thread safety.
58
59    type Temp_File_Record;
60    type Temp_File_Record_Ptr is access all Temp_File_Record;
61
62    type Temp_File_Record is record
63       Name : String (1 .. L_tmpnam + 1);
64       Next : Temp_File_Record_Ptr;
65    end record;
66    --  One of these is allocated for each temporary file created
67
68    Temp_Files : Temp_File_Record_Ptr;
69    --  Points to list of names of temporary files. Note that this global
70    --  variable must be properly protected to provide thread safety.
71
72    type File_IO_Clean_Up_Type is new Controlled with null record;
73    --  The closing of all open files and deletion of temporary files is an
74    --  action which takes place at the end of execution of the main program.
75    --  This action can be implemented using a library level object which
76    --  gets finalized at the end of the main program execution. The above is
77    --  a controlled type introduced for this purpose.
78
79    procedure Finalize (V : in out File_IO_Clean_Up_Type);
80    --  This is the finalize operation that is used to do the cleanup.
81
82    File_IO_Clean_Up_Object : File_IO_Clean_Up_Type;
83    pragma Warnings (Off, File_IO_Clean_Up_Object);
84    --  This is the single object of the type that triggers the finalization
85    --  call. Since it is at the library level, this happens just before the
86    --  environment task is finalized.
87
88    text_translation_required : Boolean;
89    pragma Import
90      (C, text_translation_required, "__gnat_text_translation_required");
91    --  If true, add appropriate suffix to control string for Open.
92
93    -----------------------
94    -- Local Subprograms --
95    -----------------------
96
97    procedure Free_String is new Unchecked_Deallocation (String, Pstring);
98
99    subtype Fopen_String is String (1 .. 4);
100    --  Holds open string (longest is "w+b" & nul)
101
102    procedure Fopen_Mode
103      (Mode    : File_Mode;
104       Text    : Boolean;
105       Creat   : Boolean;
106       Amethod : Character;
107       Fopstr  : out Fopen_String);
108    --  Determines proper open mode for a file to be opened in the given
109    --  Ada mode. Text is true for a text file and false otherwise, and
110    --  Creat is true for a create call, and False for an open call. The
111    --  value stored in Fopstr is a nul-terminated string suitable for a
112    --  call to fopen or freopen. Amethod is the character designating
113    --  the access method from the Access_Method field of the FCB.
114
115    ----------------
116    -- Append_Set --
117    ----------------
118
119    procedure Append_Set (File : AFCB_Ptr) is
120    begin
121       if File.Mode = Append_File then
122          if fseek (File.Stream, 0, SEEK_END) /= 0 then
123             raise Device_Error;
124          end if;
125       end if;
126    end Append_Set;
127
128    ----------------
129    -- Chain_File --
130    ----------------
131
132    procedure Chain_File (File : AFCB_Ptr) is
133    begin
134       --  Take a task lock, to protect the global data value Open_Files
135
136       SSL.Lock_Task.all;
137
138       --  Do the chaining operation locked
139
140       File.Next := Open_Files;
141       File.Prev := null;
142       Open_Files := File;
143
144       if File.Next /= null then
145          File.Next.Prev := File;
146       end if;
147
148       SSL.Unlock_Task.all;
149
150    exception
151       when others =>
152          SSL.Unlock_Task.all;
153          raise;
154    end Chain_File;
155
156    ---------------------
157    -- Check_File_Open --
158    ---------------------
159
160    procedure Check_File_Open (File : AFCB_Ptr) is
161    begin
162       if File = null then
163          raise Status_Error;
164       end if;
165    end Check_File_Open;
166
167    -----------------------
168    -- Check_Read_Status --
169    -----------------------
170
171    procedure Check_Read_Status (File : AFCB_Ptr) is
172    begin
173       if File = null then
174          raise Status_Error;
175       elsif File.Mode > Inout_File then
176          raise Mode_Error;
177       end if;
178    end Check_Read_Status;
179
180    ------------------------
181    -- Check_Write_Status --
182    ------------------------
183
184    procedure Check_Write_Status (File : AFCB_Ptr) is
185    begin
186       if File = null then
187          raise Status_Error;
188       elsif File.Mode = In_File then
189          raise Mode_Error;
190       end if;
191    end Check_Write_Status;
192
193    -----------
194    -- Close --
195    -----------
196
197    procedure Close (File : in out AFCB_Ptr) is
198       Close_Status : int := 0;
199       Dup_Strm     : Boolean := False;
200
201    begin
202       Check_File_Open (File);
203       AFCB_Close (File);
204
205       --  Take a task lock, to protect the global data value Open_Files
206
207       SSL.Lock_Task.all;
208
209       --  Sever the association between the given file and its associated
210       --  external file. The given file is left closed. Do not perform system
211       --  closes on the standard input, output and error files and also do
212       --  not attempt to close a stream that does not exist (signalled by a
213       --  null stream value -- happens in some error situations).
214
215       if not File.Is_System_File
216         and then File.Stream /= NULL_Stream
217       then
218          --  Do not do an fclose if this is a shared file and there is
219          --  at least one other instance of the stream that is open.
220
221          if File.Shared_Status = Yes then
222             declare
223                P   : AFCB_Ptr;
224
225             begin
226                P := Open_Files;
227                while P /= null loop
228                   if P /= File
229                     and then File.Stream = P.Stream
230                   then
231                      Dup_Strm := True;
232                      exit;
233                   end if;
234
235                   P := P.Next;
236                end loop;
237             end;
238          end if;
239
240          --  Do the fclose unless this was a duplicate in the shared case
241
242          if not Dup_Strm then
243             Close_Status := fclose (File.Stream);
244          end if;
245       end if;
246
247       --  Dechain file from list of open files and then free the storage
248
249       if File.Prev = null then
250          Open_Files := File.Next;
251       else
252          File.Prev.Next := File.Next;
253       end if;
254
255       if File.Next /= null then
256          File.Next.Prev := File.Prev;
257       end if;
258
259       --  Deallocate some parts of the file structure that were kept in heap
260       --  storage with the exception of system files (standard input, output
261       --  and error) since they had some information allocated in the stack.
262
263       if not File.Is_System_File then
264          Free_String (File.Name);
265          Free_String (File.Form);
266          AFCB_Free (File);
267       end if;
268
269       File := null;
270
271       if Close_Status /= 0 then
272          raise Device_Error;
273       end if;
274
275       SSL.Unlock_Task.all;
276
277    exception
278       when others =>
279          SSL.Unlock_Task.all;
280          raise;
281    end Close;
282
283    ------------
284    -- Delete --
285    ------------
286
287    procedure Delete (File : in out AFCB_Ptr) is
288    begin
289       Check_File_Open (File);
290
291       if not File.Is_Regular_File then
292          raise Use_Error;
293       end if;
294
295       declare
296          Filename : aliased constant String := File.Name.all;
297
298       begin
299          Close (File);
300
301          --  Now unlink the external file. Note that we use the full name
302          --  in this unlink, because the working directory may have changed
303          --  since we did the open, and we want to unlink the right file!
304
305          if unlink (Filename'Address) = -1 then
306             raise Use_Error;
307          end if;
308       end;
309    end Delete;
310
311    -----------------
312    -- End_Of_File --
313    -----------------
314
315    function End_Of_File (File : AFCB_Ptr) return Boolean is
316    begin
317       Check_File_Open (File);
318
319       if feof (File.Stream) /= 0 then
320          return True;
321
322       else
323          Check_Read_Status (File);
324
325          if ungetc (fgetc (File.Stream), File.Stream) = EOF then
326             clearerr (File.Stream);
327             return True;
328          else
329             return False;
330          end if;
331       end if;
332    end End_Of_File;
333
334    --------------
335    -- Finalize --
336    --------------
337
338    --  Note: we do not need to worry about locking against multiple task
339    --  access in this routine, since it is called only from the environment
340    --  task just before terminating execution.
341
342    procedure Finalize (V : in out File_IO_Clean_Up_Type) is
343       pragma Warnings (Off, V);
344
345       Fptr1   : AFCB_Ptr;
346       Fptr2   : AFCB_Ptr;
347
348       Discard : int;
349       pragma Unreferenced (Discard);
350
351    begin
352       --  Take a lock to protect global Open_Files data structure
353
354       SSL.Lock_Task.all;
355
356       --  First close all open files (the slightly complex form of this loop
357       --  is required because Close as a side effect nulls out its argument)
358
359       Fptr1 := Open_Files;
360       while Fptr1 /= null loop
361          Fptr2 := Fptr1.Next;
362          Close (Fptr1);
363          Fptr1 := Fptr2;
364       end loop;
365
366       --  Now unlink all temporary files. We do not bother to free the
367       --  blocks because we are just about to terminate the program. We
368       --  also ignore any errors while attempting these unlink operations.
369
370       while Temp_Files /= null loop
371          Discard := unlink (Temp_Files.Name'Address);
372          Temp_Files := Temp_Files.Next;
373       end loop;
374
375       SSL.Unlock_Task.all;
376
377    exception
378       when others =>
379          SSL.Unlock_Task.all;
380          raise;
381    end Finalize;
382
383    -----------
384    -- Flush --
385    -----------
386
387    procedure Flush (File : AFCB_Ptr) is
388    begin
389       Check_Write_Status (File);
390
391       if fflush (File.Stream) = 0 then
392          return;
393       else
394          raise Device_Error;
395       end if;
396    end Flush;
397
398    ----------------
399    -- Fopen_Mode --
400    ----------------
401
402    --  The fopen mode to be used is shown by the following table:
403
404    --                                     OPEN         CREATE
405    --     Append_File                     "r+"           "w+"
406    --     In_File                         "r"            "w+"
407    --     Out_File (Direct_IO)            "r+"           "w"
408    --     Out_File (all others)           "w"            "w"
409    --     Inout_File                      "r+"           "w+"
410
411    --  Note: we do not use "a" or "a+" for Append_File, since this would not
412    --  work in the case of stream files, where even if in append file mode,
413    --  you can reset to earlier points in the file. The caller must use the
414    --  Append_Set routine to deal with the necessary positioning.
415
416    --  Note: in several cases, the fopen mode used allows reading and
417    --  writing, but the setting of the Ada mode is more restrictive. For
418    --  instance, Create in In_File mode uses "w+" which allows writing,
419    --  but the Ada mode In_File will cause any write operations to be
420    --  rejected with Mode_Error in any case.
421
422    --  Note: for the Out_File/Open cases for other than the Direct_IO case,
423    --  an initial call will be made by the caller to first open the file in
424    --  "r" mode to be sure that it exists. The real open, in "w" mode, will
425    --  then destroy this file. This is peculiar, but that's what Ada semantics
426    --  require and the ACVT tests insist on!
427
428    --  If text file translation is required, then either b or t is
429    --  added to the mode, depending on the setting of Text.
430
431    procedure Fopen_Mode
432      (Mode    : File_Mode;
433       Text    : Boolean;
434       Creat   : Boolean;
435       Amethod : Character;
436       Fopstr  : out Fopen_String)
437    is
438       Fptr  : Positive;
439
440    begin
441       case Mode is
442          when In_File =>
443             if Creat then
444                Fopstr (1) := 'w';
445                Fopstr (2) := '+';
446                Fptr := 3;
447             else
448                Fopstr (1) := 'r';
449                Fptr := 2;
450             end if;
451
452          when Out_File =>
453             if Amethod = 'D' and not Creat then
454                Fopstr (1) := 'r';
455                Fopstr (2) := '+';
456                Fptr := 3;
457             else
458                Fopstr (1) := 'w';
459                Fptr := 2;
460             end if;
461
462          when Inout_File | Append_File =>
463             if Creat then
464                Fopstr (1) := 'w';
465             else
466                Fopstr (1) := 'r';
467             end if;
468
469             Fopstr (2) := '+';
470             Fptr := 3;
471
472       end case;
473
474       --  If text_translation_required is true then we need to append
475       --  either a t or b to the string to get the right mode
476
477       if text_translation_required then
478          if Text then
479             Fopstr (Fptr) := 't';
480          else
481             Fopstr (Fptr) := 'b';
482          end if;
483
484          Fptr := Fptr + 1;
485       end if;
486
487       Fopstr (Fptr) := ASCII.NUL;
488    end Fopen_Mode;
489
490    ----------
491    -- Form --
492    ----------
493
494    function Form (File : in AFCB_Ptr) return String is
495    begin
496       if File = null then
497          raise Status_Error;
498       else
499          return File.Form.all (1 .. File.Form'Length - 1);
500       end if;
501    end Form;
502
503    ------------------
504    -- Form_Boolean --
505    ------------------
506
507    function Form_Boolean
508      (Form    : String;
509       Keyword : String;
510       Default : Boolean)
511       return    Boolean
512    is
513       V1, V2 : Natural;
514
515    begin
516       Form_Parameter (Form, Keyword, V1, V2);
517
518       if V1 = 0 then
519          return Default;
520
521       elsif Form (V1) = 'y' then
522          return True;
523
524       elsif Form (V1) = 'n' then
525          return False;
526
527       else
528          raise Use_Error;
529       end if;
530    end Form_Boolean;
531
532    ------------------
533    -- Form_Integer --
534    ------------------
535
536    function Form_Integer
537      (Form    : String;
538       Keyword : String;
539       Default : Integer)
540       return    Integer
541    is
542       V1, V2 : Natural;
543       V      : Integer;
544
545    begin
546       Form_Parameter (Form, Keyword, V1, V2);
547
548       if V1 = 0 then
549          return Default;
550
551       else
552          V := 0;
553
554          for J in V1 .. V2 loop
555             if Form (J) not in '0' .. '9' then
556                raise Use_Error;
557             else
558                V := V * 10 + Character'Pos (Form (J)) - Character'Pos ('0');
559             end if;
560
561             if V > 999_999 then
562                raise Use_Error;
563             end if;
564          end loop;
565
566          return V;
567       end if;
568    end Form_Integer;
569
570    --------------------
571    -- Form_Parameter --
572    --------------------
573
574    procedure Form_Parameter
575      (Form    : String;
576       Keyword : String;
577       Start   : out Natural;
578       Stop    : out Natural)
579   is
580       Klen : constant Integer := Keyword'Length;
581
582    --  Start of processing for Form_Parameter
583
584    begin
585       for J in Form'First + Klen .. Form'Last - 1 loop
586          if Form (J) = '='
587            and then Form (J - Klen .. J - 1) = Keyword
588          then
589             Start := J + 1;
590             Stop := Start - 1;
591
592             while Form (Stop + 1) /= ASCII.NUL
593               and then Form (Stop + 1) /= ','
594             loop
595                Stop := Stop + 1;
596             end loop;
597
598             return;
599          end if;
600       end loop;
601
602       Start := 0;
603       Stop  := 0;
604    end Form_Parameter;
605
606    -------------
607    -- Is_Open --
608    -------------
609
610    function Is_Open (File : in AFCB_Ptr) return Boolean is
611    begin
612       return (File /= null);
613    end Is_Open;
614
615    -------------------
616    -- Make_Buffered --
617    -------------------
618
619    procedure Make_Buffered
620      (File    : AFCB_Ptr;
621       Buf_Siz : Interfaces.C_Streams.size_t)
622    is
623       status : Integer;
624       pragma Unreferenced (status);
625
626    begin
627       status := setvbuf (File.Stream, Null_Address, IOFBF, Buf_Siz);
628    end Make_Buffered;
629
630    ------------------------
631    -- Make_Line_Buffered --
632    ------------------------
633
634    procedure Make_Line_Buffered
635      (File     : AFCB_Ptr;
636       Line_Siz : Interfaces.C_Streams.size_t)
637    is
638       status : Integer;
639       pragma Unreferenced (status);
640
641    begin
642       status := setvbuf (File.Stream, Null_Address, IOLBF, Line_Siz);
643    end Make_Line_Buffered;
644
645    ---------------------
646    -- Make_Unbuffered --
647    ---------------------
648
649    procedure Make_Unbuffered (File : AFCB_Ptr) is
650       status : Integer;
651       pragma Unreferenced (status);
652
653    begin
654       status := setvbuf (File.Stream, Null_Address, IONBF, 0);
655    end Make_Unbuffered;
656
657    ----------
658    -- Mode --
659    ----------
660
661    function Mode (File : in AFCB_Ptr) return File_Mode is
662    begin
663       if File = null then
664          raise Status_Error;
665       else
666          return File.Mode;
667       end if;
668    end Mode;
669
670    ----------
671    -- Name --
672    ----------
673
674    function Name (File : in AFCB_Ptr) return String is
675    begin
676       if File = null then
677          raise Status_Error;
678       else
679          return File.Name.all (1 .. File.Name'Length - 1);
680       end if;
681    end Name;
682
683    ----------
684    -- Open --
685    ----------
686
687    procedure Open
688      (File_Ptr  : in out AFCB_Ptr;
689       Dummy_FCB : in AFCB'Class;
690       Mode      : File_Mode;
691       Name      : String;
692       Form      : String;
693       Amethod   : Character;
694       Creat     : Boolean;
695       Text      : Boolean;
696       C_Stream  : FILEs := NULL_Stream)
697    is
698       pragma Warnings (Off, Dummy_FCB);
699       --  Yes we know this is never assigned a value. That's intended, since
700       --  all we ever use of this value is the tag for dispatching purposes.
701
702       procedure Tmp_Name (Buffer : Address);
703       pragma Import (C, Tmp_Name, "__gnat_tmp_name");
704       --  set buffer (a String address) with a temporary filename.
705
706       Stream : FILEs := C_Stream;
707       --  Stream which we open in response to this request
708
709       Shared : Shared_Status_Type;
710       --  Setting of Shared_Status field for file
711
712       Fopstr : aliased Fopen_String;
713       --  Mode string used in fopen call
714
715       Formstr : aliased String (1 .. Form'Length + 1);
716       --  Form string with ASCII.NUL appended, folded to lower case
717
718       Tempfile : constant Boolean := (Name'Length = 0);
719       --  Indicates temporary file case
720
721       Namelen : constant Integer := max_path_len;
722       --  Length required for file name, not including final ASCII.NUL
723       --  Note that we used to reference L_tmpnam here, which is not
724       --  reliable since __gnat_tmp_name does not always use tmpnam.
725
726       Namestr : aliased String (1 .. Namelen + 1);
727       --  Name as given or temporary file name with ASCII.NUL appended
728
729       Fullname : aliased String (1 .. max_path_len + 1);
730       --  Full name (as required for Name function, and as stored in the
731       --  control block in the Name field) with ASCII.NUL appended.
732
733       Full_Name_Len : Integer;
734       --  Length of name actually stored in Fullname
735
736    begin
737       if File_Ptr /= null then
738          raise Status_Error;
739       end if;
740
741       --  Acquire form string, setting required NUL terminator
742
743       Formstr (1 .. Form'Length) := Form;
744       Formstr (Formstr'Last) := ASCII.NUL;
745
746       --  Convert form string to lower case
747
748       for J in Formstr'Range loop
749          if Formstr (J) in 'A' .. 'Z' then
750             Formstr (J) := Character'Val (Character'Pos (Formstr (J)) + 32);
751          end if;
752       end loop;
753
754       --  Acquire setting of shared parameter
755
756       declare
757          V1, V2 : Natural;
758
759       begin
760          Form_Parameter (Formstr, "shared", V1, V2);
761
762          if V1 = 0 then
763             Shared := None;
764
765          elsif Formstr (V1 .. V2) = "yes" then
766             Shared := Yes;
767
768          elsif Formstr (V1 .. V2) = "no" then
769             Shared := No;
770
771          else
772             raise Use_Error;
773          end if;
774       end;
775
776       --  If we were given a stream (call from xxx.C_Streams.Open), then set
777       --  the full name to the given one, and skip to end of processing.
778
779       if Stream /= NULL_Stream then
780          Full_Name_Len := Name'Length + 1;
781          Fullname (1 .. Full_Name_Len - 1) := Name;
782          Fullname (Full_Name_Len) := ASCII.Nul;
783
784       --  Normal case of Open or Create
785
786       else
787          --  If temporary file case, get temporary file name and add
788          --  to the list of temporary files to be deleted on exit.
789
790          if Tempfile then
791             if not Creat then
792                raise Name_Error;
793             end if;
794
795             Tmp_Name (Namestr'Address);
796
797             if Namestr (1) = ASCII.NUL then
798                raise Use_Error;
799             end if;
800
801             --  Chain to temp file list, ensuring thread safety with a lock
802
803             begin
804                SSL.Lock_Task.all;
805                Temp_Files :=
806                  new Temp_File_Record'(Name => Namestr, Next => Temp_Files);
807                SSL.Unlock_Task.all;
808
809             exception
810                when others =>
811                   SSL.Unlock_Task.all;
812                   raise;
813             end;
814
815          --  Normal case of non-null name given
816
817          else
818             if Name'Length > Namelen then
819                raise Name_Error;
820             end if;
821
822             Namestr (1 .. Name'Length) := Name;
823             Namestr (Name'Length + 1)  := ASCII.NUL;
824          end if;
825
826          --  Get full name in accordance with the advice of RM A.8.2(22).
827
828          full_name (Namestr'Address, Fullname'Address);
829
830          if Fullname (1) = ASCII.NUL then
831             raise Use_Error;
832          end if;
833
834          Full_Name_Len := 1;
835          while Full_Name_Len < Fullname'Last
836            and then Fullname (Full_Name_Len) /= ASCII.NUL
837          loop
838             Full_Name_Len := Full_Name_Len + 1;
839          end loop;
840
841          --  If Shared=None or Shared=Yes, then check for the existence
842          --  of another file with exactly the same full name.
843
844          if Shared /= No then
845             declare
846                P : AFCB_Ptr;
847
848             begin
849                --  Take a task lock to protect Open_Files
850
851                SSL.Lock_Task.all;
852
853                --  Search list of open files
854
855                P := Open_Files;
856                while P /= null loop
857                   if Fullname (1 .. Full_Name_Len) = P.Name.all then
858
859                      --  If we get a match, and either file has Shared=None,
860                      --  then raise Use_Error, since we don't allow two
861                      --  files of the same name to be opened unless they
862                      --  specify the required sharing mode.
863
864                      if Shared = None
865                        or else P.Shared_Status = None
866                      then
867                         raise Use_Error;
868
869                      --  If both files have Shared=Yes, then we acquire the
870                      --  stream from the located file to use as our stream.
871
872                      elsif Shared = Yes
873                        and then P.Shared_Status = Yes
874                      then
875                         Stream := P.Stream;
876                         exit;
877
878                      --  Otherwise one of the files has Shared=Yes and one
879                      --  has Shared=No. If the current file has Shared=No
880                      --  then all is well but we don't want to share any
881                      --  other file's stream. If the current file has
882                      --  Shared=Yes, we would like to share a stream, but
883                      --  not from a file that has Shared=No, so in either
884                      --  case we just keep going on the search.
885
886                      else
887                         null;
888                      end if;
889                   end if;
890
891                   P := P.Next;
892                end loop;
893
894                SSL.Unlock_Task.all;
895
896             exception
897                when others =>
898                   SSL.Unlock_Task.all;
899                   raise;
900             end;
901          end if;
902
903          --  Open specified file if we did not find an existing stream
904
905          if Stream = NULL_Stream then
906             Fopen_Mode (Mode, Text, Creat, Amethod, Fopstr);
907
908             --  A special case, if we are opening (OPEN case) a file and
909             --  the mode returned by Fopen_Mode is not "r" or "r+", then
910             --  we first make sure that the file exists as required by
911             --  Ada semantics.
912
913             if Creat = False and then Fopstr (1) /= 'r' then
914                if file_exists (Namestr'Address) = 0 then
915                   raise Name_Error;
916                end if;
917             end if;
918
919             --  Now open the file. Note that we use the name as given
920             --  in the original Open call for this purpose, since that
921             --  seems the clearest implementation of the intent. It
922             --  would presumably work to use the full name here, but
923             --  if there is any difference, then we should use the
924             --  name used in the call.
925
926             --  Note: for a corresponding delete, we will use the
927             --  full name, since by the time of the delete, the
928             --  current working directory may have changed and
929             --  we do not want to delete a different file!
930
931             Stream := fopen (Namestr'Address, Fopstr'Address);
932
933             if Stream = NULL_Stream then
934                if file_exists (Namestr'Address) = 0 then
935                   raise Name_Error;
936                else
937                   raise Use_Error;
938                end if;
939             end if;
940          end if;
941       end if;
942
943       --  Stream has been successfully located or opened, so now we are
944       --  committed to completing the opening of the file. Allocate block
945       --  on heap and fill in its fields.
946
947       File_Ptr := AFCB_Allocate (Dummy_FCB);
948
949       File_Ptr.Is_Regular_File   := (is_regular_file
950                                       (fileno (Stream)) /= 0);
951       File_Ptr.Is_System_File    := False;
952       File_Ptr.Is_Text_File      := Text;
953       File_Ptr.Shared_Status     := Shared;
954       File_Ptr.Access_Method     := Amethod;
955       File_Ptr.Stream            := Stream;
956       File_Ptr.Form              := new String'(Formstr);
957       File_Ptr.Name              := new String'(Fullname
958                                                  (1 .. Full_Name_Len));
959       File_Ptr.Mode              := Mode;
960       File_Ptr.Is_Temporary_File := Tempfile;
961
962       Chain_File (File_Ptr);
963       Append_Set (File_Ptr);
964    end Open;
965
966    --------------
967    -- Read_Buf --
968    --------------
969
970    procedure Read_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
971       Nread : size_t;
972
973    begin
974       Nread := fread (Buf, 1, Siz, File.Stream);
975
976       if Nread = Siz then
977          return;
978
979       elsif ferror (File.Stream) /= 0 then
980          raise Device_Error;
981
982       elsif Nread = 0 then
983          raise End_Error;
984
985       else -- 0 < Nread < Siz
986          raise Data_Error;
987       end if;
988
989    end Read_Buf;
990
991    procedure Read_Buf
992      (File  : AFCB_Ptr;
993       Buf   : Address;
994       Siz   : in Interfaces.C_Streams.size_t;
995       Count : out Interfaces.C_Streams.size_t)
996    is
997    begin
998       Count := fread (Buf, 1, Siz, File.Stream);
999
1000       if Count = 0 and then ferror (File.Stream) /= 0 then
1001          raise Device_Error;
1002       end if;
1003    end Read_Buf;
1004
1005    -----------
1006    -- Reset --
1007    -----------
1008
1009    --  The reset which does not change the mode simply does a rewind.
1010
1011    procedure Reset (File : in out AFCB_Ptr) is
1012    begin
1013       Check_File_Open (File);
1014       Reset (File, File.Mode);
1015    end Reset;
1016
1017    --  The reset with a change in mode is done using freopen, and is
1018    --  not permitted except for regular files (since otherwise there
1019    --  is no name for the freopen, and in any case it seems meaningless)
1020
1021    procedure Reset (File : in out AFCB_Ptr; Mode : in File_Mode) is
1022       Fopstr : aliased Fopen_String;
1023
1024    begin
1025       Check_File_Open (File);
1026
1027       --  Change of mode not allowed for shared file or file with no name
1028       --  or file that is not a regular file, or for a system file.
1029
1030       if File.Shared_Status = Yes
1031         or else File.Name'Length <= 1
1032         or else File.Is_System_File
1033         or else (not File.Is_Regular_File)
1034       then
1035          raise Use_Error;
1036
1037       --  For In_File or Inout_File for a regular file, we can just do a
1038       --  rewind if the mode is unchanged, which is more efficient than
1039       --  doing a full reopen.
1040
1041       elsif Mode = File.Mode
1042         and then Mode <= Inout_File
1043       then
1044          rewind (File.Stream);
1045
1046       --  Here the change of mode is permitted, we do it by reopening the
1047       --  file in the new mode and replacing the stream with a new stream.
1048
1049       else
1050          Fopen_Mode
1051            (Mode, File.Is_Text_File, False, File.Access_Method, Fopstr);
1052
1053          File.Stream :=
1054            freopen (File.Name.all'Address, Fopstr'Address, File.Stream);
1055
1056          if File.Stream = NULL_Stream then
1057             Close (File);
1058             raise Use_Error;
1059
1060          else
1061             File.Mode := Mode;
1062             Append_Set (File);
1063          end if;
1064       end if;
1065    end Reset;
1066
1067    ---------------
1068    -- Write_Buf --
1069    ---------------
1070
1071    procedure Write_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
1072    begin
1073       --  Note: for most purposes, the Siz and 1 parameters in the fwrite
1074       --  call could be reversed, but on VMS, this is a better choice, since
1075       --  for some file formats, reversing the parameters results in records
1076       --  of one byte each.
1077
1078       SSL.Abort_Defer.all;
1079
1080       if fwrite (Buf, Siz, 1, File.Stream) /= 1 then
1081          if Siz /= 0 then
1082             SSL.Abort_Undefer.all;
1083             raise Device_Error;
1084          end if;
1085       end if;
1086
1087       SSL.Abort_Undefer.all;
1088    end Write_Buf;
1089
1090 end System.File_IO;