gcc:
[official-gcc.git] / gcc / ada / g-awk.adb
blobe530efc15609afe4eb2119bff86312f89f6f9e1b
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-2006 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 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
28 -- --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
31 -- --
32 ------------------------------------------------------------------------------
34 pragma Ada_95;
35 -- This is needed because the pragmas Warnings (Off) in Current_Session and
36 -- Default_Session (see below) do not work when compiling clients of this
37 -- package that instantiate generic units herein.
39 pragma Style_Checks (All_Checks);
40 -- Turn off alpha ordering check for subprograms, since we cannot
41 -- Put Finalize and Initialize in alpha order (see comments).
43 with Ada.Exceptions;
44 with Ada.Text_IO;
45 with Ada.Strings.Unbounded;
46 with Ada.Strings.Fixed;
47 with Ada.Strings.Maps;
48 with Ada.Unchecked_Deallocation;
50 with GNAT.Directory_Operations;
51 with GNAT.Dynamic_Tables;
52 with GNAT.OS_Lib;
54 package body GNAT.AWK is
56 use Ada;
57 use Ada.Strings.Unbounded;
59 ----------------
60 -- Split mode --
61 ----------------
63 package Split is
65 type Mode is abstract tagged null record;
66 -- This is the main type which is declared abstract. This type must be
67 -- derived for each split style.
69 type Mode_Access is access Mode'Class;
71 procedure Current_Line (S : Mode; Session : Session_Type)
72 is abstract;
73 -- Split current line of Session using split mode S
75 ------------------------
76 -- Split on separator --
77 ------------------------
79 type Separator (Size : Positive) is new Mode with record
80 Separators : String (1 .. Size);
81 end record;
83 procedure Current_Line
84 (S : Separator;
85 Session : Session_Type);
87 ---------------------
88 -- Split on column --
89 ---------------------
91 type Column (Size : Positive) is new Mode with record
92 Columns : Widths_Set (1 .. Size);
93 end record;
95 procedure Current_Line (S : Column; Session : Session_Type);
97 end Split;
99 procedure Free is new Unchecked_Deallocation
100 (Split.Mode'Class, Split.Mode_Access);
102 ----------------
103 -- File_Table --
104 ----------------
106 type AWK_File is access String;
108 package File_Table is
109 new Dynamic_Tables (AWK_File, Natural, 1, 5, 50);
110 -- List of file names associated with a Session
112 procedure Free is new Unchecked_Deallocation (String, AWK_File);
114 -----------------
115 -- Field_Table --
116 -----------------
118 type Field_Slice is record
119 First : Positive;
120 Last : Natural;
121 end record;
122 -- This is a field slice (First .. Last) in session's current line
124 package Field_Table is
125 new Dynamic_Tables (Field_Slice, Natural, 1, 10, 100);
126 -- List of fields for the current line
128 --------------
129 -- Patterns --
130 --------------
132 -- Define all patterns style: exact string, regular expression, boolean
133 -- function.
135 package Patterns is
137 type Pattern is abstract tagged null record;
138 -- This is the main type which is declared abstract. This type must be
139 -- derived for each patterns style.
141 type Pattern_Access is access Pattern'Class;
143 function Match
144 (P : Pattern;
145 Session : Session_Type) return Boolean
146 is abstract;
147 -- Returns True if P match for the current session and False otherwise
149 procedure Release (P : in out Pattern);
150 -- Release memory used by the pattern structure
152 --------------------------
153 -- Exact string pattern --
154 --------------------------
156 type String_Pattern is new Pattern with record
157 Str : Unbounded_String;
158 Rank : Count;
159 end record;
161 function Match
162 (P : String_Pattern;
163 Session : Session_Type) return Boolean;
165 --------------------------------
166 -- Regular expression pattern --
167 --------------------------------
169 type Pattern_Matcher_Access is access Regpat.Pattern_Matcher;
171 type Regexp_Pattern is new Pattern with record
172 Regx : Pattern_Matcher_Access;
173 Rank : Count;
174 end record;
176 function Match
177 (P : Regexp_Pattern;
178 Session : Session_Type) return Boolean;
180 procedure Release (P : in out Regexp_Pattern);
182 ------------------------------
183 -- Boolean function pattern --
184 ------------------------------
186 type Callback_Pattern is new Pattern with record
187 Pattern : Pattern_Callback;
188 end record;
190 function Match
191 (P : Callback_Pattern;
192 Session : Session_Type) return Boolean;
194 end Patterns;
196 procedure Free is new Unchecked_Deallocation
197 (Patterns.Pattern'Class, Patterns.Pattern_Access);
199 -------------
200 -- Actions --
201 -------------
203 -- Define all action style : simple call, call with matches
205 package Actions is
207 type Action is abstract tagged null record;
208 -- This is the main type which is declared abstract. This type must be
209 -- derived for each action style.
211 type Action_Access is access Action'Class;
213 procedure Call
214 (A : Action;
215 Session : Session_Type) is abstract;
216 -- Call action A as required
218 -------------------
219 -- Simple action --
220 -------------------
222 type Simple_Action is new Action with record
223 Proc : Action_Callback;
224 end record;
226 procedure Call
227 (A : Simple_Action;
228 Session : Session_Type);
230 -------------------------
231 -- Action with matches --
232 -------------------------
234 type Match_Action is new Action with record
235 Proc : Match_Action_Callback;
236 end record;
238 procedure Call
239 (A : Match_Action;
240 Session : Session_Type);
242 end Actions;
244 procedure Free is new Unchecked_Deallocation
245 (Actions.Action'Class, Actions.Action_Access);
247 --------------------------
248 -- Pattern/Action table --
249 --------------------------
251 type Pattern_Action is record
252 Pattern : Patterns.Pattern_Access; -- If Pattern is True
253 Action : Actions.Action_Access; -- Action will be called
254 end record;
256 package Pattern_Action_Table is
257 new Dynamic_Tables (Pattern_Action, Natural, 1, 5, 50);
259 ------------------
260 -- Session Data --
261 ------------------
263 type Session_Data is record
264 Current_File : Text_IO.File_Type;
265 Current_Line : Unbounded_String;
266 Separators : Split.Mode_Access;
267 Files : File_Table.Instance;
268 File_Index : Natural := 0;
269 Fields : Field_Table.Instance;
270 Filters : Pattern_Action_Table.Instance;
271 NR : Natural := 0;
272 FNR : Natural := 0;
273 Matches : Regpat.Match_Array (0 .. 100);
274 -- Latest matches for the regexp pattern
275 end record;
277 procedure Free is
278 new Unchecked_Deallocation (Session_Data, Session_Data_Access);
280 ----------------
281 -- Initialize --
282 ----------------
284 procedure Initialize (Session : in out Session_Type) is
285 begin
286 Session.Data := new Session_Data;
288 -- Initialize separators
290 Session.Data.Separators :=
291 new Split.Separator'(Default_Separators'Length, Default_Separators);
293 -- Initialize all tables
295 File_Table.Init (Session.Data.Files);
296 Field_Table.Init (Session.Data.Fields);
297 Pattern_Action_Table.Init (Session.Data.Filters);
298 end Initialize;
300 -----------------------
301 -- Session Variables --
302 -----------------------
304 -- These must come after the body of Initialize, since they make
305 -- implicit calls to Initialize at elaboration time.
307 Def_Session : Session_Type;
308 Cur_Session : Session_Type;
310 --------------
311 -- Finalize --
312 --------------
314 -- Note: Finalize must come after Initialize and the definition
315 -- of the Def_Session and Cur_Session variables, since it references
316 -- the latter.
318 procedure Finalize (Session : in out Session_Type) is
319 begin
320 -- We release the session data only if it is not the default session
322 if Session.Data /= Def_Session.Data then
323 Free (Session.Data);
325 -- Since we have closed the current session, set it to point now to
326 -- the default session.
328 Cur_Session.Data := Def_Session.Data;
329 end if;
330 end Finalize;
332 ----------------------
333 -- Private Services --
334 ----------------------
336 function Always_True return Boolean;
337 -- A function that always returns True
339 function Apply_Filters
340 (Session : Session_Type) return Boolean;
341 -- Apply any filters for which the Pattern is True for Session. It returns
342 -- True if a least one filters has been applied (i.e. associated action
343 -- callback has been called).
345 procedure Open_Next_File
346 (Session : Session_Type);
347 pragma Inline (Open_Next_File);
348 -- Open next file for Session closing current file if needed. It raises
349 -- End_Error if there is no more file in the table.
351 procedure Raise_With_Info
352 (E : Exceptions.Exception_Id;
353 Message : String;
354 Session : Session_Type);
355 pragma No_Return (Raise_With_Info);
356 -- Raises exception E with the message prepended with the current line
357 -- number and the filename if possible.
359 procedure Read_Line (Session : Session_Type);
360 -- Read a line for the Session and set Current_Line
362 procedure Split_Line (Session : Session_Type);
363 -- Split session's Current_Line according to the session separators and
364 -- set the Fields table. This procedure can be called at any time.
366 ----------------------
367 -- Private Packages --
368 ----------------------
370 -------------
371 -- Actions --
372 -------------
374 package body Actions is
376 ----------
377 -- Call --
378 ----------
380 procedure Call
381 (A : Simple_Action;
382 Session : Session_Type)
384 pragma Unreferenced (Session);
385 begin
386 A.Proc.all;
387 end Call;
389 ----------
390 -- Call --
391 ----------
393 procedure Call
394 (A : Match_Action;
395 Session : Session_Type)
397 begin
398 A.Proc (Session.Data.Matches);
399 end Call;
401 end Actions;
403 --------------
404 -- Patterns --
405 --------------
407 package body Patterns is
409 -----------
410 -- Match --
411 -----------
413 function Match
414 (P : String_Pattern;
415 Session : Session_Type) return Boolean
417 begin
418 return P.Str = Field (P.Rank, Session);
419 end Match;
421 -----------
422 -- Match --
423 -----------
425 function Match
426 (P : Regexp_Pattern;
427 Session : Session_Type) return Boolean
429 use type Regpat.Match_Location;
430 begin
431 Regpat.Match
432 (P.Regx.all, Field (P.Rank, Session), Session.Data.Matches);
433 return Session.Data.Matches (0) /= Regpat.No_Match;
434 end Match;
436 -----------
437 -- Match --
438 -----------
440 function Match
441 (P : Callback_Pattern;
442 Session : Session_Type) return Boolean
444 pragma Unreferenced (Session);
445 begin
446 return P.Pattern.all;
447 end Match;
449 -------------
450 -- Release --
451 -------------
453 procedure Release (P : in out Pattern) is
454 pragma Unreferenced (P);
455 begin
456 null;
457 end Release;
459 -------------
460 -- Release --
461 -------------
463 procedure Release (P : in out Regexp_Pattern) is
464 procedure Free is new Unchecked_Deallocation
465 (Regpat.Pattern_Matcher, Pattern_Matcher_Access);
466 begin
467 Free (P.Regx);
468 end Release;
470 end Patterns;
472 -----------
473 -- Split --
474 -----------
476 package body Split is
478 use Ada.Strings;
480 ------------------
481 -- Current_Line --
482 ------------------
484 procedure Current_Line (S : Separator; Session : Session_Type) is
485 Line : constant String := To_String (Session.Data.Current_Line);
486 Fields : Field_Table.Instance renames Session.Data.Fields;
488 Start : Natural;
489 Stop : Natural;
491 Seps : constant Maps.Character_Set := Maps.To_Set (S.Separators);
493 begin
494 -- First field start here
496 Start := Line'First;
498 -- Record the first field start position which is the first character
499 -- in the line.
501 Field_Table.Increment_Last (Fields);
502 Fields.Table (Field_Table.Last (Fields)).First := Start;
504 loop
505 -- Look for next separator
507 Stop := Fixed.Index
508 (Source => Line (Start .. Line'Last),
509 Set => Seps);
511 exit when Stop = 0;
513 Fields.Table (Field_Table.Last (Fields)).Last := Stop - 1;
515 -- If separators are set to the default (space and tab) we skip
516 -- all spaces and tabs following current field.
518 if S.Separators = Default_Separators then
519 Start := Fixed.Index
520 (Line (Stop + 1 .. Line'Last),
521 Maps.To_Set (Default_Separators),
522 Outside,
523 Strings.Forward);
525 if Start = 0 then
526 Start := Stop + 1;
527 end if;
528 else
529 Start := Stop + 1;
530 end if;
532 -- Record in the field table the start of this new field
534 Field_Table.Increment_Last (Fields);
535 Fields.Table (Field_Table.Last (Fields)).First := Start;
537 end loop;
539 Fields.Table (Field_Table.Last (Fields)).Last := Line'Last;
540 end Current_Line;
542 ------------------
543 -- Current_Line --
544 ------------------
546 procedure Current_Line (S : Column; Session : Session_Type) is
547 Line : constant String := To_String (Session.Data.Current_Line);
548 Fields : Field_Table.Instance renames Session.Data.Fields;
549 Start : Positive := Line'First;
551 begin
552 -- Record the first field start position which is the first character
553 -- in the line.
555 for C in 1 .. S.Columns'Length loop
557 Field_Table.Increment_Last (Fields);
559 Fields.Table (Field_Table.Last (Fields)).First := Start;
561 Start := Start + S.Columns (C);
563 Fields.Table (Field_Table.Last (Fields)).Last := Start - 1;
565 end loop;
567 -- If there is some remaining character on the line, add them in a
568 -- new field.
570 if Start - 1 < Line'Length then
572 Field_Table.Increment_Last (Fields);
574 Fields.Table (Field_Table.Last (Fields)).First := Start;
576 Fields.Table (Field_Table.Last (Fields)).Last := Line'Last;
577 end if;
578 end Current_Line;
580 end Split;
582 --------------
583 -- Add_File --
584 --------------
586 procedure Add_File
587 (Filename : String;
588 Session : Session_Type)
590 Files : File_Table.Instance renames Session.Data.Files;
592 begin
593 if OS_Lib.Is_Regular_File (Filename) then
594 File_Table.Increment_Last (Files);
595 Files.Table (File_Table.Last (Files)) := new String'(Filename);
596 else
597 Raise_With_Info
598 (File_Error'Identity,
599 "File " & Filename & " not found.",
600 Session);
601 end if;
602 end Add_File;
604 procedure Add_File
605 (Filename : String)
608 begin
609 Add_File (Filename, Cur_Session);
610 end Add_File;
612 ---------------
613 -- Add_Files --
614 ---------------
616 procedure Add_Files
617 (Directory : String;
618 Filenames : String;
619 Number_Of_Files_Added : out Natural;
620 Session : Session_Type)
622 use Directory_Operations;
624 Dir : Dir_Type;
625 Filename : String (1 .. 200);
626 Last : Natural;
628 begin
629 Number_Of_Files_Added := 0;
631 Open (Dir, Directory);
633 loop
634 Read (Dir, Filename, Last);
635 exit when Last = 0;
637 Add_File (Filename (1 .. Last), Session);
638 Number_Of_Files_Added := Number_Of_Files_Added + 1;
639 end loop;
641 Close (Dir);
643 exception
644 when others =>
645 Raise_With_Info
646 (File_Error'Identity,
647 "Error scaning directory " & Directory
648 & " for files " & Filenames & '.',
649 Session);
650 end Add_Files;
652 procedure Add_Files
653 (Directory : String;
654 Filenames : String;
655 Number_Of_Files_Added : out Natural)
658 begin
659 Add_Files (Directory, Filenames, Number_Of_Files_Added, Cur_Session);
660 end Add_Files;
662 -----------------
663 -- Always_True --
664 -----------------
666 function Always_True return Boolean is
667 begin
668 return True;
669 end Always_True;
671 -------------------
672 -- Apply_Filters --
673 -------------------
675 function Apply_Filters
676 (Session : Session_Type) return Boolean
678 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
679 Results : Boolean := False;
681 begin
682 -- Iterate through the filters table, if pattern match call action
684 for F in 1 .. Pattern_Action_Table.Last (Filters) loop
685 if Patterns.Match (Filters.Table (F).Pattern.all, Session) then
686 Results := True;
687 Actions.Call (Filters.Table (F).Action.all, Session);
688 end if;
689 end loop;
691 return Results;
692 end Apply_Filters;
694 -----------
695 -- Close --
696 -----------
698 procedure Close (Session : Session_Type) is
699 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
700 Files : File_Table.Instance renames Session.Data.Files;
702 begin
703 -- Close current file if needed
705 if Text_IO.Is_Open (Session.Data.Current_File) then
706 Text_IO.Close (Session.Data.Current_File);
707 end if;
709 -- Release separators
711 Free (Session.Data.Separators);
713 -- Release Filters table
715 for F in 1 .. Pattern_Action_Table.Last (Filters) loop
716 Patterns.Release (Filters.Table (F).Pattern.all);
717 Free (Filters.Table (F).Pattern);
718 Free (Filters.Table (F).Action);
719 end loop;
721 for F in 1 .. File_Table.Last (Files) loop
722 Free (Files.Table (F));
723 end loop;
725 File_Table.Set_Last (Session.Data.Files, 0);
726 Field_Table.Set_Last (Session.Data.Fields, 0);
727 Pattern_Action_Table.Set_Last (Session.Data.Filters, 0);
729 Session.Data.NR := 0;
730 Session.Data.FNR := 0;
731 Session.Data.File_Index := 0;
732 Session.Data.Current_Line := Null_Unbounded_String;
733 end Close;
735 ---------------------
736 -- Current_Session --
737 ---------------------
739 function Current_Session return Session_Type is
740 begin
741 pragma Warnings (Off);
742 return Cur_Session;
743 -- ???The above return statement violates the Ada 2005 rule forbidding
744 -- copying of limited objects (see RM-7.5(2.8/2)). When compiled with
745 -- -gnatg, the compiler gives a warning instead of an error, so we can
746 -- turn it off.
747 pragma Warnings (On);
748 end Current_Session;
750 ---------------------
751 -- Default_Session --
752 ---------------------
754 function Default_Session return Session_Type is
755 begin
756 pragma Warnings (Off);
757 return Def_Session;
758 -- ???The above return statement violates the Ada 2005 rule forbidding
759 -- copying of limited objects (see RM-7.5(2.8/2)). When compiled with
760 -- -gnatg, the compiler gives a warning instead of an error, so we can
761 -- turn it off.
762 pragma Warnings (On);
763 end Default_Session;
765 --------------------
766 -- Discrete_Field --
767 --------------------
769 function Discrete_Field
770 (Rank : Count;
771 Session : Session_Type) return Discrete
773 begin
774 return Discrete'Value (Field (Rank, Session));
775 end Discrete_Field;
777 function Discrete_Field_Current_Session
778 (Rank : Count) return Discrete is
779 function Do_It is new Discrete_Field (Discrete);
780 begin
781 return Do_It (Rank, Cur_Session);
782 end Discrete_Field_Current_Session;
784 -----------------
785 -- End_Of_Data --
786 -----------------
788 function End_Of_Data
789 (Session : Session_Type) return Boolean
791 begin
792 return Session.Data.File_Index = File_Table.Last (Session.Data.Files)
793 and then End_Of_File (Session);
794 end End_Of_Data;
796 function End_Of_Data
797 return Boolean
799 begin
800 return End_Of_Data (Cur_Session);
801 end End_Of_Data;
803 -----------------
804 -- End_Of_File --
805 -----------------
807 function End_Of_File
808 (Session : Session_Type) return Boolean
810 begin
811 return Text_IO.End_Of_File (Session.Data.Current_File);
812 end End_Of_File;
814 function End_Of_File
815 return Boolean
817 begin
818 return End_Of_File (Cur_Session);
819 end End_Of_File;
821 -----------
822 -- Field --
823 -----------
825 function Field
826 (Rank : Count;
827 Session : Session_Type) return String
829 Fields : Field_Table.Instance renames Session.Data.Fields;
831 begin
832 if Rank > Number_Of_Fields (Session) then
833 Raise_With_Info
834 (Field_Error'Identity,
835 "Field number" & Count'Image (Rank) & " does not exist.",
836 Session);
838 elsif Rank = 0 then
840 -- Returns the whole line, this is what $0 does under Session_Type
842 return To_String (Session.Data.Current_Line);
844 else
845 return Slice (Session.Data.Current_Line,
846 Fields.Table (Positive (Rank)).First,
847 Fields.Table (Positive (Rank)).Last);
848 end if;
849 end Field;
851 function Field
852 (Rank : Count) return String
854 begin
855 return Field (Rank, Cur_Session);
856 end Field;
858 function Field
859 (Rank : Count;
860 Session : Session_Type) return Integer
862 begin
863 return Integer'Value (Field (Rank, Session));
865 exception
866 when Constraint_Error =>
867 Raise_With_Info
868 (Field_Error'Identity,
869 "Field number" & Count'Image (Rank)
870 & " cannot be converted to an integer.",
871 Session);
872 end Field;
874 function Field
875 (Rank : Count) return Integer
877 begin
878 return Field (Rank, Cur_Session);
879 end Field;
881 function Field
882 (Rank : Count;
883 Session : Session_Type) return Float
885 begin
886 return Float'Value (Field (Rank, Session));
888 exception
889 when Constraint_Error =>
890 Raise_With_Info
891 (Field_Error'Identity,
892 "Field number" & Count'Image (Rank)
893 & " cannot be converted to a float.",
894 Session);
895 end Field;
897 function Field
898 (Rank : Count) return Float
900 begin
901 return Field (Rank, Cur_Session);
902 end Field;
904 ----------
905 -- File --
906 ----------
908 function File
909 (Session : Session_Type) return String
911 Files : File_Table.Instance renames Session.Data.Files;
913 begin
914 if Session.Data.File_Index = 0 then
915 return "??";
916 else
917 return Files.Table (Session.Data.File_Index).all;
918 end if;
919 end File;
921 function File
922 return String
924 begin
925 return File (Cur_Session);
926 end File;
928 --------------------
929 -- For_Every_Line --
930 --------------------
932 procedure For_Every_Line
933 (Separators : String := Use_Current;
934 Filename : String := Use_Current;
935 Callbacks : Callback_Mode := None;
936 Session : Session_Type)
938 Quit : Boolean;
940 begin
941 Open (Separators, Filename, Session);
943 while not End_Of_Data (Session) loop
944 Read_Line (Session);
945 Split_Line (Session);
947 if Callbacks in Only .. Pass_Through then
948 declare
949 Discard : Boolean;
950 pragma Unreferenced (Discard);
951 begin
952 Discard := Apply_Filters (Session);
953 end;
954 end if;
956 if Callbacks /= Only then
957 Quit := False;
958 Action (Quit);
959 exit when Quit;
960 end if;
961 end loop;
963 Close (Session);
964 end For_Every_Line;
966 procedure For_Every_Line_Current_Session
967 (Separators : String := Use_Current;
968 Filename : String := Use_Current;
969 Callbacks : Callback_Mode := None)
971 procedure Do_It is new For_Every_Line (Action);
972 begin
973 Do_It (Separators, Filename, Callbacks, Cur_Session);
974 end For_Every_Line_Current_Session;
976 --------------
977 -- Get_Line --
978 --------------
980 procedure Get_Line
981 (Callbacks : Callback_Mode := None;
982 Session : Session_Type)
984 Filter_Active : Boolean;
986 begin
987 if not Text_IO.Is_Open (Session.Data.Current_File) then
988 raise File_Error;
989 end if;
991 loop
992 Read_Line (Session);
993 Split_Line (Session);
995 case Callbacks is
997 when None =>
998 exit;
1000 when Only =>
1001 Filter_Active := Apply_Filters (Session);
1002 exit when not Filter_Active;
1004 when Pass_Through =>
1005 Filter_Active := Apply_Filters (Session);
1006 exit;
1008 end case;
1009 end loop;
1010 end Get_Line;
1012 procedure Get_Line
1013 (Callbacks : Callback_Mode := None)
1015 begin
1016 Get_Line (Callbacks, Cur_Session);
1017 end Get_Line;
1019 ----------------------
1020 -- Number_Of_Fields --
1021 ----------------------
1023 function Number_Of_Fields
1024 (Session : Session_Type) return Count
1026 begin
1027 return Count (Field_Table.Last (Session.Data.Fields));
1028 end Number_Of_Fields;
1030 function Number_Of_Fields
1031 return Count
1033 begin
1034 return Number_Of_Fields (Cur_Session);
1035 end Number_Of_Fields;
1037 --------------------------
1038 -- Number_Of_File_Lines --
1039 --------------------------
1041 function Number_Of_File_Lines
1042 (Session : Session_Type) return Count
1044 begin
1045 return Count (Session.Data.FNR);
1046 end Number_Of_File_Lines;
1048 function Number_Of_File_Lines
1049 return Count
1051 begin
1052 return Number_Of_File_Lines (Cur_Session);
1053 end Number_Of_File_Lines;
1055 ---------------------
1056 -- Number_Of_Files --
1057 ---------------------
1059 function Number_Of_Files
1060 (Session : Session_Type) return Natural
1062 Files : File_Table.Instance renames Session.Data.Files;
1063 begin
1064 return File_Table.Last (Files);
1065 end Number_Of_Files;
1067 function Number_Of_Files
1068 return Natural
1070 begin
1071 return Number_Of_Files (Cur_Session);
1072 end Number_Of_Files;
1074 ---------------------
1075 -- Number_Of_Lines --
1076 ---------------------
1078 function Number_Of_Lines
1079 (Session : Session_Type) return Count
1081 begin
1082 return Count (Session.Data.NR);
1083 end Number_Of_Lines;
1085 function Number_Of_Lines
1086 return Count
1088 begin
1089 return Number_Of_Lines (Cur_Session);
1090 end Number_Of_Lines;
1092 ----------
1093 -- Open --
1094 ----------
1096 procedure Open
1097 (Separators : String := Use_Current;
1098 Filename : String := Use_Current;
1099 Session : Session_Type)
1101 begin
1102 if Text_IO.Is_Open (Session.Data.Current_File) then
1103 raise Session_Error;
1104 end if;
1106 if Filename /= Use_Current then
1107 File_Table.Init (Session.Data.Files);
1108 Add_File (Filename, Session);
1109 end if;
1111 if Separators /= Use_Current then
1112 Set_Field_Separators (Separators, Session);
1113 end if;
1115 Open_Next_File (Session);
1117 exception
1118 when End_Error =>
1119 raise File_Error;
1120 end Open;
1122 procedure Open
1123 (Separators : String := Use_Current;
1124 Filename : String := Use_Current)
1126 begin
1127 Open (Separators, Filename, Cur_Session);
1128 end Open;
1130 --------------------
1131 -- Open_Next_File --
1132 --------------------
1134 procedure Open_Next_File
1135 (Session : Session_Type)
1137 Files : File_Table.Instance renames Session.Data.Files;
1139 begin
1140 if Text_IO.Is_Open (Session.Data.Current_File) then
1141 Text_IO.Close (Session.Data.Current_File);
1142 end if;
1144 Session.Data.File_Index := Session.Data.File_Index + 1;
1146 -- If there are no mores file in the table, raise End_Error
1148 if Session.Data.File_Index > File_Table.Last (Files) then
1149 raise End_Error;
1150 end if;
1152 Text_IO.Open
1153 (File => Session.Data.Current_File,
1154 Name => Files.Table (Session.Data.File_Index).all,
1155 Mode => Text_IO.In_File);
1156 end Open_Next_File;
1158 -----------
1159 -- Parse --
1160 -----------
1162 procedure Parse
1163 (Separators : String := Use_Current;
1164 Filename : String := Use_Current;
1165 Session : Session_Type)
1167 Filter_Active : Boolean;
1168 pragma Unreferenced (Filter_Active);
1170 begin
1171 Open (Separators, Filename, Session);
1173 while not End_Of_Data (Session) loop
1174 Get_Line (None, Session);
1175 Filter_Active := Apply_Filters (Session);
1176 end loop;
1178 Close (Session);
1179 end Parse;
1181 procedure Parse
1182 (Separators : String := Use_Current;
1183 Filename : String := Use_Current)
1185 begin
1186 Parse (Separators, Filename, Cur_Session);
1187 end Parse;
1189 ---------------------
1190 -- Raise_With_Info --
1191 ---------------------
1193 procedure Raise_With_Info
1194 (E : Exceptions.Exception_Id;
1195 Message : String;
1196 Session : Session_Type)
1198 function Filename return String;
1199 -- Returns current filename and "??" if this information is not
1200 -- available.
1202 function Line return String;
1203 -- Returns current line number without the leading space
1205 --------------
1206 -- Filename --
1207 --------------
1209 function Filename return String is
1210 File : constant String := AWK.File (Session);
1211 begin
1212 if File = "" then
1213 return "??";
1214 else
1215 return File;
1216 end if;
1217 end Filename;
1219 ----------
1220 -- Line --
1221 ----------
1223 function Line return String is
1224 L : constant String := Natural'Image (Session.Data.FNR);
1225 begin
1226 return L (2 .. L'Last);
1227 end Line;
1229 -- Start of processing for Raise_With_Info
1231 begin
1232 Exceptions.Raise_Exception
1234 '[' & Filename & ':' & Line & "] " & Message);
1235 raise Constraint_Error; -- to please GNAT as this is a No_Return proc
1236 end Raise_With_Info;
1238 ---------------
1239 -- Read_Line --
1240 ---------------
1242 procedure Read_Line (Session : Session_Type) is
1244 function Read_Line return String;
1245 -- Read a line in the current file. This implementation is recursive
1246 -- and does not have a limitation on the line length.
1248 NR : Natural renames Session.Data.NR;
1249 FNR : Natural renames Session.Data.FNR;
1251 ---------------
1252 -- Read_Line --
1253 ---------------
1255 function Read_Line return String is
1256 Buffer : String (1 .. 1_024);
1257 Last : Natural;
1259 begin
1260 Text_IO.Get_Line (Session.Data.Current_File, Buffer, Last);
1262 if Last = Buffer'Last then
1263 return Buffer & Read_Line;
1264 else
1265 return Buffer (1 .. Last);
1266 end if;
1267 end Read_Line;
1269 -- Start of processing for Read_Line
1271 begin
1272 if End_Of_File (Session) then
1273 Open_Next_File (Session);
1274 FNR := 0;
1275 end if;
1277 Session.Data.Current_Line := To_Unbounded_String (Read_Line);
1279 NR := NR + 1;
1280 FNR := FNR + 1;
1281 end Read_Line;
1283 --------------
1284 -- Register --
1285 --------------
1287 procedure Register
1288 (Field : Count;
1289 Pattern : String;
1290 Action : Action_Callback;
1291 Session : Session_Type)
1293 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1294 U_Pattern : constant Unbounded_String := To_Unbounded_String (Pattern);
1296 begin
1297 Pattern_Action_Table.Increment_Last (Filters);
1299 Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1300 (Pattern => new Patterns.String_Pattern'(U_Pattern, Field),
1301 Action => new Actions.Simple_Action'(Proc => Action));
1302 end Register;
1304 procedure Register
1305 (Field : Count;
1306 Pattern : String;
1307 Action : Action_Callback)
1309 begin
1310 Register (Field, Pattern, Action, Cur_Session);
1311 end Register;
1313 procedure Register
1314 (Field : Count;
1315 Pattern : GNAT.Regpat.Pattern_Matcher;
1316 Action : Action_Callback;
1317 Session : Session_Type)
1319 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1321 A_Pattern : constant Patterns.Pattern_Matcher_Access :=
1322 new Regpat.Pattern_Matcher'(Pattern);
1323 begin
1324 Pattern_Action_Table.Increment_Last (Filters);
1326 Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1327 (Pattern => new Patterns.Regexp_Pattern'(A_Pattern, Field),
1328 Action => new Actions.Simple_Action'(Proc => Action));
1329 end Register;
1331 procedure Register
1332 (Field : Count;
1333 Pattern : GNAT.Regpat.Pattern_Matcher;
1334 Action : Action_Callback)
1336 begin
1337 Register (Field, Pattern, Action, Cur_Session);
1338 end Register;
1340 procedure Register
1341 (Field : Count;
1342 Pattern : GNAT.Regpat.Pattern_Matcher;
1343 Action : Match_Action_Callback;
1344 Session : Session_Type)
1346 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1348 A_Pattern : constant Patterns.Pattern_Matcher_Access :=
1349 new Regpat.Pattern_Matcher'(Pattern);
1350 begin
1351 Pattern_Action_Table.Increment_Last (Filters);
1353 Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1354 (Pattern => new Patterns.Regexp_Pattern'(A_Pattern, Field),
1355 Action => new Actions.Match_Action'(Proc => Action));
1356 end Register;
1358 procedure Register
1359 (Field : Count;
1360 Pattern : GNAT.Regpat.Pattern_Matcher;
1361 Action : Match_Action_Callback)
1363 begin
1364 Register (Field, Pattern, Action, Cur_Session);
1365 end Register;
1367 procedure Register
1368 (Pattern : Pattern_Callback;
1369 Action : Action_Callback;
1370 Session : Session_Type)
1372 Filters : Pattern_Action_Table.Instance renames Session.Data.Filters;
1374 begin
1375 Pattern_Action_Table.Increment_Last (Filters);
1377 Filters.Table (Pattern_Action_Table.Last (Filters)) :=
1378 (Pattern => new Patterns.Callback_Pattern'(Pattern => Pattern),
1379 Action => new Actions.Simple_Action'(Proc => Action));
1380 end Register;
1382 procedure Register
1383 (Pattern : Pattern_Callback;
1384 Action : Action_Callback)
1386 begin
1387 Register (Pattern, Action, Cur_Session);
1388 end Register;
1390 procedure Register
1391 (Action : Action_Callback;
1392 Session : Session_Type)
1394 begin
1395 Register (Always_True'Access, Action, Session);
1396 end Register;
1398 procedure Register
1399 (Action : Action_Callback)
1401 begin
1402 Register (Action, Cur_Session);
1403 end Register;
1405 -----------------
1406 -- Set_Current --
1407 -----------------
1409 procedure Set_Current (Session : Session_Type) is
1410 begin
1411 Cur_Session.Data := Session.Data;
1412 end Set_Current;
1414 --------------------------
1415 -- Set_Field_Separators --
1416 --------------------------
1418 procedure Set_Field_Separators
1419 (Separators : String := Default_Separators;
1420 Session : Session_Type)
1422 begin
1423 Free (Session.Data.Separators);
1425 Session.Data.Separators :=
1426 new Split.Separator'(Separators'Length, Separators);
1428 -- If there is a current line read, split it according to the new
1429 -- separators.
1431 if Session.Data.Current_Line /= Null_Unbounded_String then
1432 Split_Line (Session);
1433 end if;
1434 end Set_Field_Separators;
1436 procedure Set_Field_Separators
1437 (Separators : String := Default_Separators)
1439 begin
1440 Set_Field_Separators (Separators, Cur_Session);
1441 end Set_Field_Separators;
1443 ----------------------
1444 -- Set_Field_Widths --
1445 ----------------------
1447 procedure Set_Field_Widths
1448 (Field_Widths : Widths_Set;
1449 Session : Session_Type)
1451 begin
1452 Free (Session.Data.Separators);
1454 Session.Data.Separators :=
1455 new Split.Column'(Field_Widths'Length, Field_Widths);
1457 -- If there is a current line read, split it according to
1458 -- the new separators.
1460 if Session.Data.Current_Line /= Null_Unbounded_String then
1461 Split_Line (Session);
1462 end if;
1463 end Set_Field_Widths;
1465 procedure Set_Field_Widths
1466 (Field_Widths : Widths_Set)
1468 begin
1469 Set_Field_Widths (Field_Widths, Cur_Session);
1470 end Set_Field_Widths;
1472 ----------------
1473 -- Split_Line --
1474 ----------------
1476 procedure Split_Line (Session : Session_Type) is
1477 Fields : Field_Table.Instance renames Session.Data.Fields;
1478 begin
1479 Field_Table.Init (Fields);
1480 Split.Current_Line (Session.Data.Separators.all, Session);
1481 end Split_Line;
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.
1489 Free (Cur_Session.Data);
1490 Cur_Session.Data := Def_Session.Data;
1491 end GNAT.AWK;