OSDN Git Service

More improvements to sparc VIS vec_init code generation.
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-awk.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                              G N A T . A W K                             --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 2000-2011, AdaCore                     --
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.Exceptions;
33 with Ada.Text_IO;
34 with Ada.Strings.Unbounded;
35 with Ada.Strings.Fixed;
36 with Ada.Strings.Maps;
37 with Ada.Unchecked_Deallocation;
38
39 with GNAT.Directory_Operations;
40 with GNAT.Dynamic_Tables;
41 with GNAT.OS_Lib;
42
43 package body GNAT.AWK is
44
45    use Ada;
46    use Ada.Strings.Unbounded;
47
48    -----------------------
49    -- Local subprograms --
50    -----------------------
51
52    --  The following two subprograms provide a functional interface to the
53    --  two special session variables, that are manipulated explicitly by
54    --  Finalize, but must be declared after Finalize to prevent static
55    --  elaboration warnings.
56
57    function Get_Def return Session_Data_Access;
58    procedure Set_Cur;
59
60    ----------------
61    -- Split mode --
62    ----------------
63
64    package Split is
65
66       type Mode is abstract tagged null record;
67       --  This is the main type which is declared abstract. This type must be
68       --  derived for each split style.
69
70       type Mode_Access is access Mode'Class;
71
72       procedure Current_Line (S : Mode; Session : Session_Type)
73         is abstract;
74       --  Split current line of Session using split mode S
75
76       ------------------------
77       -- Split on separator --
78       ------------------------
79
80       type Separator (Size : Positive) is new Mode with record
81          Separators : String (1 .. Size);
82       end record;
83
84       procedure Current_Line
85         (S       : Separator;
86          Session : Session_Type);
87
88       ---------------------
89       -- Split on column --
90       ---------------------
91
92       type Column (Size : Positive) is new Mode with record
93          Columns : Widths_Set (1 .. Size);
94       end record;
95
96       procedure Current_Line (S : Column; Session : Session_Type);
97
98    end Split;
99
100    procedure Free is new Unchecked_Deallocation
101      (Split.Mode'Class, Split.Mode_Access);
102
103    ----------------
104    -- File_Table --
105    ----------------
106
107    type AWK_File is access String;
108
109    package File_Table is
110       new Dynamic_Tables (AWK_File, Natural, 1, 5, 50);
111    --  List of file names associated with a Session
112
113    procedure Free is new Unchecked_Deallocation (String, AWK_File);
114
115    -----------------
116    -- Field_Table --
117    -----------------
118
119    type Field_Slice is record
120       First : Positive;
121       Last  : Natural;
122    end record;
123    --  This is a field slice (First .. Last) in session's current line
124
125    package Field_Table is
126       new Dynamic_Tables (Field_Slice, Natural, 1, 10, 100);
127    --  List of fields for the current line
128
129    --------------
130    -- Patterns --
131    --------------
132
133    --  Define all patterns style: exact string, regular expression, boolean
134    --  function.
135
136    package Patterns is
137
138       type Pattern is abstract tagged null record;
139       --  This is the main type which is declared abstract. This type must be
140       --  derived for each patterns style.
141
142       type Pattern_Access is access Pattern'Class;
143
144       function Match
145         (P       : Pattern;
146          Session : Session_Type) return Boolean
147       is abstract;
148       --  Returns True if P match for the current session and False otherwise
149
150       procedure Release (P : in out Pattern);
151       --  Release memory used by the pattern structure
152
153       --------------------------
154       -- Exact string pattern --
155       --------------------------
156
157       type String_Pattern is new Pattern with record
158          Str  : Unbounded_String;
159          Rank : Count;
160       end record;
161
162       function Match
163         (P       : String_Pattern;
164          Session : Session_Type) return Boolean;
165
166       --------------------------------
167       -- Regular expression pattern --
168       --------------------------------
169
170       type Pattern_Matcher_Access is access Regpat.Pattern_Matcher;
171
172       type Regexp_Pattern is new Pattern with record
173          Regx : Pattern_Matcher_Access;
174          Rank : Count;
175       end record;
176
177       function Match
178         (P       : Regexp_Pattern;
179          Session : Session_Type) return Boolean;
180
181       procedure Release (P : in out Regexp_Pattern);
182
183       ------------------------------
184       -- Boolean function pattern --
185       ------------------------------
186
187       type Callback_Pattern is new Pattern with record
188          Pattern : Pattern_Callback;
189       end record;
190
191       function Match
192         (P       : Callback_Pattern;
193          Session : Session_Type) return Boolean;
194
195    end Patterns;
196
197    procedure Free is new Unchecked_Deallocation
198      (Patterns.Pattern'Class, Patterns.Pattern_Access);
199
200    -------------
201    -- Actions --
202    -------------
203
204    --  Define all action style : simple call, call with matches
205
206    package Actions is
207
208       type Action is abstract tagged null record;
209       --  This is the main type which is declared abstract. This type must be
210       --  derived for each action style.
211
212       type Action_Access is access Action'Class;
213
214       procedure Call
215         (A       : Action;
216          Session : Session_Type) is abstract;
217       --  Call action A as required
218
219       -------------------
220       -- Simple action --
221       -------------------
222
223       type Simple_Action is new Action with record
224          Proc : Action_Callback;
225       end record;
226
227       procedure Call
228         (A       : Simple_Action;
229          Session : Session_Type);
230
231       -------------------------
232       -- Action with matches --
233       -------------------------
234
235       type Match_Action is new Action with record
236          Proc : Match_Action_Callback;
237       end record;
238
239       procedure Call
240         (A       : Match_Action;
241          Session : Session_Type);
242
243    end Actions;
244
245    procedure Free is new Unchecked_Deallocation
246      (Actions.Action'Class, Actions.Action_Access);
247
248    --------------------------
249    -- Pattern/Action table --
250    --------------------------
251
252    type Pattern_Action is record
253       Pattern : Patterns.Pattern_Access;  -- If Pattern is True
254       Action  : Actions.Action_Access;    -- Action will be called
255    end record;
256
257    package Pattern_Action_Table is
258       new Dynamic_Tables (Pattern_Action, Natural, 1, 5, 50);
259
260    ------------------
261    -- Session Data --
262    ------------------
263
264    type Session_Data is record
265       Current_File : Text_IO.File_Type;
266       Current_Line : Unbounded_String;
267       Separators   : Split.Mode_Access;
268       Files        : File_Table.Instance;
269       File_Index   : Natural := 0;
270       Fields       : Field_Table.Instance;
271       Filters      : Pattern_Action_Table.Instance;
272       NR           : Natural := 0;
273       FNR          : Natural := 0;
274       Matches      : Regpat.Match_Array (0 .. 100);
275       --  Latest matches for the regexp pattern
276    end record;
277
278    procedure Free is
279       new Unchecked_Deallocation (Session_Data, Session_Data_Access);
280
281    --------------
282    -- Finalize --
283    --------------
284
285    procedure Finalize (Session : in out Session_Type) is
286    begin
287       --  We release the session data only if it is not the default session
288
289       if Session.Data /= Get_Def then
290          --  Release separators
291
292          Free (Session.Data.Separators);
293
294          Free (Session.Data);
295
296          --  Since we have closed the current session, set it to point now to
297          --  the default session.
298
299          Set_Cur;
300       end if;
301    end Finalize;
302
303    ----------------
304    -- Initialize --
305    ----------------
306
307    procedure Initialize (Session : in out Session_Type) is
308    begin
309       Session.Data := new Session_Data;
310
311       --  Initialize separators
312
313       Session.Data.Separators :=
314         new Split.Separator'(Default_Separators'Length, Default_Separators);
315
316       --  Initialize all tables
317
318       File_Table.Init  (Session.Data.Files);
319       Field_Table.Init (Session.Data.Fields);
320       Pattern_Action_Table.Init (Session.Data.Filters);
321    end Initialize;
322
323    -----------------------
324    -- Session Variables --
325    -----------------------
326
327    Def_Session : Session_Type;
328    Cur_Session : Session_Type;
329
330    ----------------------
331    -- Private Services --
332    ----------------------
333
334    function Always_True return Boolean;
335    --  A function that always returns True
336
337    function Apply_Filters
338      (Session : Session_Type) return Boolean;
339    --  Apply any filters for which the Pattern is True for Session. It returns
340    --  True if a least one filters has been applied (i.e. associated action
341    --  callback has been called).
342
343    procedure Open_Next_File
344      (Session : Session_Type);
345    pragma Inline (Open_Next_File);
346    --  Open next file for Session closing current file if needed. It raises
347    --  End_Error if there is no more file in the table.
348
349    procedure Raise_With_Info
350      (E       : Exceptions.Exception_Id;
351       Message : String;
352       Session : Session_Type);
353    pragma No_Return (Raise_With_Info);
354    --  Raises exception E with the message prepended with the current line
355    --  number and the filename if possible.
356
357    procedure Read_Line (Session : Session_Type);
358    --  Read a line for the Session and set Current_Line
359
360    procedure Split_Line (Session : Session_Type);
361    --  Split session's Current_Line according to the session separators and
362    --  set the Fields table. This procedure can be called at any time.
363
364    ----------------------
365    -- Private Packages --
366    ----------------------
367
368    -------------
369    -- Actions --
370    -------------
371
372    package body Actions is
373
374       ----------
375       -- Call --
376       ----------
377
378       procedure Call
379         (A       : Simple_Action;
380          Session : Session_Type)
381       is
382          pragma Unreferenced (Session);
383       begin
384          A.Proc.all;
385       end Call;
386
387       ----------
388       -- Call --
389       ----------
390
391       procedure Call
392         (A       : Match_Action;
393          Session : Session_Type)
394       is
395       begin
396          A.Proc (Session.Data.Matches);
397       end Call;
398
399    end Actions;
400
401    --------------
402    -- Patterns --
403    --------------
404
405    package body Patterns is
406
407       -----------
408       -- Match --
409       -----------
410
411       function Match
412         (P       : String_Pattern;
413          Session : Session_Type) return Boolean
414       is
415       begin
416          return P.Str = Field (P.Rank, Session);
417       end Match;
418
419       -----------
420       -- Match --
421       -----------
422
423       function Match
424         (P       : Regexp_Pattern;
425          Session : Session_Type) return Boolean
426       is
427          use type Regpat.Match_Location;
428       begin
429          Regpat.Match
430            (P.Regx.all, Field (P.Rank, Session), Session.Data.Matches);
431          return Session.Data.Matches (0) /= Regpat.No_Match;
432       end Match;
433
434       -----------
435       -- Match --
436       -----------
437
438       function Match
439         (P       : Callback_Pattern;
440          Session : Session_Type) return Boolean
441       is
442          pragma Unreferenced (Session);
443       begin
444          return P.Pattern.all;
445       end Match;
446
447       -------------
448       -- Release --
449       -------------
450
451       procedure Release (P : in out Pattern) is
452          pragma Unreferenced (P);
453       begin
454          null;
455       end Release;
456
457       -------------
458       -- Release --
459       -------------
460
461       procedure Release (P : in out Regexp_Pattern) is
462          procedure Free is new Unchecked_Deallocation
463            (Regpat.Pattern_Matcher, Pattern_Matcher_Access);
464       begin
465          Free (P.Regx);
466       end Release;
467
468    end Patterns;
469
470    -----------
471    -- Split --
472    -----------
473
474    package body Split is
475
476       use Ada.Strings;
477
478       ------------------
479       -- Current_Line --
480       ------------------
481
482       procedure Current_Line (S : Separator; Session : Session_Type) is
483          Line   : constant String := To_String (Session.Data.Current_Line);
484          Fields : Field_Table.Instance renames Session.Data.Fields;
485          Seps   : constant Maps.Character_Set := Maps.To_Set (S.Separators);
486
487          Start  : Natural;
488          Stop   : Natural;
489
490       begin
491          --  First field start here
492
493          Start := Line'First;
494
495          --  Record the first field start position which is the first character
496          --  in the line.
497
498          Field_Table.Increment_Last (Fields);
499          Fields.Table (Field_Table.Last (Fields)).First := Start;
500
501          loop
502             --  Look for next separator
503
504             Stop := Fixed.Index
505               (Source => Line (Start .. Line'Last),
506                Set    => Seps);
507
508             exit when Stop = 0;
509
510             Fields.Table (Field_Table.Last (Fields)).Last := Stop - 1;
511
512             --  If separators are set to the default (space and tab) we skip
513             --  all spaces and tabs following current field.
514
515             if S.Separators = Default_Separators then
516                Start := Fixed.Index
517                  (Line (Stop + 1 .. Line'Last),
518                   Maps.To_Set (Default_Separators),
519                   Outside,
520                   Strings.Forward);
521
522                if Start = 0 then
523                   Start := Stop + 1;
524                end if;
525
526             else
527                Start := Stop + 1;
528             end if;
529
530             --  Record in the field table the start of this new field
531
532             Field_Table.Increment_Last (Fields);
533             Fields.Table (Field_Table.Last (Fields)).First := Start;
534
535          end loop;
536
537          Fields.Table (Field_Table.Last (Fields)).Last := Line'Last;
538       end Current_Line;
539
540       ------------------
541       -- Current_Line --
542       ------------------
543
544       procedure Current_Line (S : Column; Session : Session_Type) is
545          Line   : constant String := To_String (Session.Data.Current_Line);
546          Fields : Field_Table.Instance renames Session.Data.Fields;
547          Start  : Positive := Line'First;
548
549       begin
550          --  Record the first field start position which is the first character
551          --  in the line.
552
553          for C in 1 .. S.Columns'Length loop
554
555             Field_Table.Increment_Last (Fields);
556
557             Fields.Table (Field_Table.Last (Fields)).First := Start;
558
559             Start := Start + S.Columns (C);
560
561             Fields.Table (Field_Table.Last (Fields)).Last := Start - 1;
562
563          end loop;
564
565          --  If there is some remaining character on the line, add them in a
566          --  new field.
567
568          if Start - 1 < Line'Length then
569
570             Field_Table.Increment_Last (Fields);
571
572             Fields.Table (Field_Table.Last (Fields)).First := Start;
573
574             Fields.Table (Field_Table.Last (Fields)).Last := Line'Last;
575          end if;
576       end Current_Line;
577
578    end Split;
579
580    --------------
581    -- Add_File --
582    --------------
583
584    procedure Add_File
585      (Filename : String;
586       Session  : Session_Type)
587    is
588       Files : File_Table.Instance renames Session.Data.Files;
589
590    begin
591       if OS_Lib.Is_Regular_File (Filename) then
592          File_Table.Increment_Last (Files);
593          Files.Table (File_Table.Last (Files)) := new String'(Filename);
594       else
595          Raise_With_Info
596            (File_Error'Identity,
597             "File " & Filename & " not found.",
598             Session);
599       end if;
600    end Add_File;
601
602    procedure Add_File
603      (Filename : String)
604    is
605
606    begin
607       Add_File (Filename, Cur_Session);
608    end Add_File;
609
610    ---------------
611    -- Add_Files --
612    ---------------
613
614    procedure Add_Files
615      (Directory             : String;
616       Filenames             : String;
617       Number_Of_Files_Added : out Natural;
618       Session               : Session_Type)
619    is
620       use Directory_Operations;
621
622       Dir      : Dir_Type;
623       Filename : String (1 .. 200);
624       Last     : Natural;
625
626    begin
627       Number_Of_Files_Added := 0;
628
629       Open (Dir, Directory);
630
631       loop
632          Read (Dir, Filename, Last);
633          exit when Last = 0;
634
635          Add_File (Filename (1 .. Last), Session);
636          Number_Of_Files_Added := Number_Of_Files_Added + 1;
637       end loop;
638
639       Close (Dir);
640
641    exception
642       when others =>
643          Raise_With_Info
644            (File_Error'Identity,
645             "Error scanning directory " & Directory
646             & " for files " & Filenames & '.',
647             Session);
648    end Add_Files;
649
650    procedure Add_Files
651      (Directory             : String;
652       Filenames             : String;
653       Number_Of_Files_Added : out Natural)
654    is
655
656    begin
657       Add_Files (Directory, Filenames, Number_Of_Files_Added, Cur_Session);
658    end Add_Files;
659
660    -----------------
661    -- Always_True --
662    -----------------
663
664    function Always_True return Boolean is
665    begin
666       return True;
667    end Always_True;
668
669    -------------------
670    -- Apply_Filters --
671    -------------------
672
673    function Apply_Filters
674      (Session : Session_Type) return Boolean
675    is
676       Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
677       Results : Boolean := False;
678
679    begin
680       --  Iterate through the filters table, if pattern match call action
681
682       for F in 1 .. Pattern_Action_Table.Last (Filters) loop
683          if Patterns.Match (Filters.Table (F).Pattern.all, Session) then
684             Results := True;
685             Actions.Call (Filters.Table (F).Action.all, Session);
686          end if;
687       end loop;
688
689       return Results;
690    end Apply_Filters;
691
692    -----------
693    -- Close --
694    -----------
695
696    procedure Close (Session : Session_Type) is
697       Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
698       Files   : File_Table.Instance renames Session.Data.Files;
699
700    begin
701       --  Close current file if needed
702
703       if Text_IO.Is_Open (Session.Data.Current_File) then
704          Text_IO.Close (Session.Data.Current_File);
705       end if;
706
707       --  Release Filters table
708
709       for F in 1 .. Pattern_Action_Table.Last (Filters) loop
710          Patterns.Release (Filters.Table (F).Pattern.all);
711          Free (Filters.Table (F).Pattern);
712          Free (Filters.Table (F).Action);
713       end loop;
714
715       for F in 1 .. File_Table.Last (Files) loop
716          Free (Files.Table (F));
717       end loop;
718
719       File_Table.Set_Last (Session.Data.Files, 0);
720       Field_Table.Set_Last (Session.Data.Fields, 0);
721       Pattern_Action_Table.Set_Last (Session.Data.Filters, 0);
722
723       Session.Data.NR := 0;
724       Session.Data.FNR := 0;
725       Session.Data.File_Index := 0;
726       Session.Data.Current_Line := Null_Unbounded_String;
727    end Close;
728
729    ---------------------
730    -- Current_Session --
731    ---------------------
732
733    function Current_Session return not null access Session_Type is
734    begin
735       return Cur_Session.Self;
736    end Current_Session;
737
738    ---------------------
739    -- Default_Session --
740    ---------------------
741
742    function Default_Session return not null access Session_Type is
743    begin
744       return Def_Session.Self;
745    end Default_Session;
746
747    --------------------
748    -- Discrete_Field --
749    --------------------
750
751    function Discrete_Field
752      (Rank    : Count;
753       Session : Session_Type) return Discrete
754    is
755    begin
756       return Discrete'Value (Field (Rank, Session));
757    end Discrete_Field;
758
759    function Discrete_Field_Current_Session
760      (Rank    : Count) return Discrete is
761       function Do_It is new Discrete_Field (Discrete);
762    begin
763       return Do_It (Rank, Cur_Session);
764    end Discrete_Field_Current_Session;
765
766    -----------------
767    -- End_Of_Data --
768    -----------------
769
770    function End_Of_Data
771      (Session : Session_Type) return Boolean
772    is
773    begin
774       return Session.Data.File_Index = File_Table.Last (Session.Data.Files)
775         and then End_Of_File (Session);
776    end End_Of_Data;
777
778    function End_Of_Data
779      return Boolean
780    is
781    begin
782       return End_Of_Data (Cur_Session);
783    end End_Of_Data;
784
785    -----------------
786    -- End_Of_File --
787    -----------------
788
789    function End_Of_File
790      (Session : Session_Type) return Boolean
791    is
792    begin
793       return Text_IO.End_Of_File (Session.Data.Current_File);
794    end End_Of_File;
795
796    function End_Of_File
797      return Boolean
798    is
799    begin
800       return End_Of_File (Cur_Session);
801    end End_Of_File;
802
803    -----------
804    -- Field --
805    -----------
806
807    function Field
808      (Rank    : Count;
809       Session : Session_Type) return String
810    is
811       Fields : Field_Table.Instance renames Session.Data.Fields;
812
813    begin
814       if Rank > Number_Of_Fields (Session) then
815          Raise_With_Info
816            (Field_Error'Identity,
817             "Field number" & Count'Image (Rank) & " does not exist.",
818             Session);
819
820       elsif Rank = 0 then
821
822          --  Returns the whole line, this is what $0 does under Session_Type
823
824          return To_String (Session.Data.Current_Line);
825
826       else
827          return Slice (Session.Data.Current_Line,
828                        Fields.Table (Positive (Rank)).First,
829                        Fields.Table (Positive (Rank)).Last);
830       end if;
831    end Field;
832
833    function Field
834      (Rank    : Count) return String
835    is
836    begin
837       return Field (Rank, Cur_Session);
838    end Field;
839
840    function Field
841      (Rank    : Count;
842       Session : Session_Type) return Integer
843    is
844    begin
845       return Integer'Value (Field (Rank, Session));
846
847    exception
848       when Constraint_Error =>
849          Raise_With_Info
850            (Field_Error'Identity,
851             "Field number" & Count'Image (Rank)
852             & " cannot be converted to an integer.",
853             Session);
854    end Field;
855
856    function Field
857      (Rank    : Count) return Integer
858    is
859    begin
860       return Field (Rank, Cur_Session);
861    end Field;
862
863    function Field
864      (Rank    : Count;
865       Session : Session_Type) return Float
866    is
867    begin
868       return Float'Value (Field (Rank, Session));
869
870    exception
871       when Constraint_Error =>
872          Raise_With_Info
873            (Field_Error'Identity,
874             "Field number" & Count'Image (Rank)
875             & " cannot be converted to a float.",
876             Session);
877    end Field;
878
879    function Field
880      (Rank    : Count) return Float
881    is
882    begin
883       return Field (Rank, Cur_Session);
884    end Field;
885
886    ----------
887    -- File --
888    ----------
889
890    function File
891      (Session : Session_Type) return String
892    is
893       Files : File_Table.Instance renames Session.Data.Files;
894
895    begin
896       if Session.Data.File_Index = 0 then
897          return "??";
898       else
899          return Files.Table (Session.Data.File_Index).all;
900       end if;
901    end File;
902
903    function File
904      return String
905    is
906    begin
907       return File (Cur_Session);
908    end File;
909
910    --------------------
911    -- For_Every_Line --
912    --------------------
913
914    procedure For_Every_Line
915      (Separators : String        := Use_Current;
916       Filename   : String        := Use_Current;
917       Callbacks  : Callback_Mode := None;
918       Session    : Session_Type)
919    is
920       Quit : Boolean;
921
922    begin
923       Open (Separators, Filename, Session);
924
925       while not End_Of_Data (Session) loop
926          Read_Line (Session);
927          Split_Line (Session);
928
929          if Callbacks in Only .. Pass_Through then
930             declare
931                Discard : Boolean;
932                pragma Unreferenced (Discard);
933             begin
934                Discard := Apply_Filters (Session);
935             end;
936          end if;
937
938          if Callbacks /= Only then
939             Quit := False;
940             Action (Quit);
941             exit when Quit;
942          end if;
943       end loop;
944
945       Close (Session);
946    end For_Every_Line;
947
948    procedure For_Every_Line_Current_Session
949      (Separators : String        := Use_Current;
950       Filename   : String        := Use_Current;
951       Callbacks  : Callback_Mode := None)
952    is
953       procedure Do_It is new For_Every_Line (Action);
954    begin
955       Do_It (Separators, Filename, Callbacks, Cur_Session);
956    end For_Every_Line_Current_Session;
957
958    --------------
959    -- Get_Line --
960    --------------
961
962    procedure Get_Line
963      (Callbacks : Callback_Mode := None;
964       Session   : Session_Type)
965    is
966       Filter_Active : Boolean;
967
968    begin
969       if not Text_IO.Is_Open (Session.Data.Current_File) then
970          raise File_Error;
971       end if;
972
973       loop
974          Read_Line (Session);
975          Split_Line (Session);
976
977          case Callbacks is
978
979             when None =>
980                exit;
981
982             when Only =>
983                Filter_Active := Apply_Filters (Session);
984                exit when not Filter_Active;
985
986             when Pass_Through =>
987                Filter_Active := Apply_Filters (Session);
988                exit;
989
990          end case;
991       end loop;
992    end Get_Line;
993
994    procedure Get_Line
995      (Callbacks : Callback_Mode := None)
996    is
997    begin
998       Get_Line (Callbacks, Cur_Session);
999    end Get_Line;
1000
1001    ----------------------
1002    -- Number_Of_Fields --
1003    ----------------------
1004
1005    function Number_Of_Fields
1006      (Session : Session_Type) return Count
1007    is
1008    begin
1009       return Count (Field_Table.Last (Session.Data.Fields));
1010    end Number_Of_Fields;
1011
1012    function Number_Of_Fields
1013      return Count
1014    is
1015    begin
1016       return Number_Of_Fields (Cur_Session);
1017    end Number_Of_Fields;
1018
1019    --------------------------
1020    -- Number_Of_File_Lines --
1021    --------------------------
1022
1023    function Number_Of_File_Lines
1024      (Session : Session_Type) return Count
1025    is
1026    begin
1027       return Count (Session.Data.FNR);
1028    end Number_Of_File_Lines;
1029
1030    function Number_Of_File_Lines
1031      return Count
1032    is
1033    begin
1034       return Number_Of_File_Lines (Cur_Session);
1035    end Number_Of_File_Lines;
1036
1037    ---------------------
1038    -- Number_Of_Files --
1039    ---------------------
1040
1041    function Number_Of_Files
1042      (Session : Session_Type) return Natural
1043    is
1044       Files : File_Table.Instance renames Session.Data.Files;
1045    begin
1046       return File_Table.Last (Files);
1047    end Number_Of_Files;
1048
1049    function Number_Of_Files
1050      return Natural
1051    is
1052    begin
1053       return Number_Of_Files (Cur_Session);
1054    end Number_Of_Files;
1055
1056    ---------------------
1057    -- Number_Of_Lines --
1058    ---------------------
1059
1060    function Number_Of_Lines
1061      (Session : Session_Type) return Count
1062    is
1063    begin
1064       return Count (Session.Data.NR);
1065    end Number_Of_Lines;
1066
1067    function Number_Of_Lines
1068      return Count
1069    is
1070    begin
1071       return Number_Of_Lines (Cur_Session);
1072    end Number_Of_Lines;
1073
1074    ----------
1075    -- Open --
1076    ----------
1077
1078    procedure Open
1079      (Separators : String       := Use_Current;
1080       Filename   : String       := Use_Current;
1081       Session    : Session_Type)
1082    is
1083    begin
1084       if Text_IO.Is_Open (Session.Data.Current_File) then
1085          raise Session_Error;
1086       end if;
1087
1088       if Filename /= Use_Current then
1089          File_Table.Init (Session.Data.Files);
1090          Add_File (Filename, Session);
1091       end if;
1092
1093       if Separators /= Use_Current then
1094          Set_Field_Separators (Separators, Session);
1095       end if;
1096
1097       Open_Next_File (Session);
1098
1099    exception
1100       when End_Error =>
1101          raise File_Error;
1102    end Open;
1103
1104    procedure Open
1105      (Separators : String       := Use_Current;
1106       Filename   : String       := Use_Current)
1107    is
1108    begin
1109       Open (Separators, Filename, Cur_Session);
1110    end Open;
1111
1112    --------------------
1113    -- Open_Next_File --
1114    --------------------
1115
1116    procedure Open_Next_File
1117      (Session : Session_Type)
1118    is
1119       Files : File_Table.Instance renames Session.Data.Files;
1120
1121    begin
1122       if Text_IO.Is_Open (Session.Data.Current_File) then
1123          Text_IO.Close (Session.Data.Current_File);
1124       end if;
1125
1126       Session.Data.File_Index := Session.Data.File_Index + 1;
1127
1128       --  If there are no mores file in the table, raise End_Error
1129
1130       if Session.Data.File_Index > File_Table.Last (Files) then
1131          raise End_Error;
1132       end if;
1133
1134       Text_IO.Open
1135         (File => Session.Data.Current_File,
1136          Name => Files.Table (Session.Data.File_Index).all,
1137          Mode => Text_IO.In_File);
1138    end Open_Next_File;
1139
1140    -----------
1141    -- Parse --
1142    -----------
1143
1144    procedure Parse
1145      (Separators : String       := Use_Current;
1146       Filename   : String       := Use_Current;
1147       Session    : Session_Type)
1148    is
1149       Filter_Active : Boolean;
1150       pragma Unreferenced (Filter_Active);
1151
1152    begin
1153       Open (Separators, Filename, Session);
1154
1155       while not End_Of_Data (Session) loop
1156          Get_Line (None, Session);
1157          Filter_Active := Apply_Filters (Session);
1158       end loop;
1159
1160       Close (Session);
1161    end Parse;
1162
1163    procedure Parse
1164      (Separators : String       := Use_Current;
1165       Filename   : String       := Use_Current)
1166    is
1167    begin
1168       Parse (Separators, Filename, Cur_Session);
1169    end Parse;
1170
1171    ---------------------
1172    -- Raise_With_Info --
1173    ---------------------
1174
1175    procedure Raise_With_Info
1176      (E       : Exceptions.Exception_Id;
1177       Message : String;
1178       Session : Session_Type)
1179    is
1180       function Filename return String;
1181       --  Returns current filename and "??" if this information is not
1182       --  available.
1183
1184       function Line return String;
1185       --  Returns current line number without the leading space
1186
1187       --------------
1188       -- Filename --
1189       --------------
1190
1191       function Filename return String is
1192          File : constant String := AWK.File (Session);
1193       begin
1194          if File = "" then
1195             return "??";
1196          else
1197             return File;
1198          end if;
1199       end Filename;
1200
1201       ----------
1202       -- Line --
1203       ----------
1204
1205       function Line return String is
1206          L : constant String := Natural'Image (Session.Data.FNR);
1207       begin
1208          return L (2 .. L'Last);
1209       end Line;
1210
1211    --  Start of processing for Raise_With_Info
1212
1213    begin
1214       Exceptions.Raise_Exception
1215         (E,
1216          '[' & Filename & ':' & Line & "] " & Message);
1217       raise Constraint_Error; -- to please GNAT as this is a No_Return proc
1218    end Raise_With_Info;
1219
1220    ---------------
1221    -- Read_Line --
1222    ---------------
1223
1224    procedure Read_Line (Session : Session_Type) is
1225
1226       function Read_Line return String;
1227       --  Read a line in the current file. This implementation is recursive
1228       --  and does not have a limitation on the line length.
1229
1230       NR  : Natural renames Session.Data.NR;
1231       FNR : Natural renames Session.Data.FNR;
1232
1233       ---------------
1234       -- Read_Line --
1235       ---------------
1236
1237       function Read_Line return String is
1238          Buffer : String (1 .. 1_024);
1239          Last   : Natural;
1240
1241       begin
1242          Text_IO.Get_Line (Session.Data.Current_File, Buffer, Last);
1243
1244          if Last = Buffer'Last then
1245             return Buffer & Read_Line;
1246          else
1247             return Buffer (1 .. Last);
1248          end if;
1249       end Read_Line;
1250
1251    --  Start of processing for Read_Line
1252
1253    begin
1254       if End_Of_File (Session) then
1255          Open_Next_File (Session);
1256          FNR := 0;
1257       end if;
1258
1259       Session.Data.Current_Line := To_Unbounded_String (Read_Line);
1260
1261       NR := NR + 1;
1262       FNR := FNR + 1;
1263    end Read_Line;
1264
1265    --------------
1266    -- Register --
1267    --------------
1268
1269    procedure Register
1270      (Field   : Count;
1271       Pattern : String;
1272       Action  : Action_Callback;
1273       Session : Session_Type)
1274    is
1275       Filters   : Pattern_Action_Table.Instance renames Session.Data.Filters;
1276       U_Pattern : constant Unbounded_String := To_Unbounded_String (Pattern);
1277
1278    begin
1279       Pattern_Action_Table.Increment_Last (Filters);
1280
1281       Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1282         (Pattern => new Patterns.String_Pattern'(U_Pattern, Field),
1283          Action  => new Actions.Simple_Action'(Proc => Action));
1284    end Register;
1285
1286    procedure Register
1287      (Field   : Count;
1288       Pattern : String;
1289       Action  : Action_Callback)
1290    is
1291    begin
1292       Register (Field, Pattern, Action, Cur_Session);
1293    end Register;
1294
1295    procedure Register
1296      (Field   : Count;
1297       Pattern : GNAT.Regpat.Pattern_Matcher;
1298       Action  : Action_Callback;
1299       Session : Session_Type)
1300    is
1301       Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1302
1303       A_Pattern : constant Patterns.Pattern_Matcher_Access :=
1304                     new Regpat.Pattern_Matcher'(Pattern);
1305    begin
1306       Pattern_Action_Table.Increment_Last (Filters);
1307
1308       Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1309         (Pattern => new Patterns.Regexp_Pattern'(A_Pattern, Field),
1310          Action  => new Actions.Simple_Action'(Proc => Action));
1311    end Register;
1312
1313    procedure Register
1314      (Field   : Count;
1315       Pattern : GNAT.Regpat.Pattern_Matcher;
1316       Action  : Action_Callback)
1317    is
1318    begin
1319       Register (Field, Pattern, Action, Cur_Session);
1320    end Register;
1321
1322    procedure Register
1323      (Field   : Count;
1324       Pattern : GNAT.Regpat.Pattern_Matcher;
1325       Action  : Match_Action_Callback;
1326       Session : Session_Type)
1327    is
1328       Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1329
1330       A_Pattern : constant Patterns.Pattern_Matcher_Access :=
1331                     new Regpat.Pattern_Matcher'(Pattern);
1332    begin
1333       Pattern_Action_Table.Increment_Last (Filters);
1334
1335       Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1336         (Pattern => new Patterns.Regexp_Pattern'(A_Pattern, Field),
1337          Action  => new Actions.Match_Action'(Proc => Action));
1338    end Register;
1339
1340    procedure Register
1341      (Field   : Count;
1342       Pattern : GNAT.Regpat.Pattern_Matcher;
1343       Action  : Match_Action_Callback)
1344    is
1345    begin
1346       Register (Field, Pattern, Action, Cur_Session);
1347    end Register;
1348
1349    procedure Register
1350      (Pattern : Pattern_Callback;
1351       Action  : Action_Callback;
1352       Session : Session_Type)
1353    is
1354       Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1355
1356    begin
1357       Pattern_Action_Table.Increment_Last (Filters);
1358
1359       Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1360         (Pattern => new Patterns.Callback_Pattern'(Pattern => Pattern),
1361          Action  => new Actions.Simple_Action'(Proc => Action));
1362    end Register;
1363
1364    procedure Register
1365      (Pattern : Pattern_Callback;
1366       Action  : Action_Callback)
1367    is
1368    begin
1369       Register (Pattern, Action, Cur_Session);
1370    end Register;
1371
1372    procedure Register
1373      (Action  : Action_Callback;
1374       Session : Session_Type)
1375    is
1376    begin
1377       Register (Always_True'Access, Action, Session);
1378    end Register;
1379
1380    procedure Register
1381      (Action  : Action_Callback)
1382    is
1383    begin
1384       Register (Action, Cur_Session);
1385    end Register;
1386
1387    -----------------
1388    -- Set_Current --
1389    -----------------
1390
1391    procedure Set_Current (Session : Session_Type) is
1392    begin
1393       Cur_Session.Data := Session.Data;
1394    end Set_Current;
1395
1396    --------------------------
1397    -- Set_Field_Separators --
1398    --------------------------
1399
1400    procedure Set_Field_Separators
1401      (Separators : String       := Default_Separators;
1402       Session    : Session_Type)
1403    is
1404    begin
1405       Free (Session.Data.Separators);
1406
1407       Session.Data.Separators :=
1408         new Split.Separator'(Separators'Length, Separators);
1409
1410       --  If there is a current line read, split it according to the new
1411       --  separators.
1412
1413       if Session.Data.Current_Line /= Null_Unbounded_String then
1414          Split_Line (Session);
1415       end if;
1416    end Set_Field_Separators;
1417
1418    procedure Set_Field_Separators
1419      (Separators : String       := Default_Separators)
1420    is
1421    begin
1422       Set_Field_Separators (Separators, Cur_Session);
1423    end Set_Field_Separators;
1424
1425    ----------------------
1426    -- Set_Field_Widths --
1427    ----------------------
1428
1429    procedure Set_Field_Widths
1430      (Field_Widths : Widths_Set;
1431       Session      : Session_Type)
1432    is
1433    begin
1434       Free (Session.Data.Separators);
1435
1436       Session.Data.Separators :=
1437         new Split.Column'(Field_Widths'Length, Field_Widths);
1438
1439       --  If there is a current line read, split it according to
1440       --  the new separators.
1441
1442       if Session.Data.Current_Line /= Null_Unbounded_String then
1443          Split_Line (Session);
1444       end if;
1445    end Set_Field_Widths;
1446
1447    procedure Set_Field_Widths
1448      (Field_Widths : Widths_Set)
1449    is
1450    begin
1451       Set_Field_Widths (Field_Widths, Cur_Session);
1452    end Set_Field_Widths;
1453
1454    ----------------
1455    -- Split_Line --
1456    ----------------
1457
1458    procedure Split_Line (Session : Session_Type) is
1459       Fields : Field_Table.Instance renames Session.Data.Fields;
1460    begin
1461       Field_Table.Init (Fields);
1462       Split.Current_Line (Session.Data.Separators.all, Session);
1463    end Split_Line;
1464
1465    -------------
1466    -- Get_Def --
1467    -------------
1468
1469    function Get_Def return Session_Data_Access is
1470    begin
1471       return Def_Session.Data;
1472    end Get_Def;
1473
1474    -------------
1475    -- Set_Cur --
1476    -------------
1477
1478    procedure Set_Cur is
1479    begin
1480       Cur_Session.Data := Def_Session.Data;
1481    end Set_Cur;
1482
1483 begin
1484    --  We have declared two sessions but both should share the same data.
1485    --  The current session must point to the default session as its initial
1486    --  value. So first we release the session data then we set current
1487    --  session data to point to default session data.
1488
1489    Free (Cur_Session.Data);
1490    Cur_Session.Data := Def_Session.Data;
1491 end GNAT.AWK;