OSDN Git Service

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