OSDN Git Service

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