OSDN Git Service

2007-09-21 Olivier Hainque <hainque@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-2007, 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
527    begin
528       Form_Parameter (Form, Keyword, V1, V2);
529
530       if V1 = 0 then
531          return Default;
532
533       elsif Form (V1) = 'y' then
534          return True;
535
536       elsif Form (V1) = 'n' then
537          return False;
538
539       else
540          raise Use_Error;
541       end if;
542    end Form_Boolean;
543
544    ------------------
545    -- Form_Integer --
546    ------------------
547
548    function Form_Integer
549      (Form    : String;
550       Keyword : String;
551       Default : Integer)
552       return    Integer
553    is
554       V1, V2 : Natural;
555       V      : Integer;
556
557    begin
558       Form_Parameter (Form, Keyword, V1, V2);
559
560       if V1 = 0 then
561          return Default;
562
563       else
564          V := 0;
565
566          for J in V1 .. V2 loop
567             if Form (J) not in '0' .. '9' then
568                raise Use_Error;
569             else
570                V := V * 10 + Character'Pos (Form (J)) - Character'Pos ('0');
571             end if;
572
573             if V > 999_999 then
574                raise Use_Error;
575             end if;
576          end loop;
577
578          return V;
579       end if;
580    end Form_Integer;
581
582    --------------------
583    -- Form_Parameter --
584    --------------------
585
586    procedure Form_Parameter
587      (Form    : String;
588       Keyword : String;
589       Start   : out Natural;
590       Stop    : out Natural)
591   is
592       Klen : constant Integer := Keyword'Length;
593
594    --  Start of processing for Form_Parameter
595
596    begin
597       for J in Form'First + Klen .. Form'Last - 1 loop
598          if Form (J) = '='
599            and then Form (J - Klen .. J - 1) = Keyword
600          then
601             Start := J + 1;
602             Stop := Start - 1;
603
604             while Form (Stop + 1) /= ASCII.NUL
605               and then Form (Stop + 1) /= ','
606             loop
607                Stop := Stop + 1;
608             end loop;
609
610             return;
611          end if;
612       end loop;
613
614       Start := 0;
615       Stop  := 0;
616    end Form_Parameter;
617
618    -------------
619    -- Is_Open --
620    -------------
621
622    function Is_Open (File : AFCB_Ptr) return Boolean is
623    begin
624       --  We return True if the file is open, and the underlying file stream is
625       --  usable. In particular on Windows an application linked with -mwindows
626       --  option set does not have a console attached. In this case standard
627       --  files (Current_Output, Current_Error, Current_Input) are not created.
628       --  We want Is_Open (Current_Output) to return False in this case.
629
630       return File /= null and then fileno (File.Stream) /= -1;
631    end Is_Open;
632
633    -------------------
634    -- Make_Buffered --
635    -------------------
636
637    procedure Make_Buffered
638      (File    : AFCB_Ptr;
639       Buf_Siz : Interfaces.C_Streams.size_t)
640    is
641       status : Integer;
642       pragma Unreferenced (status);
643
644    begin
645       status := setvbuf (File.Stream, Null_Address, IOFBF, Buf_Siz);
646    end Make_Buffered;
647
648    ------------------------
649    -- Make_Line_Buffered --
650    ------------------------
651
652    procedure Make_Line_Buffered
653      (File     : AFCB_Ptr;
654       Line_Siz : Interfaces.C_Streams.size_t)
655    is
656       status : Integer;
657       pragma Unreferenced (status);
658
659    begin
660       status := setvbuf (File.Stream, Null_Address, IOLBF, Line_Siz);
661    end Make_Line_Buffered;
662
663    ---------------------
664    -- Make_Unbuffered --
665    ---------------------
666
667    procedure Make_Unbuffered (File : AFCB_Ptr) is
668       status : Integer;
669       pragma Unreferenced (status);
670
671    begin
672       status := setvbuf (File.Stream, Null_Address, IONBF, 0);
673    end Make_Unbuffered;
674
675    ----------
676    -- Mode --
677    ----------
678
679    function Mode (File : AFCB_Ptr) return File_Mode is
680    begin
681       if File = null then
682          raise Status_Error;
683       else
684          return File.Mode;
685       end if;
686    end Mode;
687
688    ----------
689    -- Name --
690    ----------
691
692    function Name (File : AFCB_Ptr) return String is
693    begin
694       if File = null then
695          raise Status_Error;
696       else
697          return File.Name.all (1 .. File.Name'Length - 1);
698       end if;
699    end Name;
700
701    ----------
702    -- Open --
703    ----------
704
705    procedure Open
706      (File_Ptr  : in out AFCB_Ptr;
707       Dummy_FCB : AFCB'Class;
708       Mode      : File_Mode;
709       Name      : String;
710       Form      : String;
711       Amethod   : Character;
712       Creat     : Boolean;
713       Text      : Boolean;
714       C_Stream  : FILEs := NULL_Stream)
715    is
716       pragma Warnings (Off, Dummy_FCB);
717       --  Yes we know this is never assigned a value. That's intended, since
718       --  all we ever use of this value is the tag for dispatching purposes.
719
720       procedure Tmp_Name (Buffer : Address);
721       pragma Import (C, Tmp_Name, "__gnat_tmp_name");
722       --  set buffer (a String address) with a temporary filename
723
724       Stream : FILEs := C_Stream;
725       --  Stream which we open in response to this request
726
727       Shared : Shared_Status_Type;
728       --  Setting of Shared_Status field for file
729
730       Fopstr : aliased Fopen_String;
731       --  Mode string used in fopen call
732
733       Formstr : aliased String (1 .. Form'Length + 1);
734       --  Form string with ASCII.NUL appended, folded to lower case
735
736       Tempfile : constant Boolean := (Name'Length = 0);
737       --  Indicates temporary file case
738
739       Namelen : constant Integer := max_path_len;
740       --  Length required for file name, not including final ASCII.NUL
741       --  Note that we used to reference L_tmpnam here, which is not
742       --  reliable since __gnat_tmp_name does not always use tmpnam.
743
744       Namestr : aliased String (1 .. Namelen + 1);
745       --  Name as given or temporary file name with ASCII.NUL appended
746
747       Fullname : aliased String (1 .. max_path_len + 1);
748       --  Full name (as required for Name function, and as stored in the
749       --  control block in the Name field) with ASCII.NUL appended.
750
751       Full_Name_Len : Integer;
752       --  Length of name actually stored in Fullname
753
754       Encoding : System.CRTL.Filename_Encoding;
755       --  Filename encoding specified into the form parameter
756
757    begin
758       if File_Ptr /= null then
759          raise Status_Error;
760       end if;
761
762       --  Acquire form string, setting required NUL terminator
763
764       Formstr (1 .. Form'Length) := Form;
765       Formstr (Formstr'Last) := ASCII.NUL;
766
767       --  Convert form string to lower case
768
769       for J in Formstr'Range loop
770          if Formstr (J) in 'A' .. 'Z' then
771             Formstr (J) := Character'Val (Character'Pos (Formstr (J)) + 32);
772          end if;
773       end loop;
774
775       --  Acquire setting of shared parameter
776
777       declare
778          V1, V2 : Natural;
779
780       begin
781          Form_Parameter (Formstr, "shared", V1, V2);
782
783          if V1 = 0 then
784             Shared := None;
785
786          elsif Formstr (V1 .. V2) = "yes" then
787             Shared := Yes;
788
789          elsif Formstr (V1 .. V2) = "no" then
790             Shared := No;
791
792          else
793             raise Use_Error;
794          end if;
795       end;
796
797       --  Acquire setting of shared parameter
798
799       declare
800          V1, V2 : Natural;
801
802       begin
803          Form_Parameter (Formstr, "encoding", V1, V2);
804
805          if V1 = 0 then
806             Encoding := System.CRTL.UTF8;
807
808          elsif Formstr (V1 .. V2) = "utf8" then
809             Encoding := System.CRTL.UTF8;
810
811          elsif Formstr (V1 .. V2) = "8bits" then
812             Encoding := System.CRTL.ASCII_8bits;
813
814          else
815             raise Use_Error;
816          end if;
817       end;
818
819       --  If we were given a stream (call from xxx.C_Streams.Open), then set
820       --  the full name to the given one, and skip to end of processing.
821
822       if Stream /= NULL_Stream then
823          Full_Name_Len := Name'Length + 1;
824          Fullname (1 .. Full_Name_Len - 1) := Name;
825          Fullname (Full_Name_Len) := ASCII.Nul;
826
827       --  Normal case of Open or Create
828
829       else
830          --  If temporary file case, get temporary file name and add
831          --  to the list of temporary files to be deleted on exit.
832
833          if Tempfile then
834             if not Creat then
835                raise Name_Error;
836             end if;
837
838             Tmp_Name (Namestr'Address);
839
840             if Namestr (1) = ASCII.NUL then
841                raise Use_Error;
842             end if;
843
844             --  Chain to temp file list, ensuring thread safety with a lock
845
846             begin
847                SSL.Lock_Task.all;
848                Temp_Files :=
849                  new Temp_File_Record'(Name => Namestr, Next => Temp_Files);
850                SSL.Unlock_Task.all;
851
852             exception
853                when others =>
854                   SSL.Unlock_Task.all;
855                   raise;
856             end;
857
858          --  Normal case of non-null name given
859
860          else
861             if Name'Length > Namelen then
862                raise Name_Error;
863             end if;
864
865             Namestr (1 .. Name'Length) := Name;
866             Namestr (Name'Length + 1)  := ASCII.NUL;
867          end if;
868
869          --  Get full name in accordance with the advice of RM A.8.2(22)
870
871          full_name (Namestr'Address, Fullname'Address);
872
873          if Fullname (1) = ASCII.NUL then
874             raise Use_Error;
875          end if;
876
877          Full_Name_Len := 1;
878          while Full_Name_Len < Fullname'Last
879            and then Fullname (Full_Name_Len) /= ASCII.NUL
880          loop
881             Full_Name_Len := Full_Name_Len + 1;
882          end loop;
883
884          --  Fullname is generated by calling system's full_name. The problem
885          --  is, full_name does nothing about the casing, so a file name
886          --  comparison may generally speaking not be valid on non-case
887          --  sensitive systems, and in particular we get unexpected failures
888          --  on Windows/Vista because of this. So we use s-casuti to force
889          --  the name to lower case.
890
891          if not File_Names_Case_Sensitive then
892             To_Lower (Fullname (1 .. Full_Name_Len));
893          end if;
894
895          --  If Shared=None or Shared=Yes, then check for the existence
896          --  of another file with exactly the same full name.
897
898          if Shared /= No then
899             declare
900                P : AFCB_Ptr;
901
902             begin
903                --  Take a task lock to protect Open_Files
904
905                SSL.Lock_Task.all;
906
907                --  Search list of open files
908
909                P := Open_Files;
910                while P /= null loop
911                   if Fullname (1 .. Full_Name_Len) = P.Name.all then
912
913                      --  If we get a match, and either file has Shared=None,
914                      --  then raise Use_Error, since we don't allow two files
915                      --  of the same name to be opened unless they specify the
916                      --  required sharing mode.
917
918                      if Shared = None
919                        or else P.Shared_Status = None
920                      then
921                         raise Use_Error;
922
923                      --  If both files have Shared=Yes, then we acquire the
924                      --  stream from the located file to use as our stream.
925
926                      elsif Shared = Yes
927                        and then P.Shared_Status = Yes
928                      then
929                         Stream := P.Stream;
930                         exit;
931
932                      --  Otherwise one of the files has Shared=Yes and one has
933                      --  Shared=No. If the current file has Shared=No then all
934                      --  is well but we don't want to share any other file's
935                      --  stream. If the current file has Shared=Yes, we would
936                      --  like to share a stream, but not from a file that has
937                      --  Shared=No, so either way, we just continue the search.
938
939                      else
940                         null;
941                      end if;
942                   end if;
943
944                   P := P.Next;
945                end loop;
946
947                SSL.Unlock_Task.all;
948
949             exception
950                when others =>
951                   SSL.Unlock_Task.all;
952                   raise;
953             end;
954          end if;
955
956          --  Open specified file if we did not find an existing stream
957
958          if Stream = NULL_Stream then
959             Fopen_Mode (Mode, Text, Creat, Amethod, Fopstr);
960
961             --  A special case, if we are opening (OPEN case) a file and the
962             --  mode returned by Fopen_Mode is not "r" or "r+", then we first
963             --  make sure that the file exists as required by Ada semantics.
964
965             if Creat = False and then Fopstr (1) /= 'r' then
966                if file_exists (Namestr'Address) = 0 then
967                   raise Name_Error;
968                end if;
969             end if;
970
971             --  Now open the file. Note that we use the name as given in the
972             --  original Open call for this purpose, since that seems the
973             --  clearest implementation of the intent. It would presumably
974             --  work to use the full name here, but if there is any difference,
975             --  then we should use the name used in the call.
976
977             --  Note: for a corresponding delete, we will use the full name,
978             --  since by the time of the delete, the current working directory
979             --  may have changed and we do not want to delete a different file!
980
981             Stream := fopen (Namestr'Address, Fopstr'Address, Encoding);
982
983             if Stream = NULL_Stream then
984                if file_exists (Namestr'Address) = 0 then
985                   raise Name_Error;
986                else
987                   raise Use_Error;
988                end if;
989             end if;
990          end if;
991       end if;
992
993       --  Stream has been successfully located or opened, so now we are
994       --  committed to completing the opening of the file. Allocate block
995       --  on heap and fill in its fields.
996
997       File_Ptr := AFCB_Allocate (Dummy_FCB);
998
999       File_Ptr.Is_Regular_File   := (is_regular_file (fileno (Stream)) /= 0);
1000       File_Ptr.Is_System_File    := False;
1001       File_Ptr.Is_Text_File      := Text;
1002       File_Ptr.Shared_Status     := Shared;
1003       File_Ptr.Access_Method     := Amethod;
1004       File_Ptr.Stream            := Stream;
1005       File_Ptr.Form              := new String'(Formstr);
1006       File_Ptr.Name              := new String'(Fullname (1 .. Full_Name_Len));
1007       File_Ptr.Mode              := Mode;
1008       File_Ptr.Is_Temporary_File := Tempfile;
1009       File_Ptr.Encoding          := Encoding;
1010
1011       Chain_File (File_Ptr);
1012       Append_Set (File_Ptr);
1013    end Open;
1014
1015    --------------
1016    -- Read_Buf --
1017    --------------
1018
1019    procedure Read_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
1020       Nread : size_t;
1021
1022    begin
1023       Nread := fread (Buf, 1, Siz, File.Stream);
1024
1025       if Nread = Siz then
1026          return;
1027
1028       elsif ferror (File.Stream) /= 0 then
1029          raise Device_Error;
1030
1031       elsif Nread = 0 then
1032          raise End_Error;
1033
1034       else -- 0 < Nread < Siz
1035          raise Data_Error;
1036       end if;
1037
1038    end Read_Buf;
1039
1040    procedure Read_Buf
1041      (File  : AFCB_Ptr;
1042       Buf   : Address;
1043       Siz   : Interfaces.C_Streams.size_t;
1044       Count : out Interfaces.C_Streams.size_t)
1045    is
1046    begin
1047       Count := fread (Buf, 1, Siz, File.Stream);
1048
1049       if Count = 0 and then ferror (File.Stream) /= 0 then
1050          raise Device_Error;
1051       end if;
1052    end Read_Buf;
1053
1054    -----------
1055    -- Reset --
1056    -----------
1057
1058    --  The reset which does not change the mode simply does a rewind
1059
1060    procedure Reset (File : in out AFCB_Ptr) is
1061    begin
1062       Check_File_Open (File);
1063       Reset (File, File.Mode);
1064    end Reset;
1065
1066    --  The reset with a change in mode is done using freopen, and is
1067    --  not permitted except for regular files (since otherwise there
1068    --  is no name for the freopen, and in any case it seems meaningless)
1069
1070    procedure Reset (File : in out AFCB_Ptr; Mode : File_Mode) is
1071       Fopstr : aliased Fopen_String;
1072
1073    begin
1074       Check_File_Open (File);
1075
1076       --  Change of mode not allowed for shared file or file with no name
1077       --  or file that is not a regular file, or for a system file.
1078
1079       if File.Shared_Status = Yes
1080         or else File.Name'Length <= 1
1081         or else File.Is_System_File
1082         or else (not File.Is_Regular_File)
1083       then
1084          raise Use_Error;
1085
1086       --  For In_File or Inout_File for a regular file, we can just do a
1087       --  rewind if the mode is unchanged, which is more efficient than
1088       --  doing a full reopen.
1089
1090       elsif Mode = File.Mode
1091         and then Mode <= Inout_File
1092       then
1093          rewind (File.Stream);
1094
1095       --  Here the change of mode is permitted, we do it by reopening the
1096       --  file in the new mode and replacing the stream with a new stream.
1097
1098       else
1099          Fopen_Mode
1100            (Mode, File.Is_Text_File, False, File.Access_Method, Fopstr);
1101
1102          File.Stream := freopen
1103            (File.Name.all'Address, Fopstr'Address, File.Stream, File.Encoding);
1104
1105          if File.Stream = NULL_Stream then
1106             Close (File);
1107             raise Use_Error;
1108
1109          else
1110             File.Mode := Mode;
1111             Append_Set (File);
1112          end if;
1113       end if;
1114    end Reset;
1115
1116    ---------------
1117    -- Write_Buf --
1118    ---------------
1119
1120    procedure Write_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
1121    begin
1122       --  Note: for most purposes, the Siz and 1 parameters in the fwrite
1123       --  call could be reversed, but on VMS, this is a better choice, since
1124       --  for some file formats, reversing the parameters results in records
1125       --  of one byte each.
1126
1127       SSL.Abort_Defer.all;
1128
1129       if fwrite (Buf, Siz, 1, File.Stream) /= 1 then
1130          if Siz /= 0 then
1131             SSL.Abort_Undefer.all;
1132             raise Device_Error;
1133          end if;
1134       end if;
1135
1136       SSL.Abort_Undefer.all;
1137    end Write_Buf;
1138
1139 end System.File_IO;