1 ------------------------------------------------------------------------------
3 -- GNAT SYSTEM UTILITIES --
9 -- Copyright (C) 2003-2007, Free Software Foundation, Inc. --
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. 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 ------------------------------------------------------------------------------
23 -- This utility is used to process the source of gnat_ugn.texi to make a
24 -- version suitable for running through standard Texinfo processor. It is
25 -- invoked as follows:
27 -- xgnatugn <target> <in-file> <word-list> [ <out-file> [ <warnings> ] ]
29 -- 1. <target> is the target type of the manual, which is one of:
31 -- unw Unix and Windows platforms
34 -- 2. <in-file> is the file name of the Texinfo file to be
37 -- 3. <word-list> is the name of the word list file. This file is used for
38 -- rewriting the VMS edition. Each line contains a word mapping: The source
39 -- word in the first column, the target word in the second column. The
40 -- columns are separated by a '^' character. When preprocessing for VMS, the
41 -- first word is replaced with the second. (Words consist of letters,
42 -- digits, and the four characters "?-_~". A sequence of multiple words can
43 -- be replaced if they are listed in the first column, separated by a single
44 -- space character. If multiple words are to be replaced, there must be a
45 -- replacement for each prefix.)
47 -- 4. <out-file> (optional) is the name of the output file. It defaults to
48 -- gnat_ugn_unw.texi or gnat_ugn_vms.texi, depending on the target.
50 -- 5. <warnings> (optional, and allowed only if <out-file> is explicit)
51 -- can be any string. If present, it indicates that warning messages are
52 -- to be output to Standard_Error. If absent, no warning messages are
55 -- The following steps are performed:
59 -- Any occurrences of ^alpha^beta^ are replaced by beta. The sequence
60 -- must fit on a single line, and there can only be one occurrence on a
63 -- Any occurrences of a word in the Ug_Words list are replaced by the
64 -- appropriate vms equivalents. Note that replacements do not occur
65 -- within ^alpha^beta^ sequences.
67 -- Any occurence of [filename].extension, where extension one of the
70 -- "o", "ads", "adb", "ali", "ada", "atb", "ats", "adc", "c"
72 -- replaced by the appropriate VMS names (all upper case with .o
73 -- replaced .OBJ). Note that replacements do not occur within
74 -- ^alpha^beta^ sequences.
78 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
79 -- must fit on a single line.
83 -- The sequence ^^^ is replaced by a single ^. This escape sequence
84 -- must be used if the literal character ^ is to appear in the
85 -- output. A line containing this escape sequence may not also contain
86 -- a ^alpha^beta^ sequence.
88 -- Process @ifset and @ifclear for the target flags (unw, vms);
89 -- this is because we have menu problems if we let makeinfo handle
90 -- these ifset/ifclear pairs.
91 -- Note: @ifset/@ifclear commands for the edition flags (FSFEDITION,
92 -- PROEDITION, GPLEDITION) are passed through unchanged
94 with Ada.Command_Line; use Ada.Command_Line;
95 with Ada.Strings; use Ada.Strings;
96 with Ada.Strings.Fixed; use Ada.Strings.Fixed;
97 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
98 with Ada.Strings.Maps; use Ada.Strings.Maps;
99 with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants;
100 with Ada.Text_IO; use Ada.Text_IO;
102 with GNAT.Spitbol; use GNAT.Spitbol;
103 with GNAT.Spitbol.Table_VString; use GNAT.Spitbol.Table_VString;
105 procedure Xgnatugn is
108 -- Print usage information. Invoked if an invalid command line is
111 Output_File : File_Type;
112 -- The preprocessed output is written to this file
114 type Input_File is record
119 -- Records information on an input file. Name and Line are used
120 -- in error messages, Line is updated automatically by Get_Line.
122 function Get_Line (Input : access Input_File) return String;
123 -- Returns a line from Input and performs the necessary
124 -- line-oriented checks (length, character set, trailing spaces).
126 Number_Of_Warnings : Natural := 0;
127 Number_Of_Errors : Natural := 0;
128 Warnings_Enabled : Boolean;
132 At_Character : Natural;
137 -- Prints a message reporting an error on line Input.Line. If
138 -- At_Character is not 0, indicate the exact character at which
143 At_Character : Natural;
148 -- Like Error, but just print a warning message
150 Dictionary_File : aliased Input_File;
151 procedure Read_Dictionary_File;
152 -- Dictionary_File is opened using the name given on the command
153 -- line. It contains the replacements for the Ug_Words list.
154 -- Read_Dictionary_File reads Dictionary_File and fills the
157 Source_File : aliased Input_File;
158 procedure Process_Source_File;
159 -- Source_File is opened using the name given on the command line.
160 -- It contains the Texinfo source code. Process_Source_File
161 -- performs the necessary replacements.
163 type Flag_Type is (UNW, VMS, FSFEDITION, PROEDITION, GPLEDITION);
164 -- The flags permitted in @ifset or @ifclear commands:
166 -- Targets for preprocessing
167 -- UNW (Unix and Windows) or VMS
169 -- Editions of the manual
170 -- FSFEDITION, PROEDITION, or GPLEDITION
172 -- Conditional commands for target are processed by xgnatugn
174 -- Conditional commands for edition are passed through unchanged
176 subtype Target_Type is Flag_Type range UNW .. VMS;
177 subtype Edition_Type is Flag_Type range FSFEDITION .. GPLEDITION;
179 Target : Target_Type;
180 -- The Target variable is initialized using the command line
182 Valid_Characters : constant Character_Set :=
183 To_Set (Span => (' ', '~'));
184 -- This array controls which characters are permitted in the input
185 -- file (after line breaks have been removed). Valid characters
186 -- are all printable ASCII characters and the space character.
188 Word_Characters : constant Character_Set :=
190 (('0', '9'), ('a', 'z'), ('A', 'Z')))
192 -- The characters which are permitted in words. Other (valid)
193 -- characters are assumed to be delimiters between words. Note that
194 -- this set has to include all characters of the source words of the
195 -- Ug_Words dictionary.
197 Reject_Trailing_Spaces : constant Boolean := True;
198 -- Controls whether Xgnatug rejects superfluous space characters
199 -- at the end of lines.
201 Maximum_Line_Length : constant Positive := 79;
202 Fatal_Line_Length_Limit : constant Positive := 5000;
203 Fatal_Line_Length : exception;
204 -- If Maximum_Line_Length is exceeded in an input file, an error
205 -- message is printed. If Fatal_Line_Length is exceeded,
206 -- execution terminates with a Fatal_Line_Length exception.
208 VMS_Escape_Character : constant Character := '^';
209 -- The character used to mark VMS alternatives (^alpha^beta^)
211 Extensions : GNAT.Spitbol.Table_VString.Table (20);
212 procedure Initialize_Extensions;
213 -- This table records extensions and their replacement for
214 -- rewriting filenames in the VMS version of the manual.
216 function Is_Extension (Extension : String) return Boolean;
217 function Get_Replacement_Extension (Extension : String) return String;
218 -- These functions query the replacement table. Is_Extension
219 -- checks if the given string is a known extension.
220 -- Get_Replacement returns the replacement extension.
222 Ug_Words : GNAT.Spitbol.Table_VString.Table (200);
223 function Is_Known_Word (Word : String) return Boolean;
224 function Get_Replacement_Word (Word : String) return String;
225 -- The Ug_Words table lists replacement words for the VMS version
226 -- of the manual. Is_Known_Word and Get_Replacement_Word query
227 -- this table. The table is filled using Read_Dictionary_File.
229 function Rewrite_Source_Line (Line : String) return String;
230 -- This subprogram takes a line and rewrites it according to Target.
231 -- It relies on information in Source_File to generate error messages.
233 type Conditional is (Set, Clear);
234 procedure Push_Conditional (Cond : Conditional; Flag : Target_Type);
235 procedure Pop_Conditional (Cond : Conditional);
236 -- These subprograms deal with conditional processing (@ifset/@ifclear).
237 -- They rely on information in Source_File to generate error messages.
239 function Currently_Excluding return Boolean;
240 -- Returns true if conditional processing directives imply that the
241 -- current line should not be included in the output.
243 function VMS_Context_Determined return Boolean;
244 -- Returns true if, in the current conditional preprocessing context, we
245 -- always have a VMS or a non-VMS version, regardless of the value of
248 function In_VMS_Section return Boolean;
249 -- Returns True if in an "@ifset vms" section
251 procedure Check_No_Pending_Conditional;
252 -- Checks that all preprocessing directives have been properly matched by
253 -- their @end counterpart. If this is not the case, print an error
256 -- The following definitions implement a stack to track the conditional
257 -- preprocessing context.
259 type Conditional_Context is record
260 Starting_Line : Positive;
266 Conditional_Stack_Depth : constant := 3;
269 array (1 .. Conditional_Stack_Depth) of Conditional_Context;
271 Conditional_TOS : Natural := 0;
272 -- Pointer to the Top Of Stack for Conditional_Stack
280 Put_Line (Standard_Error,
281 "usage: xgnatugn TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
283 Put_Line (Standard_Error, "TARGET is one of:");
285 for T in Target_Type'Range loop
286 Put_Line (Standard_Error, " " & Target_Type'Image (T));
290 Put_Line (Standard_Error, "SOURCE is the source file to process.");
292 Put_Line (Standard_Error, "DICTIONARY is the name of a file "
293 & "that contains word replacements");
294 Put_Line (Standard_Error, "for the VMS version.");
296 Put_Line (Standard_Error,
297 "OUT-FILE, if present, is the output file to be created;");
298 Put_Line (Standard_Error,
299 "If OUT-FILE is absent, the output file is either " &
300 "gnat_ugn_unw.texi, ");
301 Put_Line (Standard_Error,
302 "or gnat_ugn_vms.texi, depending on TARGET.");
304 Put_Line (Standard_Error,
305 "WARNINGS, if present, is any string;");
306 Put_Line (Standard_Error,
307 "it will result in warning messages (e.g., line too long))");
308 Put_Line (Standard_Error,
309 "being output to Standard_Error.");
316 function Get_Line (Input : access Input_File) return String is
317 Line_Buffer : String (1 .. Fatal_Line_Length_Limit);
321 Input.Line := Input.Line + 1;
322 Get_Line (Input.Data, Line_Buffer, Last);
324 if Last = Line_Buffer'Last then
325 Error (Input.all, "line exceeds fatal line length limit");
326 raise Fatal_Line_Length;
330 Line : String renames Line_Buffer (Line_Buffer'First .. Last);
333 for J in Line'Range loop
334 if not Is_In (Line (J), Valid_Characters) then
335 Error (Input.all, J, "invalid character");
340 if Line'Length > Maximum_Line_Length then
341 Warning (Input.all, Maximum_Line_Length + 1, "line too long");
344 if Reject_Trailing_Spaces
345 and then Line'Length > 0
346 and then Line (Line'Last) = ' '
348 Error (Input.all, Line'Last, "trailing space character");
351 return Trim (Line, Right);
364 Error (Input, 0, Message);
369 At_Character : Natural;
372 Line_Image : constant String := Integer'Image (Input.Line);
373 At_Character_Image : constant String := Integer'Image (At_Character);
374 -- These variables are required because we have to drop the leading
378 Number_Of_Errors := Number_Of_Errors + 1;
380 if At_Character > 0 then
381 Put_Line (Standard_Error,
383 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
384 & At_Character_Image (At_Character_Image'First + 1
385 .. At_Character_Image'Last)
389 Put_Line (Standard_Error,
391 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
406 if Warnings_Enabled then
407 Warning (Input, 0, Message);
413 At_Character : Natural;
416 Line_Image : constant String := Integer'Image (Input.Line);
417 At_Character_Image : constant String := Integer'Image (At_Character);
418 -- These variables are required because we have to drop the leading
422 if not Warnings_Enabled then
426 Number_Of_Warnings := Number_Of_Warnings + 1;
428 if At_Character > 0 then
429 Put_Line (Standard_Error,
431 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
432 & At_Character_Image (At_Character_Image'First + 1
433 .. At_Character_Image'Last)
437 Put_Line (Standard_Error,
439 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
445 --------------------------
446 -- Read_Dictionary_File --
447 --------------------------
449 procedure Read_Dictionary_File is
451 while not End_Of_File (Dictionary_File.Data) loop
453 Line : constant String :=
454 Get_Line (Dictionary_File'Access);
455 Split : constant Natural :=
456 Index (Line, (1 => VMS_Escape_Character));
459 if Line'Length = 0 then
460 Error (Dictionary_File, "empty line in dictionary file");
462 elsif Line (Line'First) = ' ' then
463 Error (Dictionary_File, 1, "line starts with space character");
466 Error (Dictionary_File, "line does not contain "
467 & VMS_Escape_Character & " character");
470 Source : constant String :=
471 Trim (Line (1 .. Split - 1), Both);
472 Target : constant String :=
473 Trim (Line (Split + 1 .. Line'Last), Both);
474 Two_Spaces : constant Natural :=
476 Non_Word_Character : constant Natural :=
483 if Two_Spaces /= 0 then
484 Error (Dictionary_File, Two_Spaces,
485 "multiple space characters in source word");
488 if Non_Word_Character /= 0 then
489 Error (Dictionary_File, Non_Word_Character,
490 "illegal character in source word");
493 if Source'Length = 0 then
494 Error (Dictionary_File, "source is empty");
496 elsif Target'Length = 0 then
497 Error (Dictionary_File, "target is empty");
500 Set (Ug_Words, Source, V (Target));
502 -- Ensure that if Source is a sequence of words
503 -- "WORD1 WORD2 ...", we already have a mapping for
506 for J in Source'Range loop
507 if Source (J) = ' ' then
509 Prefix : String renames
510 Source (Source'First .. J - 1);
513 if not Is_Known_Word (Prefix) then
514 Error (Dictionary_File,
516 & "' not known at this point");
526 end Read_Dictionary_File;
528 -------------------------
529 -- Rewrite_Source_Line --
530 -------------------------
532 function Rewrite_Source_Line (Line : String) return String is
534 -- We use a simple lexer to split the line into tokens:
536 -- Word consisting entirely of Word_Characters
537 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
538 -- Space a space character
539 -- Other everything else (sequence of non-word characters)
540 -- VMS_Error incomplete VMS alternative
541 -- End_Of_Line no more characters on this line
543 -- A sequence of three VMS_Escape_Characters is automatically
544 -- collapsed to an Other token.
546 type Token_Span is record
547 First, Last : Positive;
549 -- The character range covered by a token in Line
551 type Token_Kind is (End_Of_Line, Word, Other,
552 VMS_Alternative, VMS_Error);
553 type Token_Record (Kind : Token_Kind := End_Of_Line) is record
558 when VMS_Alternative =>
559 Non_VMS, VMS : Token_Span;
560 when VMS_Error | End_Of_Line =>
565 Input_Position : Positive := Line'First;
566 Token : Token_Record;
567 -- The position of the next character to be processed by Next_Token
569 procedure Next_Token;
570 -- Returns the next token in Line, starting at Input_Position
572 Rewritten_Line : VString;
573 -- Collects the line as it is rewritten
575 procedure Rewrite_Word;
576 -- The current token is assumed to be a Word. When processing the VMS
577 -- version of the manual, additional tokens are gathered to check if
578 -- we have a file name or a sequence of known words.
580 procedure Maybe_Rewrite_Extension;
581 -- The current token is assumed to be Other. When processing the VMS
582 -- version of the manual and the token represents a single dot ".",
583 -- the following word is rewritten according to the rules for
586 VMS_Token_Seen : Boolean := False;
587 -- This is set to true if a VMS_Alternative has been encountered, or a
594 procedure Next_Token is
595 Remaining_Line : String renames Line (Input_Position .. Line'Last);
596 Last_Character : Natural;
599 if Remaining_Line'Length = 0 then
600 Token := (End_Of_Line, Remaining_Line'First);
604 -- ^alpha^beta^, the VMS_Alternative case
606 if Remaining_Line (Remaining_Line'First) = VMS_Escape_Character then
608 VMS_Second_Character, VMS_Third_Character : Natural;
611 if VMS_Token_Seen then
612 Error (Source_File, Remaining_Line'First,
613 "multiple " & VMS_Escape_Character
614 & " characters on a single line");
616 VMS_Token_Seen := True;
619 -- Find the second and third escape character. If one of
620 -- them is not present, generate an error token.
622 VMS_Second_Character :=
623 Index (Remaining_Line (Remaining_Line'First + 1
624 .. Remaining_Line'Last),
625 (1 => VMS_Escape_Character));
627 if VMS_Second_Character = 0 then
628 Input_Position := Remaining_Line'Last + 1;
629 Token := (VMS_Error, Remaining_Line'First);
633 VMS_Third_Character :=
634 Index (Remaining_Line (VMS_Second_Character + 1
635 .. Remaining_Line'Last),
636 (1 => VMS_Escape_Character));
638 if VMS_Third_Character = 0 then
639 Input_Position := Remaining_Line'Last + 1;
640 Token := (VMS_Error, Remaining_Line'First);
644 -- Consume all the characters we are about to include in
647 Input_Position := VMS_Third_Character + 1;
649 -- Check if we are in a ^^^ situation, and return an Other
650 -- token in this case.
652 if Remaining_Line'First + 1 = VMS_Second_Character
653 and then Remaining_Line'First + 2 = VMS_Third_Character
655 Token := (Other, Remaining_Line'First,
656 (Remaining_Line'First, Remaining_Line'First));
660 Token := (VMS_Alternative, Remaining_Line'First,
661 (Remaining_Line'First + 1, VMS_Second_Character - 1),
662 (VMS_Second_Character + 1, VMS_Third_Character - 1));
665 end if; -- VMS_Alternative
667 -- The Word case. Search for characters not in Word_Characters.
668 -- We have found a word if the first non-word character is not
669 -- the first character in Remaining_Line, i.e. if Remaining_Line
670 -- starts with a word character.
672 Last_Character := Index (Remaining_Line, Word_Characters, Outside);
673 if Last_Character /= Remaining_Line'First then
675 -- If we haven't found a character which is not in
676 -- Word_Characters, all remaining characters are part of the
677 -- current Word token.
679 if Last_Character = 0 then
680 Last_Character := Remaining_Line'Last + 1;
683 Input_Position := Last_Character;
684 Token := (Word, Remaining_Line'First,
685 (Remaining_Line'First, Last_Character - 1));
689 -- Remaining characters are in the Other category. To speed
690 -- up processing, we collect them together if there are several
693 Input_Position := Last_Character + 1;
695 Remaining_Line'First,
696 (Remaining_Line'First, Last_Character));
703 procedure Rewrite_Word is
705 renames Line (Token.Span.First .. Token.Span.Last);
708 -- We do not perform any error checking below, so we can just skip
709 -- all processing for the non-VMS version.
711 if Target /= VMS then
712 Append (Rewritten_Line, First_Word);
717 if Is_Known_Word (First_Word) then
719 -- If we have a word from the dictionary, we look for the
720 -- longest possible sequence we can rewrite.
723 Seq : Token_Span := Token.Span;
724 Lost_Space : Boolean := False;
729 if Token.Kind = Other
730 and then Line (Token.Span.First .. Token.Span.Last) = " "
733 if Token.Kind /= Word
734 or else not Is_Known_Word (Line (Seq.First
737 -- When we reach this point, the following
738 -- conditions are true:
740 -- Seq is a known word.
741 -- The previous token was a space character.
742 -- Seq extended to the current token is not a
750 -- Extend Seq to cover the current (known) word
752 Seq.Last := Token.Span.Last;
757 -- When we reach this point, the following conditions
760 -- Seq is a known word.
761 -- The previous token was a word.
762 -- The current token is not a space character.
768 -- Rewrite Seq, and add the lost space if necessary
770 Append (Rewritten_Line,
771 Get_Replacement_Word (Line (Seq.First .. Seq.Last)));
773 Append (Rewritten_Line, ' ');
776 -- The unknown token will be processed during the
777 -- next iteration of the main loop.
784 if Token.Kind = Other
785 and then Line (Token.Span.First .. Token.Span.Last) = "."
787 -- Deal with extensions
791 and then Is_Extension (Line (Token.Span.First
794 -- We have discovered a file extension. Convert the file
795 -- name to upper case.
797 Append (Rewritten_Line,
798 Translate (First_Word, Upper_Case_Map) & '.');
799 Append (Rewritten_Line,
800 Get_Replacement_Extension
801 (Line (Token.Span.First .. Token.Span.Last)));
804 -- We already have: Word ".", followed by an unknown token
806 Append (Rewritten_Line, First_Word & '.');
808 -- The unknown token will be processed during the next
809 -- iteration of the main loop.
813 -- We have an unknown Word, followed by an unknown token.
814 -- The unknown token will be processed by the outer loop.
816 Append (Rewritten_Line, First_Word);
820 -----------------------------
821 -- Maybe_Rewrite_Extension --
822 -----------------------------
824 procedure Maybe_Rewrite_Extension is
826 -- Again, we need no special processing in the non-VMS case
829 and then Line (Token.Span.First .. Token.Span.Last) = "."
831 -- This extension is not preceded by a word, otherwise
832 -- Rewrite_Word would have handled it.
836 and then Is_Extension (Line (Token.Span.First
839 Append (Rewritten_Line, '.' & Get_Replacement_Extension
840 (Line (Token.Span.First .. Token.Span.Last)));
843 Append (Rewritten_Line, '.');
846 Append (Rewritten_Line, Line (Token.Span.First
847 .. Token.Span.Last));
850 end Maybe_Rewrite_Extension;
852 -- Start of processing for Process_Source_Line
855 -- The following parser recognizes the following special token
858 -- Word "." Word rewrite as file name if second word is extension
859 -- Word " " Word rewrite as a single word using Ug_Words table
871 Maybe_Rewrite_Extension;
873 when VMS_Alternative =>
874 if VMS_Context_Determined then
875 if (not In_VMS_Section)
877 Line (Token.VMS.First .. Token.VMS.Last) /=
878 Line (Token.Non_VMS.First .. Token.Non_VMS.Last)
880 Warning (Source_File, Token.First,
881 "VMS alternative already determined "
882 & "by conditionals");
886 Append (Rewritten_Line, Line (Token.VMS.First
889 Append (Rewritten_Line, Line (Token.Non_VMS.First
890 .. Token.Non_VMS.Last));
895 Error (Source_File, Token.First, "invalid VMS alternative");
900 return S (Rewritten_Line);
901 end Rewrite_Source_Line;
903 -------------------------
904 -- Process_Source_File --
905 -------------------------
907 procedure Process_Source_File is
908 Ifset : constant String := "@ifset ";
909 Ifclear : constant String := "@ifclear ";
910 Endsetclear : constant String := "@end ";
911 -- Strings to be recognized for conditional processing
914 while not End_Of_File (Source_File.Data) loop
916 Line : constant String := Get_Line (Source_File'Access);
917 Rewritten : constant String := Rewrite_Source_Line (Line);
918 -- We unconditionally rewrite the line so that we can check the
919 -- syntax of all lines, and not only those which are actually
920 -- included in the output.
922 Have_Conditional : Boolean := False;
923 -- True if we have encountered a conditional preprocessing
927 -- The kind of the directive
933 -- If the line starts with @ifset or @ifclear, we try to convert
934 -- the following flag to one of our flag types. If we fail,
935 -- Have_Conditional remains False.
937 if Line'Length >= Ifset'Length
938 and then Line (1 .. Ifset'Length) = Ifset
943 Arg : constant String :=
944 Trim (Line (Ifset'Length + 1 .. Line'Last), Both);
947 Flag := Flag_Type'Value (Arg);
948 Have_Conditional := True;
952 if Translate (Target_Type'Image (Flag),
956 Error (Source_File, "flag has to be lowercase");
963 when Constraint_Error =>
964 Error (Source_File, "unknown flag for '@ifset'");
967 elsif Line'Length >= Ifclear'Length
968 and then Line (1 .. Ifclear'Length) = Ifclear
973 Arg : constant String :=
974 Trim (Line (Ifclear'Length + 1 .. Line'Last), Both);
977 Flag := Flag_Type'Value (Arg);
978 Have_Conditional := True;
982 if Translate (Target_Type'Image (Flag),
986 Error (Source_File, "flag has to be lowercase");
993 when Constraint_Error =>
994 Error (Source_File, "unknown flag for '@ifclear'");
998 if Have_Conditional and (Flag in Target_Type) then
1000 -- We create a new conditional context and suppress the
1001 -- directive in the output.
1003 Push_Conditional (Cond, Flag);
1005 elsif Line'Length >= Endsetclear'Length
1006 and then Line (1 .. Endsetclear'Length) = Endsetclear
1007 and then (Flag in Target_Type)
1009 -- The '@end ifset'/'@end ifclear' case is handled here. We
1010 -- have to pop the conditional context.
1013 First, Last : Natural;
1016 Find_Token (Source => Line (Endsetclear'Length + 1
1024 Error (Source_File, "'@end' without argument");
1026 if Line (First .. Last) = "ifset" then
1027 Have_Conditional := True;
1029 elsif Line (First .. Last) = "ifclear" then
1030 Have_Conditional := True;
1034 if Have_Conditional then
1035 Pop_Conditional (Cond);
1038 -- We fall through to the ordinary case for other @end
1041 end if; -- @end without argument
1043 end if; -- Have_Conditional
1045 if (not Have_Conditional) or (Flag in Edition_Type) then
1047 -- The ordinary case
1049 if not Currently_Excluding then
1050 Put_Line (Output_File, Rewritten);
1056 Check_No_Pending_Conditional;
1057 end Process_Source_File;
1059 ---------------------------
1060 -- Initialize_Extensions --
1061 ---------------------------
1063 procedure Initialize_Extensions is
1065 procedure Add (Extension : String);
1066 -- Adds an extension which is replaced with itself (in upper
1069 procedure Add (Extension, Replacement : String);
1070 -- Adds an extension with a custom replacement
1076 procedure Add (Extension : String) is
1078 Add (Extension, Translate (Extension, Upper_Case_Map));
1081 procedure Add (Extension, Replacement : String) is
1083 Set (Extensions, Extension, V (Replacement));
1086 -- Start of processing for Initialize_Extensions
1089 -- To avoid performance degradation, increase the constant in the
1090 -- definition of Extensions above if you add more extensions here.
1101 end Initialize_Extensions;
1107 function Is_Extension (Extension : String) return Boolean is
1109 return Present (Extensions, Extension);
1112 -------------------------------
1113 -- Get_Replacement_Extension --
1114 -------------------------------
1116 function Get_Replacement_Extension (Extension : String) return String is
1118 return S (Get (Extensions, Extension));
1119 end Get_Replacement_Extension;
1125 function Is_Known_Word (Word : String) return Boolean is
1127 return Present (Ug_Words, Word);
1130 --------------------------
1131 -- Get_Replacement_Word --
1132 --------------------------
1134 function Get_Replacement_Word (Word : String) return String is
1136 return S (Get (Ug_Words, Word));
1137 end Get_Replacement_Word;
1139 ----------------------
1140 -- Push_Conditional --
1141 ----------------------
1143 procedure Push_Conditional (Cond : Conditional; Flag : Target_Type) is
1144 Will_Exclude : Boolean;
1147 -- If we are already in an excluding context, inherit this property,
1148 -- otherwise calculate it from scratch.
1150 if Conditional_TOS > 0
1151 and then Conditional_Stack (Conditional_TOS).Excluding
1153 Will_Exclude := True;
1157 Will_Exclude := Flag /= Target;
1159 Will_Exclude := Flag = Target;
1163 -- Check if the current directive is pointless because of a previous,
1164 -- enclosing directive.
1166 for J in 1 .. Conditional_TOS loop
1167 if Conditional_Stack (J).Flag = Flag then
1168 Warning (Source_File, "directive without effect because of line"
1169 & Integer'Image (Conditional_Stack (J).Starting_Line));
1173 Conditional_TOS := Conditional_TOS + 1;
1174 Conditional_Stack (Conditional_TOS) :=
1175 (Starting_Line => Source_File.Line,
1178 Excluding => Will_Exclude);
1179 end Push_Conditional;
1181 ---------------------
1182 -- Pop_Conditional --
1183 ---------------------
1185 procedure Pop_Conditional (Cond : Conditional) is
1187 if Conditional_TOS > 0 then
1190 if Conditional_Stack (Conditional_TOS).Cond /= Set then
1192 "'@end ifset' does not match '@ifclear' at line"
1193 & Integer'Image (Conditional_Stack
1194 (Conditional_TOS).Starting_Line));
1198 if Conditional_Stack (Conditional_TOS).Cond /= Clear then
1200 "'@end ifclear' does not match '@ifset' at line"
1201 & Integer'Image (Conditional_Stack
1202 (Conditional_TOS).Starting_Line));
1206 Conditional_TOS := Conditional_TOS - 1;
1212 "'@end ifset' without corresponding '@ifset'");
1216 "'@end ifclear' without corresponding '@ifclear'");
1219 end Pop_Conditional;
1221 -------------------------
1222 -- Currently_Excluding --
1223 -------------------------
1225 function Currently_Excluding return Boolean is
1227 return Conditional_TOS > 0
1228 and then Conditional_Stack (Conditional_TOS).Excluding;
1229 end Currently_Excluding;
1231 ----------------------------
1232 -- VMS_Context_Determined --
1233 ----------------------------
1235 function VMS_Context_Determined return Boolean is
1237 for J in 1 .. Conditional_TOS loop
1238 if Conditional_Stack (J).Flag = VMS then
1244 end VMS_Context_Determined;
1246 --------------------
1247 -- In_VMS_Section --
1248 --------------------
1250 function In_VMS_Section return Boolean is
1252 for J in 1 .. Conditional_TOS loop
1253 if Conditional_Stack (J).Flag = VMS then
1254 return Conditional_Stack (J).Cond = Set;
1261 ----------------------------------
1262 -- Check_No_Pending_Conditional --
1263 ----------------------------------
1265 procedure Check_No_Pending_Conditional is
1267 for J in 1 .. Conditional_TOS loop
1268 case Conditional_Stack (J).Cond is
1270 Error (Source_File, "Missing '@end ifset' for '@ifset' at line"
1271 & Integer'Image (Conditional_Stack (J).Starting_Line));
1275 "Missing '@end ifclear' for '@ifclear' at line"
1276 & Integer'Image (Conditional_Stack (J).Starting_Line));
1279 end Check_No_Pending_Conditional;
1281 -- Start of processing for Xgnatugn
1283 Valid_Command_Line : Boolean;
1284 Output_File_Name : VString;
1287 Initialize_Extensions;
1288 Valid_Command_Line := Argument_Count in 3 .. 5;
1290 -- First argument: Target
1292 if Valid_Command_Line then
1294 Target := Flag_Type'Value (Argument (1));
1296 if not Target'Valid then
1297 Valid_Command_Line := False;
1301 when Constraint_Error =>
1302 Valid_Command_Line := False;
1306 -- Second argument: Source_File
1308 if Valid_Command_Line then
1310 Source_File.Name := V (Argument (2));
1311 Open (Source_File.Data, In_File, Argument (2));
1315 Valid_Command_Line := False;
1319 -- Third argument: Dictionary_File
1321 if Valid_Command_Line then
1323 Dictionary_File.Name := V (Argument (3));
1324 Open (Dictionary_File.Data, In_File, Argument (3));
1328 Valid_Command_Line := False;
1332 -- Fourth argument: Output_File
1334 if Valid_Command_Line then
1335 if Argument_Count in 4 .. 5 then
1336 Output_File_Name := V (Argument (4));
1340 Output_File_Name := V ("gnat_ugn_unw.texi");
1342 Output_File_Name := V ("gnat_ugn_vms.texi");
1346 Warnings_Enabled := Argument_Count = 5;
1349 Create (Output_File, Out_File, S (Output_File_Name));
1352 when Name_Error | Use_Error =>
1353 Valid_Command_Line := False;
1357 if not Valid_Command_Line then
1359 Set_Exit_Status (Failure);
1362 Read_Dictionary_File;
1363 Close (Dictionary_File.Data);
1365 -- Main processing starts here
1367 Process_Source_File;
1368 Close (Output_File);
1369 Close (Source_File.Data);
1371 New_Line (Standard_Error);
1373 if Number_Of_Warnings = 0 then
1374 Put_Line (Standard_Error, " NO Warnings");
1377 Put (Standard_Error, Integer'Image (Number_Of_Warnings));
1378 Put (Standard_Error, " Warning");
1380 if Number_Of_Warnings > 1 then
1381 Put (Standard_Error, "s");
1384 New_Line (Standard_Error);
1387 if Number_Of_Errors = 0 then
1388 Put_Line (Standard_Error, " NO Errors");
1391 Put (Standard_Error, Integer'Image (Number_Of_Errors));
1392 Put (Standard_Error, " Error");
1394 if Number_Of_Errors > 1 then
1395 Put (Standard_Error, "s");
1398 New_Line (Standard_Error);
1401 if Number_Of_Errors /= 0 then
1402 Set_Exit_Status (Failure);
1404 Set_Exit_Status (Success);