OSDN Git Service

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