1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME COMPONENTS -- 4-- -- 5-- A D A . W I D E _ T E X T _ I O -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1992-2020, 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 32with Ada.Streams; use Ada.Streams; 33with Interfaces.C_Streams; use Interfaces.C_Streams; 34 35with System.CRTL; 36with System.File_IO; 37with System.WCh_Cnv; use System.WCh_Cnv; 38with System.WCh_Con; use System.WCh_Con; 39 40with Ada.Unchecked_Conversion; 41with Ada.Unchecked_Deallocation; 42 43pragma Elaborate_All (System.File_IO); 44-- Needed because of calls to Chain_File in package body elaboration 45 46package body Ada.Wide_Text_IO is 47 48 package FIO renames System.File_IO; 49 50 subtype AP is FCB.AFCB_Ptr; 51 52 function To_FCB is new Ada.Unchecked_Conversion (File_Mode, FCB.File_Mode); 53 function To_TIO is new Ada.Unchecked_Conversion (FCB.File_Mode, File_Mode); 54 use type FCB.File_Mode; 55 56 use type System.CRTL.size_t; 57 58 WC_Encoding : Character; 59 pragma Import (C, WC_Encoding, "__gl_wc_encoding"); 60 -- Default wide character encoding 61 62 Err_Name : aliased String := "*stderr" & ASCII.NUL; 63 In_Name : aliased String := "*stdin" & ASCII.NUL; 64 Out_Name : aliased String := "*stdout" & ASCII.NUL; 65 -- Names of standard files 66 -- 67 -- Use "preallocated" strings to avoid calling "new" during the elaboration 68 -- of the run time. This is needed in the tasking case to avoid calling 69 -- Task_Lock too early. A filename is expected to end with a null character 70 -- in the runtime, here the null characters are added just to have a 71 -- correct filename length. 72 -- 73 -- Note: the names for these files are bogus, and probably it would be 74 -- better for these files to have no names, but the ACVC tests insist. 75 -- We use names that are bound to fail in open etc. 76 77 Null_Str : aliased constant String := ""; 78 -- Used as form string for standard files 79 80 ----------------------- 81 -- Local Subprograms -- 82 ----------------------- 83 84 function Get_Wide_Char_Immed 85 (C : Character; 86 File : File_Type) return Wide_Character; 87 -- This routine is identical to Get_Wide_Char, except that the reads are 88 -- done in Get_Immediate mode (i.e. without waiting for a line return). 89 90 function Getc_Immed (File : File_Type) return int; 91 -- This routine is identical to Getc, except that the read is done in 92 -- Get_Immediate mode (i.e. without waiting for a line return). 93 94 procedure Putc (ch : int; File : File_Type); 95 -- Outputs the given character to the file, which has already been checked 96 -- for being in output status. Device_Error is raised if the character 97 -- cannot be written. 98 99 procedure Set_WCEM (File : in out File_Type); 100 -- Called by Open and Create to set the wide character encoding method for 101 -- the file, processing a WCEM form parameter if one is present. File is 102 -- IN OUT because it may be closed in case of an error. 103 104 procedure Terminate_Line (File : File_Type); 105 -- If the file is in Write_File or Append_File mode, and the current line 106 -- is not terminated, then a line terminator is written using New_Line. 107 -- Note that there is no Terminate_Page routine, because the page mark at 108 -- the end of the file is implied if necessary. 109 110 procedure Ungetc (ch : int; File : File_Type); 111 -- Pushes back character into stream, using ungetc. The caller has checked 112 -- that the file is in read status. Device_Error is raised if the character 113 -- cannot be pushed back. An attempt to push back and end of file character 114 -- (EOF) is ignored. 115 116 ------------------- 117 -- AFCB_Allocate -- 118 ------------------- 119 120 function AFCB_Allocate 121 (Control_Block : Wide_Text_AFCB) return FCB.AFCB_Ptr 122 is 123 pragma Unreferenced (Control_Block); 124 begin 125 return new Wide_Text_AFCB; 126 end AFCB_Allocate; 127 128 ---------------- 129 -- AFCB_Close -- 130 ---------------- 131 132 procedure AFCB_Close (File : not null access Wide_Text_AFCB) is 133 begin 134 -- If the file being closed is one of the current files, then close 135 -- the corresponding current file. It is not clear that this action 136 -- is required (RM A.10.3(23)) but it seems reasonable, and besides 137 -- ACVC test CE3208A expects this behavior. 138 139 if File_Type (File) = Current_In then 140 Current_In := null; 141 elsif File_Type (File) = Current_Out then 142 Current_Out := null; 143 elsif File_Type (File) = Current_Err then 144 Current_Err := null; 145 end if; 146 147 Terminate_Line (File_Type (File)); 148 end AFCB_Close; 149 150 --------------- 151 -- AFCB_Free -- 152 --------------- 153 154 procedure AFCB_Free (File : not null access Wide_Text_AFCB) is 155 type FCB_Ptr is access all Wide_Text_AFCB; 156 FT : FCB_Ptr := FCB_Ptr (File); 157 158 procedure Free is 159 new Ada.Unchecked_Deallocation (Wide_Text_AFCB, FCB_Ptr); 160 161 begin 162 Free (FT); 163 end AFCB_Free; 164 165 ----------- 166 -- Close -- 167 ----------- 168 169 procedure Close (File : in out File_Type) is 170 begin 171 FIO.Close (AP (File)'Unrestricted_Access); 172 end Close; 173 174 --------- 175 -- Col -- 176 --------- 177 178 -- Note: we assume that it is impossible in practice for the column 179 -- to exceed the value of Count'Last, i.e. no check is required for 180 -- overflow raising layout error. 181 182 function Col (File : File_Type) return Positive_Count is 183 begin 184 FIO.Check_File_Open (AP (File)); 185 return File.Col; 186 end Col; 187 188 function Col return Positive_Count is 189 begin 190 return Col (Current_Out); 191 end Col; 192 193 ------------ 194 -- Create -- 195 ------------ 196 197 procedure Create 198 (File : in out File_Type; 199 Mode : File_Mode := Out_File; 200 Name : String := ""; 201 Form : String := "") 202 is 203 Dummy_File_Control_Block : Wide_Text_AFCB; 204 pragma Warnings (Off, Dummy_File_Control_Block); 205 -- Yes, we know this is never assigned a value, only the tag 206 -- is used for dispatching purposes, so that's expected. 207 208 begin 209 FIO.Open (File_Ptr => AP (File), 210 Dummy_FCB => Dummy_File_Control_Block, 211 Mode => To_FCB (Mode), 212 Name => Name, 213 Form => Form, 214 Amethod => 'W', 215 Creat => True, 216 Text => True); 217 218 File.Self := File; 219 Set_WCEM (File); 220 end Create; 221 222 ------------------- 223 -- Current_Error -- 224 ------------------- 225 226 function Current_Error return File_Type is 227 begin 228 return Current_Err; 229 end Current_Error; 230 231 function Current_Error return File_Access is 232 begin 233 return Current_Err.Self'Access; 234 end Current_Error; 235 236 ------------------- 237 -- Current_Input -- 238 ------------------- 239 240 function Current_Input return File_Type is 241 begin 242 return Current_In; 243 end Current_Input; 244 245 function Current_Input return File_Access is 246 begin 247 return Current_In.Self'Access; 248 end Current_Input; 249 250 -------------------- 251 -- Current_Output -- 252 -------------------- 253 254 function Current_Output return File_Type is 255 begin 256 return Current_Out; 257 end Current_Output; 258 259 function Current_Output return File_Access is 260 begin 261 return Current_Out.Self'Access; 262 end Current_Output; 263 264 ------------ 265 -- Delete -- 266 ------------ 267 268 procedure Delete (File : in out File_Type) is 269 begin 270 FIO.Delete (AP (File)'Unrestricted_Access); 271 end Delete; 272 273 ----------------- 274 -- End_Of_File -- 275 ----------------- 276 277 function End_Of_File (File : File_Type) return Boolean is 278 ch : int; 279 280 begin 281 FIO.Check_Read_Status (AP (File)); 282 283 if File.Before_Wide_Character then 284 return False; 285 286 elsif File.Before_LM then 287 if File.Before_LM_PM then 288 return Nextc (File) = EOF; 289 end if; 290 291 else 292 ch := Getc (File); 293 294 if ch = EOF then 295 return True; 296 297 elsif ch /= LM then 298 Ungetc (ch, File); 299 return False; 300 301 else -- ch = LM 302 File.Before_LM := True; 303 end if; 304 end if; 305 306 -- Here we are just past the line mark with Before_LM set so that we 307 -- do not have to try to back up past the LM, thus avoiding the need 308 -- to back up more than one character. 309 310 ch := Getc (File); 311 312 if ch = EOF then 313 return True; 314 315 elsif ch = PM and then File.Is_Regular_File then 316 File.Before_LM_PM := True; 317 return Nextc (File) = EOF; 318 319 -- Here if neither EOF nor PM followed end of line 320 321 else 322 Ungetc (ch, File); 323 return False; 324 end if; 325 326 end End_Of_File; 327 328 function End_Of_File return Boolean is 329 begin 330 return End_Of_File (Current_In); 331 end End_Of_File; 332 333 ----------------- 334 -- End_Of_Line -- 335 ----------------- 336 337 function End_Of_Line (File : File_Type) return Boolean is 338 ch : int; 339 340 begin 341 FIO.Check_Read_Status (AP (File)); 342 343 if File.Before_Wide_Character then 344 return False; 345 346 elsif File.Before_LM then 347 return True; 348 349 else 350 ch := Getc (File); 351 352 if ch = EOF then 353 return True; 354 355 else 356 Ungetc (ch, File); 357 return (ch = LM); 358 end if; 359 end if; 360 end End_Of_Line; 361 362 function End_Of_Line return Boolean is 363 begin 364 return End_Of_Line (Current_In); 365 end End_Of_Line; 366 367 ----------------- 368 -- End_Of_Page -- 369 ----------------- 370 371 function End_Of_Page (File : File_Type) return Boolean is 372 ch : int; 373 374 begin 375 FIO.Check_Read_Status (AP (File)); 376 377 if not File.Is_Regular_File then 378 return False; 379 380 elsif File.Before_Wide_Character then 381 return False; 382 383 elsif File.Before_LM then 384 if File.Before_LM_PM then 385 return True; 386 end if; 387 388 else 389 ch := Getc (File); 390 391 if ch = EOF then 392 return True; 393 394 elsif ch /= LM then 395 Ungetc (ch, File); 396 return False; 397 398 else -- ch = LM 399 File.Before_LM := True; 400 end if; 401 end if; 402 403 -- Here we are just past the line mark with Before_LM set so that we 404 -- do not have to try to back up past the LM, thus avoiding the need 405 -- to back up more than one character. 406 407 ch := Nextc (File); 408 409 return ch = PM or else ch = EOF; 410 end End_Of_Page; 411 412 function End_Of_Page return Boolean is 413 begin 414 return End_Of_Page (Current_In); 415 end End_Of_Page; 416 417 ----------- 418 -- Flush -- 419 ----------- 420 421 procedure Flush (File : File_Type) is 422 begin 423 FIO.Flush (AP (File)); 424 end Flush; 425 426 procedure Flush is 427 begin 428 Flush (Current_Out); 429 end Flush; 430 431 ---------- 432 -- Form -- 433 ---------- 434 435 function Form (File : File_Type) return String is 436 begin 437 return FIO.Form (AP (File)); 438 end Form; 439 440 --------- 441 -- Get -- 442 --------- 443 444 procedure Get 445 (File : File_Type; 446 Item : out Wide_Character) 447 is 448 C : Character; 449 450 begin 451 FIO.Check_Read_Status (AP (File)); 452 453 if File.Before_Wide_Character then 454 File.Before_Wide_Character := False; 455 Item := File.Saved_Wide_Character; 456 457 -- Ada.Text_IO checks Before_LM_PM here, shouldn't we do the same??? 458 459 else 460 Get_Character (File, C); 461 Item := Get_Wide_Char (C, File); 462 end if; 463 end Get; 464 465 procedure Get (Item : out Wide_Character) is 466 begin 467 Get (Current_In, Item); 468 end Get; 469 470 procedure Get 471 (File : File_Type; 472 Item : out Wide_String) 473 is 474 begin 475 for J in Item'Range loop 476 Get (File, Item (J)); 477 end loop; 478 end Get; 479 480 procedure Get (Item : out Wide_String) is 481 begin 482 Get (Current_In, Item); 483 end Get; 484 485 ------------------- 486 -- Get_Character -- 487 ------------------- 488 489 procedure Get_Character 490 (File : File_Type; 491 Item : out Character) 492 is 493 ch : int; 494 495 begin 496 if File.Before_LM then 497 File.Before_LM := False; 498 File.Before_LM_PM := False; 499 File.Col := 1; 500 501 if File.Before_LM_PM then 502 File.Line := 1; 503 File.Page := File.Page + 1; 504 File.Before_LM_PM := False; 505 506 else 507 File.Line := File.Line + 1; 508 end if; 509 end if; 510 511 loop 512 ch := Getc (File); 513 514 if ch = EOF then 515 raise End_Error; 516 517 elsif ch = LM then 518 File.Line := File.Line + 1; 519 File.Col := 1; 520 521 elsif ch = PM and then File.Is_Regular_File then 522 File.Page := File.Page + 1; 523 File.Line := 1; 524 525 else 526 Item := Character'Val (ch); 527 File.Col := File.Col + 1; 528 return; 529 end if; 530 end loop; 531 end Get_Character; 532 533 ------------------- 534 -- Get_Immediate -- 535 ------------------- 536 537 procedure Get_Immediate 538 (File : File_Type; 539 Item : out Wide_Character) 540 is 541 ch : int; 542 543 begin 544 FIO.Check_Read_Status (AP (File)); 545 546 if File.Before_Wide_Character then 547 File.Before_Wide_Character := False; 548 Item := File.Saved_Wide_Character; 549 550 elsif File.Before_LM then 551 File.Before_LM := False; 552 File.Before_LM_PM := False; 553 Item := Wide_Character'Val (LM); 554 555 else 556 ch := Getc_Immed (File); 557 558 if ch = EOF then 559 raise End_Error; 560 else 561 Item := Get_Wide_Char_Immed (Character'Val (ch), File); 562 end if; 563 end if; 564 end Get_Immediate; 565 566 procedure Get_Immediate 567 (Item : out Wide_Character) 568 is 569 begin 570 Get_Immediate (Current_In, Item); 571 end Get_Immediate; 572 573 procedure Get_Immediate 574 (File : File_Type; 575 Item : out Wide_Character; 576 Available : out Boolean) 577 is 578 ch : int; 579 580 begin 581 FIO.Check_Read_Status (AP (File)); 582 Available := True; 583 584 if File.Before_Wide_Character then 585 File.Before_Wide_Character := False; 586 Item := File.Saved_Wide_Character; 587 588 elsif File.Before_LM then 589 File.Before_LM := False; 590 File.Before_LM_PM := False; 591 Item := Wide_Character'Val (LM); 592 593 else 594 -- Shouldn't we use getc_immediate_nowait here, like Text_IO??? 595 596 ch := Getc_Immed (File); 597 598 if ch = EOF then 599 raise End_Error; 600 else 601 Item := Get_Wide_Char_Immed (Character'Val (ch), File); 602 end if; 603 end if; 604 end Get_Immediate; 605 606 procedure Get_Immediate 607 (Item : out Wide_Character; 608 Available : out Boolean) 609 is 610 begin 611 Get_Immediate (Current_In, Item, Available); 612 end Get_Immediate; 613 614 -------------- 615 -- Get_Line -- 616 -------------- 617 618 procedure Get_Line 619 (File : File_Type; 620 Item : out Wide_String; 621 Last : out Natural) 622 is 623 begin 624 FIO.Check_Read_Status (AP (File)); 625 Last := Item'First - 1; 626 627 -- Immediate exit for null string, this is a case in which we do not 628 -- need to test for end of file and we do not skip a line mark under 629 -- any circumstances. 630 631 if Last >= Item'Last then 632 return; 633 end if; 634 635 -- Here we have at least one character, if we are immediately before 636 -- a line mark, then we will just skip past it storing no characters. 637 638 if File.Before_LM then 639 File.Before_LM := False; 640 File.Before_LM_PM := False; 641 642 -- Otherwise we need to read some characters 643 644 else 645 -- If we are at the end of file now, it means we are trying to 646 -- skip a file terminator and we raise End_Error (RM A.10.7(20)) 647 648 if Nextc (File) = EOF then 649 raise End_Error; 650 end if; 651 652 -- Loop through characters in string 653 654 loop 655 -- Exit the loop if read is terminated by encountering line mark 656 -- Note that the use of Skip_Line here ensures we properly deal 657 -- with setting the page and line numbers. 658 659 if End_Of_Line (File) then 660 Skip_Line (File); 661 return; 662 end if; 663 664 -- Otherwise store the character, note that we know that ch is 665 -- something other than LM or EOF. It could possibly be a page 666 -- mark if there is a stray page mark in the middle of a line, but 667 -- this is not an official page mark in any case, since official 668 -- page marks can only follow a line mark. The whole page business 669 -- is pretty much nonsense anyway, so we do not want to waste 670 -- time trying to make sense out of non-standard page marks in 671 -- the file. This means that the behavior of Get_Line is different 672 -- from repeated Get of a character, but that's too bad. We 673 -- only promise that page numbers etc make sense if the file 674 -- is formatted in a standard manner. 675 676 -- Note: we do not adjust the column number because it is quicker 677 -- to adjust it once at the end of the operation than incrementing 678 -- it each time around the loop. 679 680 Last := Last + 1; 681 Get (File, Item (Last)); 682 683 -- All done if the string is full, this is the case in which 684 -- we do not skip the following line mark. We need to adjust 685 -- the column number in this case. 686 687 if Last = Item'Last then 688 File.Col := File.Col + Count (Item'Length); 689 return; 690 end if; 691 692 -- Exit from the loop if we are at the end of file. This happens 693 -- if we have a last line that is not terminated with a line mark. 694 -- In this case we consider that there is an implied line mark; 695 -- this is a non-standard file, but we will treat it nicely. 696 697 exit when Nextc (File) = EOF; 698 end loop; 699 end if; 700 end Get_Line; 701 702 procedure Get_Line 703 (Item : out Wide_String; 704 Last : out Natural) 705 is 706 begin 707 Get_Line (Current_In, Item, Last); 708 end Get_Line; 709 710 function Get_Line (File : File_Type) return Wide_String is 711 Buffer : Wide_String (1 .. 500); 712 Last : Natural; 713 714 function Get_Rest (S : Wide_String) return Wide_String; 715 -- This is a recursive function that reads the rest of the line and 716 -- returns it. S is the part read so far. 717 718 -------------- 719 -- Get_Rest -- 720 -------------- 721 722 function Get_Rest (S : Wide_String) return Wide_String is 723 724 -- Each time we allocate a buffer the same size as what we have 725 -- read so far. This limits us to a logarithmic number of calls 726 -- to Get_Rest and also ensures only a linear use of stack space. 727 728 Buffer : Wide_String (1 .. S'Length); 729 Last : Natural; 730 731 begin 732 Get_Line (File, Buffer, Last); 733 734 declare 735 R : constant Wide_String := S & Buffer (1 .. Last); 736 begin 737 if Last < Buffer'Last then 738 return R; 739 else 740 return Get_Rest (R); 741 end if; 742 end; 743 end Get_Rest; 744 745 -- Start of processing for Get_Line 746 747 begin 748 Get_Line (File, Buffer, Last); 749 750 if Last < Buffer'Last then 751 return Buffer (1 .. Last); 752 else 753 return Get_Rest (Buffer (1 .. Last)); 754 end if; 755 end Get_Line; 756 757 function Get_Line return Wide_String is 758 begin 759 return Get_Line (Current_In); 760 end Get_Line; 761 762 ------------------- 763 -- Get_Wide_Char -- 764 ------------------- 765 766 function Get_Wide_Char 767 (C : Character; 768 File : File_Type) return Wide_Character 769 is 770 function In_Char return Character; 771 -- Function used to obtain additional characters it the wide character 772 -- sequence is more than one character long. 773 774 function WC_In is new Char_Sequence_To_Wide_Char (In_Char); 775 776 ------------- 777 -- In_Char -- 778 ------------- 779 780 function In_Char return Character is 781 ch : constant Integer := Getc (File); 782 begin 783 if ch = EOF then 784 raise End_Error; 785 else 786 return Character'Val (ch); 787 end if; 788 end In_Char; 789 790 -- Start of processing for Get_Wide_Char 791 792 begin 793 FIO.Check_Read_Status (AP (File)); 794 return WC_In (C, File.WC_Method); 795 end Get_Wide_Char; 796 797 ------------------------- 798 -- Get_Wide_Char_Immed -- 799 ------------------------- 800 801 function Get_Wide_Char_Immed 802 (C : Character; 803 File : File_Type) return Wide_Character 804 is 805 function In_Char return Character; 806 -- Function used to obtain additional characters it the wide character 807 -- sequence is more than one character long. 808 809 function WC_In is new Char_Sequence_To_Wide_Char (In_Char); 810 811 ------------- 812 -- In_Char -- 813 ------------- 814 815 function In_Char return Character is 816 ch : constant Integer := Getc_Immed (File); 817 begin 818 if ch = EOF then 819 raise End_Error; 820 else 821 return Character'Val (ch); 822 end if; 823 end In_Char; 824 825 -- Start of processing for Get_Wide_Char_Immed 826 827 begin 828 FIO.Check_Read_Status (AP (File)); 829 return WC_In (C, File.WC_Method); 830 end Get_Wide_Char_Immed; 831 832 ---------- 833 -- Getc -- 834 ---------- 835 836 function Getc (File : File_Type) return int is 837 ch : int; 838 839 begin 840 ch := fgetc (File.Stream); 841 842 if ch = EOF and then ferror (File.Stream) /= 0 then 843 raise Device_Error; 844 else 845 return ch; 846 end if; 847 end Getc; 848 849 ---------------- 850 -- Getc_Immed -- 851 ---------------- 852 853 function Getc_Immed (File : File_Type) return int is 854 ch : int; 855 end_of_file : int; 856 857 procedure getc_immediate 858 (stream : FILEs; ch : out int; end_of_file : out int); 859 pragma Import (C, getc_immediate, "getc_immediate"); 860 861 begin 862 FIO.Check_Read_Status (AP (File)); 863 864 if File.Before_LM then 865 File.Before_LM := False; 866 File.Before_LM_PM := False; 867 ch := LM; 868 869 else 870 getc_immediate (File.Stream, ch, end_of_file); 871 872 if ferror (File.Stream) /= 0 then 873 raise Device_Error; 874 elsif end_of_file /= 0 then 875 return EOF; 876 end if; 877 end if; 878 879 return ch; 880 end Getc_Immed; 881 882 ------------------------------- 883 -- Initialize_Standard_Files -- 884 ------------------------------- 885 886 procedure Initialize_Standard_Files is 887 begin 888 Standard_Err.Stream := stderr; 889 Standard_Err.Name := Err_Name'Access; 890 Standard_Err.Form := Null_Str'Unrestricted_Access; 891 Standard_Err.Mode := FCB.Out_File; 892 Standard_Err.Is_Regular_File := is_regular_file (fileno (stderr)) /= 0; 893 Standard_Err.Is_Temporary_File := False; 894 Standard_Err.Is_System_File := True; 895 Standard_Err.Text_Encoding := Default_Text; 896 Standard_Err.Access_Method := 'T'; 897 Standard_Err.Self := Standard_Err; 898 Standard_Err.WC_Method := Default_WCEM; 899 900 Standard_In.Stream := stdin; 901 Standard_In.Name := In_Name'Access; 902 Standard_In.Form := Null_Str'Unrestricted_Access; 903 Standard_In.Mode := FCB.In_File; 904 Standard_In.Is_Regular_File := is_regular_file (fileno (stdin)) /= 0; 905 Standard_In.Is_Temporary_File := False; 906 Standard_In.Is_System_File := True; 907 Standard_In.Text_Encoding := Default_Text; 908 Standard_In.Access_Method := 'T'; 909 Standard_In.Self := Standard_In; 910 Standard_In.WC_Method := Default_WCEM; 911 912 Standard_Out.Stream := stdout; 913 Standard_Out.Name := Out_Name'Access; 914 Standard_Out.Form := Null_Str'Unrestricted_Access; 915 Standard_Out.Mode := FCB.Out_File; 916 Standard_Out.Is_Regular_File := is_regular_file (fileno (stdout)) /= 0; 917 Standard_Out.Is_Temporary_File := False; 918 Standard_Out.Is_System_File := True; 919 Standard_Out.Text_Encoding := Default_Text; 920 Standard_Out.Access_Method := 'T'; 921 Standard_Out.Self := Standard_Out; 922 Standard_Out.WC_Method := Default_WCEM; 923 924 FIO.Make_Unbuffered (AP (Standard_Out)); 925 FIO.Make_Unbuffered (AP (Standard_Err)); 926 end Initialize_Standard_Files; 927 928 ------------- 929 -- Is_Open -- 930 ------------- 931 932 function Is_Open (File : File_Type) return Boolean is 933 begin 934 return FIO.Is_Open (AP (File)); 935 end Is_Open; 936 937 ---------- 938 -- Line -- 939 ---------- 940 941 -- Note: we assume that it is impossible in practice for the line to exceed 942 -- the value of Count'Last, i.e. no check is required for overflow raising 943 -- layout error. 944 945 function Line (File : File_Type) return Positive_Count is 946 begin 947 FIO.Check_File_Open (AP (File)); 948 return File.Line; 949 end Line; 950 951 function Line return Positive_Count is 952 begin 953 return Line (Current_Out); 954 end Line; 955 956 ----------------- 957 -- Line_Length -- 958 ----------------- 959 960 function Line_Length (File : File_Type) return Count is 961 begin 962 FIO.Check_Write_Status (AP (File)); 963 return File.Line_Length; 964 end Line_Length; 965 966 function Line_Length return Count is 967 begin 968 return Line_Length (Current_Out); 969 end Line_Length; 970 971 ---------------- 972 -- Look_Ahead -- 973 ---------------- 974 975 procedure Look_Ahead 976 (File : File_Type; 977 Item : out Wide_Character; 978 End_Of_Line : out Boolean) 979 is 980 ch : int; 981 982 -- Start of processing for Look_Ahead 983 984 begin 985 FIO.Check_Read_Status (AP (File)); 986 987 -- If we are logically before a line mark, we can return immediately 988 989 if File.Before_LM then 990 End_Of_Line := True; 991 Item := Wide_Character'Val (0); 992 993 -- If we are before a wide character, just return it (this can happen 994 -- if there are two calls to Look_Ahead in a row). 995 996 elsif File.Before_Wide_Character then 997 End_Of_Line := False; 998 Item := File.Saved_Wide_Character; 999 1000 -- otherwise we must read a character from the input stream 1001 1002 else 1003 ch := Getc (File); 1004 1005 if ch = LM 1006 or else ch = EOF 1007 or else (ch = EOF and then File.Is_Regular_File) 1008 then 1009 End_Of_Line := True; 1010 Ungetc (ch, File); 1011 Item := Wide_Character'Val (0); 1012 1013 -- Case where character obtained does not represent the start of an 1014 -- encoded sequence so it stands for itself and we can unget it with 1015 -- no difficulty. 1016 1017 elsif not Is_Start_Of_Encoding 1018 (Character'Val (ch), File.WC_Method) 1019 then 1020 End_Of_Line := False; 1021 Ungetc (ch, File); 1022 Item := Wide_Character'Val (ch); 1023 1024 -- For the start of an encoding, we read the character using the 1025 -- Get_Wide_Char routine. It will occupy more than one byte so we 1026 -- can't put it back with ungetc. Instead we save it in the control 1027 -- block, setting a flag that everyone interested in reading 1028 -- characters must test before reading the stream. 1029 1030 else 1031 Item := Get_Wide_Char (Character'Val (ch), File); 1032 End_Of_Line := False; 1033 File.Saved_Wide_Character := Item; 1034 File.Before_Wide_Character := True; 1035 end if; 1036 end if; 1037 end Look_Ahead; 1038 1039 procedure Look_Ahead 1040 (Item : out Wide_Character; 1041 End_Of_Line : out Boolean) 1042 is 1043 begin 1044 Look_Ahead (Current_In, Item, End_Of_Line); 1045 end Look_Ahead; 1046 1047 ---------- 1048 -- Mode -- 1049 ---------- 1050 1051 function Mode (File : File_Type) return File_Mode is 1052 begin 1053 return To_TIO (FIO.Mode (AP (File))); 1054 end Mode; 1055 1056 ---------- 1057 -- Name -- 1058 ---------- 1059 1060 function Name (File : File_Type) return String is 1061 begin 1062 return FIO.Name (AP (File)); 1063 end Name; 1064 1065 -------------- 1066 -- New_Line -- 1067 -------------- 1068 1069 procedure New_Line 1070 (File : File_Type; 1071 Spacing : Positive_Count := 1) 1072 is 1073 begin 1074 -- Raise Constraint_Error if out of range value. The reason for this 1075 -- explicit test is that we don't want junk values around, even if 1076 -- checks are off in the caller. 1077 1078 if not Spacing'Valid then 1079 raise Constraint_Error; 1080 end if; 1081 1082 FIO.Check_Write_Status (AP (File)); 1083 1084 for K in 1 .. Spacing loop 1085 1086 -- We use Put here (rather than Putc) so that we get the proper 1087 -- behavior on windows for output of Wide_String to the console. 1088 1089 Put (File, Wide_Character'Val (LM)); 1090 1091 File.Line := File.Line + 1; 1092 1093 if File.Page_Length /= 0 and then File.Line > File.Page_Length then 1094 1095 -- Same situation as above, use Put instead of Putc 1096 1097 Put (File, Wide_Character'Val (PM)); 1098 1099 File.Line := 1; 1100 File.Page := File.Page + 1; 1101 end if; 1102 end loop; 1103 1104 File.Col := 1; 1105 end New_Line; 1106 1107 procedure New_Line (Spacing : Positive_Count := 1) is 1108 begin 1109 New_Line (Current_Out, Spacing); 1110 end New_Line; 1111 1112 -------------- 1113 -- New_Page -- 1114 -------------- 1115 1116 procedure New_Page (File : File_Type) is 1117 begin 1118 FIO.Check_Write_Status (AP (File)); 1119 1120 if File.Col /= 1 or else File.Line = 1 then 1121 Putc (LM, File); 1122 end if; 1123 1124 Putc (PM, File); 1125 File.Page := File.Page + 1; 1126 File.Line := 1; 1127 File.Col := 1; 1128 end New_Page; 1129 1130 procedure New_Page is 1131 begin 1132 New_Page (Current_Out); 1133 end New_Page; 1134 1135 ----------- 1136 -- Nextc -- 1137 ----------- 1138 1139 function Nextc (File : File_Type) return int is 1140 ch : int; 1141 1142 begin 1143 ch := fgetc (File.Stream); 1144 1145 if ch = EOF then 1146 if ferror (File.Stream) /= 0 then 1147 raise Device_Error; 1148 end if; 1149 1150 else 1151 if ungetc (ch, File.Stream) = EOF then 1152 raise Device_Error; 1153 end if; 1154 end if; 1155 1156 return ch; 1157 end Nextc; 1158 1159 ---------- 1160 -- Open -- 1161 ---------- 1162 1163 procedure Open 1164 (File : in out File_Type; 1165 Mode : File_Mode; 1166 Name : String; 1167 Form : String := "") 1168 is 1169 Dummy_File_Control_Block : Wide_Text_AFCB; 1170 pragma Warnings (Off, Dummy_File_Control_Block); 1171 -- Yes, we know this is never assigned a value, only the tag 1172 -- is used for dispatching purposes, so that's expected. 1173 1174 begin 1175 FIO.Open (File_Ptr => AP (File), 1176 Dummy_FCB => Dummy_File_Control_Block, 1177 Mode => To_FCB (Mode), 1178 Name => Name, 1179 Form => Form, 1180 Amethod => 'W', 1181 Creat => False, 1182 Text => True); 1183 1184 File.Self := File; 1185 Set_WCEM (File); 1186 end Open; 1187 1188 ---------- 1189 -- Page -- 1190 ---------- 1191 1192 -- Note: we assume that it is impossible in practice for the page 1193 -- to exceed the value of Count'Last, i.e. no check is required for 1194 -- overflow raising layout error. 1195 1196 function Page (File : File_Type) return Positive_Count is 1197 begin 1198 FIO.Check_File_Open (AP (File)); 1199 return File.Page; 1200 end Page; 1201 1202 function Page return Positive_Count is 1203 begin 1204 return Page (Current_Out); 1205 end Page; 1206 1207 ----------------- 1208 -- Page_Length -- 1209 ----------------- 1210 1211 function Page_Length (File : File_Type) return Count is 1212 begin 1213 FIO.Check_Write_Status (AP (File)); 1214 return File.Page_Length; 1215 end Page_Length; 1216 1217 function Page_Length return Count is 1218 begin 1219 return Page_Length (Current_Out); 1220 end Page_Length; 1221 1222 --------- 1223 -- Put -- 1224 --------- 1225 1226 procedure Put 1227 (File : File_Type; 1228 Item : Wide_Character) 1229 is 1230 wide_text_translation_required : Integer; 1231 pragma Import 1232 (C, wide_text_translation_required, 1233 "__gnat_wide_text_translation_required"); 1234 -- Text translation is required on Windows only. This means that the 1235 -- console is doing translation and we do not want to do any encoding 1236 -- here. If this variable is not 0 we output the character via fputwc. 1237 1238 procedure Out_Char (C : Character); 1239 -- Procedure to output one character of a wide character sequence 1240 1241 procedure WC_Out is new Wide_Char_To_Char_Sequence (Out_Char); 1242 1243 -------------- 1244 -- Out_Char -- 1245 -------------- 1246 1247 procedure Out_Char (C : Character) is 1248 begin 1249 Putc (Character'Pos (C), File); 1250 end Out_Char; 1251 1252 Discard : int; 1253 1254 -- Start of processing for Put 1255 1256 begin 1257 FIO.Check_Write_Status (AP (File)); 1258 1259 if wide_text_translation_required /= 0 1260 or else File.Text_Encoding in Non_Default_Text_Content_Encoding 1261 then 1262 set_mode (fileno (File.Stream), File.Text_Encoding); 1263 Discard := fputwc (Wide_Character'Pos (Item), File.Stream); 1264 else 1265 WC_Out (Item, File.WC_Method); 1266 end if; 1267 1268 File.Col := File.Col + 1; 1269 end Put; 1270 1271 procedure Put (Item : Wide_Character) is 1272 begin 1273 Put (Current_Out, Item); 1274 end Put; 1275 1276 --------- 1277 -- Put -- 1278 --------- 1279 1280 procedure Put 1281 (File : File_Type; 1282 Item : Wide_String) 1283 is 1284 begin 1285 for J in Item'Range loop 1286 Put (File, Item (J)); 1287 end loop; 1288 end Put; 1289 1290 procedure Put (Item : Wide_String) is 1291 begin 1292 Put (Current_Out, Item); 1293 end Put; 1294 1295 -------------- 1296 -- Put_Line -- 1297 -------------- 1298 1299 procedure Put_Line 1300 (File : File_Type; 1301 Item : Wide_String) 1302 is 1303 begin 1304 Put (File, Item); 1305 New_Line (File); 1306 end Put_Line; 1307 1308 procedure Put_Line (Item : Wide_String) is 1309 begin 1310 Put (Current_Out, Item); 1311 New_Line (Current_Out); 1312 end Put_Line; 1313 1314 ---------- 1315 -- Putc -- 1316 ---------- 1317 1318 procedure Putc (ch : int; File : File_Type) is 1319 begin 1320 if fputc (ch, File.Stream) = EOF then 1321 raise Device_Error; 1322 end if; 1323 end Putc; 1324 1325 ---------- 1326 -- Read -- 1327 ---------- 1328 1329 -- This is the primitive Stream Read routine, used when a Text_IO file 1330 -- is treated directly as a stream using Text_IO.Streams.Stream. 1331 1332 procedure Read 1333 (File : in out Wide_Text_AFCB; 1334 Item : out Stream_Element_Array; 1335 Last : out Stream_Element_Offset) 1336 is 1337 Discard_ch : int; 1338 pragma Unreferenced (Discard_ch); 1339 1340 begin 1341 -- Need to deal with Before_Wide_Character ??? 1342 1343 if File.Mode /= FCB.In_File then 1344 raise Mode_Error; 1345 end if; 1346 1347 -- Deal with case where our logical and physical position do not match 1348 -- because of being after an LM or LM-PM sequence when in fact we are 1349 -- logically positioned before it. 1350 1351 if File.Before_LM then 1352 1353 -- If we are before a PM, then it is possible for a stream read 1354 -- to leave us after the LM and before the PM, which is a bit 1355 -- odd. The easiest way to deal with this is to unget the PM, 1356 -- so we are indeed positioned between the characters. This way 1357 -- further stream read operations will work correctly, and the 1358 -- effect on text processing is a little weird, but what can 1359 -- be expected if stream and text input are mixed this way? 1360 1361 if File.Before_LM_PM then 1362 Discard_ch := ungetc (PM, File.Stream); 1363 File.Before_LM_PM := False; 1364 end if; 1365 1366 File.Before_LM := False; 1367 1368 Item (Item'First) := Stream_Element (Character'Pos (ASCII.LF)); 1369 1370 if Item'Length = 1 then 1371 Last := Item'Last; 1372 1373 else 1374 Last := 1375 Item'First + 1376 Stream_Element_Offset 1377 (fread (buffer => Item'Address, 1378 index => size_t (Item'First + 1), 1379 size => 1, 1380 count => Item'Length - 1, 1381 stream => File.Stream)); 1382 end if; 1383 1384 return; 1385 end if; 1386 1387 -- Now we do the read. Since this is a text file, it is normally in 1388 -- text mode, but stream data must be read in binary mode, so we 1389 -- temporarily set binary mode for the read, resetting it after. 1390 -- These calls have no effect in a system (like Unix) where there is 1391 -- no distinction between text and binary files. 1392 1393 set_binary_mode (fileno (File.Stream)); 1394 1395 Last := 1396 Item'First + 1397 Stream_Element_Offset 1398 (fread (Item'Address, 1, Item'Length, File.Stream)) - 1; 1399 1400 if Last < Item'Last then 1401 if ferror (File.Stream) /= 0 then 1402 raise Device_Error; 1403 end if; 1404 end if; 1405 1406 set_text_mode (fileno (File.Stream)); 1407 end Read; 1408 1409 ----------- 1410 -- Reset -- 1411 ----------- 1412 1413 procedure Reset 1414 (File : in out File_Type; 1415 Mode : File_Mode) 1416 is 1417 begin 1418 -- Don't allow change of mode for current file (RM A.10.2(5)) 1419 1420 if (File = Current_In or else 1421 File = Current_Out or else 1422 File = Current_Error) 1423 and then To_FCB (Mode) /= File.Mode 1424 then 1425 raise Mode_Error; 1426 end if; 1427 1428 Terminate_Line (File); 1429 FIO.Reset (AP (File)'Unrestricted_Access, To_FCB (Mode)); 1430 File.Page := 1; 1431 File.Line := 1; 1432 File.Col := 1; 1433 File.Line_Length := 0; 1434 File.Page_Length := 0; 1435 File.Before_LM := False; 1436 File.Before_LM_PM := False; 1437 end Reset; 1438 1439 procedure Reset (File : in out File_Type) is 1440 begin 1441 Terminate_Line (File); 1442 FIO.Reset (AP (File)'Unrestricted_Access); 1443 File.Page := 1; 1444 File.Line := 1; 1445 File.Col := 1; 1446 File.Line_Length := 0; 1447 File.Page_Length := 0; 1448 File.Before_LM := False; 1449 File.Before_LM_PM := False; 1450 end Reset; 1451 1452 ------------- 1453 -- Set_Col -- 1454 ------------- 1455 1456 procedure Set_Col 1457 (File : File_Type; 1458 To : Positive_Count) 1459 is 1460 ch : int; 1461 1462 begin 1463 -- Raise Constraint_Error if out of range value. The reason for this 1464 -- explicit test is that we don't want junk values around, even if 1465 -- checks are off in the caller. 1466 1467 if not To'Valid then 1468 raise Constraint_Error; 1469 end if; 1470 1471 FIO.Check_File_Open (AP (File)); 1472 1473 if To = File.Col then 1474 return; 1475 end if; 1476 1477 if Mode (File) >= Out_File then 1478 if File.Line_Length /= 0 and then To > File.Line_Length then 1479 raise Layout_Error; 1480 end if; 1481 1482 if To < File.Col then 1483 New_Line (File); 1484 end if; 1485 1486 while File.Col < To loop 1487 Put (File, ' '); 1488 end loop; 1489 1490 else 1491 loop 1492 ch := Getc (File); 1493 1494 if ch = EOF then 1495 raise End_Error; 1496 1497 elsif ch = LM then 1498 File.Line := File.Line + 1; 1499 File.Col := 1; 1500 1501 elsif ch = PM and then File.Is_Regular_File then 1502 File.Page := File.Page + 1; 1503 File.Line := 1; 1504 File.Col := 1; 1505 1506 elsif To = File.Col then 1507 Ungetc (ch, File); 1508 return; 1509 1510 else 1511 File.Col := File.Col + 1; 1512 end if; 1513 end loop; 1514 end if; 1515 end Set_Col; 1516 1517 procedure Set_Col (To : Positive_Count) is 1518 begin 1519 Set_Col (Current_Out, To); 1520 end Set_Col; 1521 1522 --------------- 1523 -- Set_Error -- 1524 --------------- 1525 1526 procedure Set_Error (File : File_Type) is 1527 begin 1528 FIO.Check_Write_Status (AP (File)); 1529 Current_Err := File; 1530 end Set_Error; 1531 1532 --------------- 1533 -- Set_Input -- 1534 --------------- 1535 1536 procedure Set_Input (File : File_Type) is 1537 begin 1538 FIO.Check_Read_Status (AP (File)); 1539 Current_In := File; 1540 end Set_Input; 1541 1542 -------------- 1543 -- Set_Line -- 1544 -------------- 1545 1546 procedure Set_Line 1547 (File : File_Type; 1548 To : Positive_Count) 1549 is 1550 begin 1551 -- Raise Constraint_Error if out of range value. The reason for this 1552 -- explicit test is that we don't want junk values around, even if 1553 -- checks are off in the caller. 1554 1555 if not To'Valid then 1556 raise Constraint_Error; 1557 end if; 1558 1559 FIO.Check_File_Open (AP (File)); 1560 1561 if To = File.Line then 1562 return; 1563 end if; 1564 1565 if Mode (File) >= Out_File then 1566 if File.Page_Length /= 0 and then To > File.Page_Length then 1567 raise Layout_Error; 1568 end if; 1569 1570 if To < File.Line then 1571 New_Page (File); 1572 end if; 1573 1574 while File.Line < To loop 1575 New_Line (File); 1576 end loop; 1577 1578 else 1579 while To /= File.Line loop 1580 Skip_Line (File); 1581 end loop; 1582 end if; 1583 end Set_Line; 1584 1585 procedure Set_Line (To : Positive_Count) is 1586 begin 1587 Set_Line (Current_Out, To); 1588 end Set_Line; 1589 1590 --------------------- 1591 -- Set_Line_Length -- 1592 --------------------- 1593 1594 procedure Set_Line_Length (File : File_Type; To : Count) is 1595 begin 1596 -- Raise Constraint_Error if out of range value. The reason for this 1597 -- explicit test is that we don't want junk values around, even if 1598 -- checks are off in the caller. 1599 1600 if not To'Valid then 1601 raise Constraint_Error; 1602 end if; 1603 1604 FIO.Check_Write_Status (AP (File)); 1605 File.Line_Length := To; 1606 end Set_Line_Length; 1607 1608 procedure Set_Line_Length (To : Count) is 1609 begin 1610 Set_Line_Length (Current_Out, To); 1611 end Set_Line_Length; 1612 1613 ---------------- 1614 -- Set_Output -- 1615 ---------------- 1616 1617 procedure Set_Output (File : File_Type) is 1618 begin 1619 FIO.Check_Write_Status (AP (File)); 1620 Current_Out := File; 1621 end Set_Output; 1622 1623 --------------------- 1624 -- Set_Page_Length -- 1625 --------------------- 1626 1627 procedure Set_Page_Length (File : File_Type; To : Count) is 1628 begin 1629 -- Raise Constraint_Error if out of range value. The reason for this 1630 -- explicit test is that we don't want junk values around, even if 1631 -- checks are off in the caller. 1632 1633 if not To'Valid then 1634 raise Constraint_Error; 1635 end if; 1636 1637 FIO.Check_Write_Status (AP (File)); 1638 File.Page_Length := To; 1639 end Set_Page_Length; 1640 1641 procedure Set_Page_Length (To : Count) is 1642 begin 1643 Set_Page_Length (Current_Out, To); 1644 end Set_Page_Length; 1645 1646 -------------- 1647 -- Set_WCEM -- 1648 -------------- 1649 1650 procedure Set_WCEM (File : in out File_Type) is 1651 Start : Natural; 1652 Stop : Natural; 1653 1654 begin 1655 FIO.Form_Parameter (File.Form.all, "wcem", Start, Stop); 1656 1657 if Start = 0 then 1658 File.WC_Method := Default_WCEM; 1659 1660 else 1661 if Stop = Start then 1662 for J in WC_Encoding_Letters'Range loop 1663 if File.Form (Start) = WC_Encoding_Letters (J) then 1664 File.WC_Method := J; 1665 return; 1666 end if; 1667 end loop; 1668 end if; 1669 1670 Close (File); 1671 raise Use_Error with "invalid WCEM form parameter"; 1672 end if; 1673 end Set_WCEM; 1674 1675 --------------- 1676 -- Skip_Line -- 1677 --------------- 1678 1679 procedure Skip_Line 1680 (File : File_Type; 1681 Spacing : Positive_Count := 1) 1682 is 1683 ch : int; 1684 1685 begin 1686 -- Raise Constraint_Error if out of range value. The reason for this 1687 -- explicit test is that we don't want junk values around, even if 1688 -- checks are off in the caller. 1689 1690 if not Spacing'Valid then 1691 raise Constraint_Error; 1692 end if; 1693 1694 FIO.Check_Read_Status (AP (File)); 1695 1696 for L in 1 .. Spacing loop 1697 if File.Before_LM then 1698 File.Before_LM := False; 1699 File.Before_LM_PM := False; 1700 1701 else 1702 ch := Getc (File); 1703 1704 -- If at end of file now, then immediately raise End_Error. Note 1705 -- that we can never be positioned between a line mark and a page 1706 -- mark, so if we are at the end of file, we cannot logically be 1707 -- before the implicit page mark that is at the end of the file. 1708 1709 -- For the same reason, we do not need an explicit check for a 1710 -- page mark. If there is a FF in the middle of a line, the file 1711 -- is not in canonical format and we do not care about the page 1712 -- numbers for files other than ones in canonical format. 1713 1714 if ch = EOF then 1715 raise End_Error; 1716 end if; 1717 1718 -- If not at end of file, then loop till we get to an LM or EOF. 1719 -- The latter case happens only in non-canonical files where the 1720 -- last line is not terminated by LM, but we don't want to blow 1721 -- up for such files, so we assume an implicit LM in this case. 1722 1723 loop 1724 exit when ch = LM or else ch = EOF; 1725 ch := Getc (File); 1726 end loop; 1727 end if; 1728 1729 -- We have got past a line mark, now, for a regular file only, 1730 -- see if a page mark immediately follows this line mark and 1731 -- if so, skip past the page mark as well. We do not do this 1732 -- for non-regular files, since it would cause an undesirable 1733 -- wait for an additional character. 1734 1735 File.Col := 1; 1736 File.Line := File.Line + 1; 1737 1738 if File.Before_LM_PM then 1739 File.Page := File.Page + 1; 1740 File.Line := 1; 1741 File.Before_LM_PM := False; 1742 1743 elsif File.Is_Regular_File then 1744 ch := Getc (File); 1745 1746 -- Page mark can be explicit, or implied at the end of the file 1747 1748 if (ch = PM or else ch = EOF) 1749 and then File.Is_Regular_File 1750 then 1751 File.Page := File.Page + 1; 1752 File.Line := 1; 1753 else 1754 Ungetc (ch, File); 1755 end if; 1756 end if; 1757 end loop; 1758 1759 File.Before_Wide_Character := False; 1760 end Skip_Line; 1761 1762 procedure Skip_Line (Spacing : Positive_Count := 1) is 1763 begin 1764 Skip_Line (Current_In, Spacing); 1765 end Skip_Line; 1766 1767 --------------- 1768 -- Skip_Page -- 1769 --------------- 1770 1771 procedure Skip_Page (File : File_Type) is 1772 ch : int; 1773 1774 begin 1775 FIO.Check_Read_Status (AP (File)); 1776 1777 -- If at page mark already, just skip it 1778 1779 if File.Before_LM_PM then 1780 File.Before_LM := False; 1781 File.Before_LM_PM := False; 1782 File.Page := File.Page + 1; 1783 File.Line := 1; 1784 File.Col := 1; 1785 return; 1786 end if; 1787 1788 -- This is a bit tricky, if we are logically before an LM then 1789 -- it is not an error if we are at an end of file now, since we 1790 -- are not really at it. 1791 1792 if File.Before_LM then 1793 File.Before_LM := False; 1794 File.Before_LM_PM := False; 1795 ch := Getc (File); 1796 1797 -- Otherwise we do raise End_Error if we are at the end of file now 1798 1799 else 1800 ch := Getc (File); 1801 1802 if ch = EOF then 1803 raise End_Error; 1804 end if; 1805 end if; 1806 1807 -- Now we can just rumble along to the next page mark, or to the 1808 -- end of file, if that comes first. The latter case happens when 1809 -- the page mark is implied at the end of file. 1810 1811 loop 1812 exit when ch = EOF 1813 or else (ch = PM and then File.Is_Regular_File); 1814 ch := Getc (File); 1815 end loop; 1816 1817 File.Page := File.Page + 1; 1818 File.Line := 1; 1819 File.Col := 1; 1820 File.Before_Wide_Character := False; 1821 end Skip_Page; 1822 1823 procedure Skip_Page is 1824 begin 1825 Skip_Page (Current_In); 1826 end Skip_Page; 1827 1828 -------------------- 1829 -- Standard_Error -- 1830 -------------------- 1831 1832 function Standard_Error return File_Type is 1833 begin 1834 return Standard_Err; 1835 end Standard_Error; 1836 1837 function Standard_Error return File_Access is 1838 begin 1839 return Standard_Err'Access; 1840 end Standard_Error; 1841 1842 -------------------- 1843 -- Standard_Input -- 1844 -------------------- 1845 1846 function Standard_Input return File_Type is 1847 begin 1848 return Standard_In; 1849 end Standard_Input; 1850 1851 function Standard_Input return File_Access is 1852 begin 1853 return Standard_In'Access; 1854 end Standard_Input; 1855 1856 --------------------- 1857 -- Standard_Output -- 1858 --------------------- 1859 1860 function Standard_Output return File_Type is 1861 begin 1862 return Standard_Out; 1863 end Standard_Output; 1864 1865 function Standard_Output return File_Access is 1866 begin 1867 return Standard_Out'Access; 1868 end Standard_Output; 1869 1870 -------------------- 1871 -- Terminate_Line -- 1872 -------------------- 1873 1874 procedure Terminate_Line (File : File_Type) is 1875 begin 1876 FIO.Check_File_Open (AP (File)); 1877 1878 -- For file other than In_File, test for needing to terminate last line 1879 1880 if Mode (File) /= In_File then 1881 1882 -- If not at start of line definition need new line 1883 1884 if File.Col /= 1 then 1885 New_Line (File); 1886 1887 -- For files other than standard error and standard output, we 1888 -- make sure that an empty file has a single line feed, so that 1889 -- it is properly formatted. We avoid this for the standard files 1890 -- because it is too much of a nuisance to have these odd line 1891 -- feeds when nothing has been written to the file. 1892 1893 elsif (File /= Standard_Err and then File /= Standard_Out) 1894 and then (File.Line = 1 and then File.Page = 1) 1895 then 1896 New_Line (File); 1897 end if; 1898 end if; 1899 end Terminate_Line; 1900 1901 ------------ 1902 -- Ungetc -- 1903 ------------ 1904 1905 procedure Ungetc (ch : int; File : File_Type) is 1906 begin 1907 if ch /= EOF then 1908 if ungetc (ch, File.Stream) = EOF then 1909 raise Device_Error; 1910 end if; 1911 end if; 1912 end Ungetc; 1913 1914 ----------- 1915 -- Write -- 1916 ----------- 1917 1918 -- This is the primitive Stream Write routine, used when a Text_IO file 1919 -- is treated directly as a stream using Text_IO.Streams.Stream. 1920 1921 procedure Write 1922 (File : in out Wide_Text_AFCB; 1923 Item : Stream_Element_Array) 1924 is 1925 pragma Warnings (Off, File); 1926 -- Because in this implementation we don't need IN OUT, we only read 1927 1928 Siz : constant size_t := Item'Length; 1929 1930 begin 1931 if File.Mode = FCB.In_File then 1932 raise Mode_Error; 1933 end if; 1934 1935 -- Now we do the write. Since this is a text file, it is normally in 1936 -- text mode, but stream data must be written in binary mode, so we 1937 -- temporarily set binary mode for the write, resetting it after. 1938 -- These calls have no effect in a system (like Unix) where there is 1939 -- no distinction between text and binary files. 1940 1941 set_binary_mode (fileno (File.Stream)); 1942 1943 if fwrite (Item'Address, 1, Siz, File.Stream) /= Siz then 1944 raise Device_Error; 1945 end if; 1946 1947 set_text_mode (fileno (File.Stream)); 1948 end Write; 1949 1950begin 1951 -- Initialize Standard Files 1952 1953 for J in WC_Encoding_Method loop 1954 if WC_Encoding = WC_Encoding_Letters (J) then 1955 Default_WCEM := J; 1956 end if; 1957 end loop; 1958 1959 Initialize_Standard_Files; 1960 1961 FIO.Chain_File (AP (Standard_In)); 1962 FIO.Chain_File (AP (Standard_Out)); 1963 FIO.Chain_File (AP (Standard_Err)); 1964 1965end Ada.Wide_Text_IO; 1966