OSDN Git Service

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