1 ------------------------------------------------------------------------------
3 -- GNAT SYSTEM UTILITIES --
9 -- Copyright (C) 2003-2004 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 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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 ------------------------------------------------------------------------------
24 -- This utility is used to process the source of gnat_ugn.texi to make a
25 -- version suitable for running through standard Texinfo processor. It is
26 -- invoked as follows:
28 -- xgnatugn <target> <in-file> <word-list> [ <out-file> [ <warnings> ] ]
30 -- 1. <target> is the target type of the manual, which is one of:
32 -- unw Unix and Windows platforms
35 -- 2. <in-file> is the file name of the Texinfo file to be
38 -- 3. <word-list> is the name of the word list file. This file is used for
39 -- rewriting the VMS edition. Each line contains a word mapping: The source
40 -- word in the first column, the target word in the second column. The
41 -- columns are separated by a '^' character. When preprocessing for VMS, the
42 -- first word is replaced with the second. (Words consist of letters,
43 -- digits, and the four characters "?-_~". A sequence of multiple words can
44 -- be replaced if they are listed in the first column, separated by a single
45 -- space character. If multiple words are to be replaced, there must be a
46 -- replacement for each prefix.)
48 -- 4. <out-file> (optional) is the name of the output file. It defaults to
49 -- gnat_ugn_unw.texi or gnat_ugn_vms.texi, depending on the target.
51 -- 5. <warnings> (optional, and allowed only if <out-file> is explicit)
52 -- can be any string. If present, it indicates that warning messages are
53 -- to be output to Standard_Error. If absent, no warning messages are
56 -- The following steps are performed:
60 -- Any occurrences of ^alpha^beta^ are replaced by beta. The sequence
61 -- must fit on a single line, and there can only be one occurrence on a
64 -- Any occurrences of a word in the Ug_Words list are replaced by the
65 -- appropriate vms equivalents. Note that replacements do not occur
66 -- within ^alpha^beta^ sequences.
68 -- Any occurence of [filename].extension, where extension one of the
71 -- "o", "ads", "adb", "ali", "ada", "atb", "ats", "adc", "c"
73 -- replaced by the appropriate VMS names (all upper case with .o
74 -- replaced .OBJ). Note that replacements do not occur within
75 -- ^alpha^beta^ sequences.
79 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
80 -- must fit on a single line.
84 -- The sequence ^^^ is replaced by a single ^. This escape sequence
85 -- must be used if the literal character ^ is to appear in the
86 -- output. A line containing this escape sequence may not also contain
87 -- a ^alpha^beta^ sequence.
89 -- Process @ifset and @ifclear for the target flags (unw, vms);
90 -- this is because we have menu problems if we let makeinfo handle
91 -- these ifset/ifclear pairs.
92 -- Note: @ifset/@ifclear commands for the edition flags (FSFEDITION,
93 -- PROEDITION, ACADEMICEDITION) are passed through unchanged
95 with Ada
.Command_Line
; use Ada
.Command_Line
;
96 with Ada
.Strings
; use Ada
.Strings
;
97 with Ada
.Strings
.Fixed
; use Ada
.Strings
.Fixed
;
98 with Ada
.Strings
.Unbounded
; use Ada
.Strings
.Unbounded
;
99 with Ada
.Strings
.Maps
; use Ada
.Strings
.Maps
;
100 with Ada
.Strings
.Maps
.Constants
; use Ada
.Strings
.Maps
.Constants
;
101 with Ada
.Text_IO
; use Ada
.Text_IO
;
103 with GNAT
.Spitbol
; use GNAT
.Spitbol
;
104 with GNAT
.Spitbol
.Table_VString
; use GNAT
.Spitbol
.Table_VString
;
106 procedure Xgnatugn
is
109 -- Print usage information. Invoked if an invalid command line is
112 Output_File
: File_Type
;
113 -- The preprocessed output is written to this file
115 type Input_File
is record
120 -- Records information on an input file. Name and Line are used
121 -- in error messages, Line is updated automatically by Get_Line.
123 function Get_Line
(Input
: access Input_File
) return String;
124 -- Returns a line from Input and performs the necessary
125 -- line-oriented checks (length, character set, trailing spaces).
127 Number_Of_Warnings
: Natural := 0;
128 Number_Of_Errors
: Natural := 0;
129 Warnings_Enabled
: Boolean;
133 At_Character
: Natural;
138 -- Prints a message reporting an error on line Input.Line. If
139 -- At_Character is not 0, indicate the exact character at which
144 At_Character
: Natural;
149 -- Like Error, but just print a warning message
151 Dictionary_File
: aliased Input_File
;
152 procedure Read_Dictionary_File
;
153 -- Dictionary_File is opened using the name given on the command
154 -- line. It contains the replacements for the Ug_Words list.
155 -- Read_Dictionary_File reads Dictionary_File and fills the
158 Source_File
: aliased Input_File
;
159 procedure Process_Source_File
;
160 -- Source_File is opened using the name given on the command line.
161 -- It contains the Texinfo source code. Process_Source_File
162 -- performs the necessary replacements.
164 type Flag_Type
is (UNW
, VMS
, FSFEDITION
, PROEDITION
, ACADEMICEDITION
);
165 -- The flags permitted in @ifset or @ifclear commands:
167 -- Targets for preprocessing
168 -- UNW (Unix and Windows) or VMS
170 -- Editions of the manual
171 -- FSFEDITION, PROEDITION, or ACADEMICEDITION
173 -- Conditional commands for target are processed by xgnatugn
175 -- Conditional commands for edition are passed through unchanged
177 subtype Target_Type
is Flag_Type
range UNW
.. VMS
;
178 subtype Edition_Type
is Flag_Type
range FSFEDITION
.. ACADEMICEDITION
;
180 Target
: Target_Type
;
181 -- The Target variable is initialized using the command line
183 Valid_Characters
: constant Character_Set
184 := To_Set
(Span
=> (' ', '~'));
185 -- This array controls which characters are permitted in the input
186 -- file (after line breaks have been removed). Valid characters
187 -- are all printable ASCII characters and the space character.
189 Word_Characters
: constant Character_Set
:=
191 (('0', '9'), ('a', 'z'), ('A', 'Z')))
193 -- The characters which are permitted in words. Other (valid)
194 -- characters are assumed to be delimiters between words. Note that
195 -- this set has to include all characters of the source words of the
196 -- Ug_Words dictionary.
198 Reject_Trailing_Spaces
: constant Boolean := True;
199 -- Controls whether Xgnatug rejects superfluous space characters
200 -- at the end of lines.
202 Maximum_Line_Length
: constant Positive := 79;
203 Fatal_Line_Length_Limit
: constant Positive := 5000;
204 Fatal_Line_Length
: exception;
205 -- If Maximum_Line_Length is exceeded in an input file, an error
206 -- message is printed. If Fatal_Line_Length is exceeded,
207 -- execution terminates with a Fatal_Line_Length exception.
209 VMS_Escape_Character
: constant Character := '^';
210 -- The character used to mark VMS alternatives (^alpha^beta^)
212 Extensions
: GNAT
.Spitbol
.Table_VString
.Table
(20);
213 procedure Initialize_Extensions
;
214 -- This table records extensions and their replacement for
215 -- rewriting filenames in the VMS version of the manual.
217 function Is_Extension
(Extension
: String) return Boolean;
218 function Get_Replacement_Extension
(Extension
: String) return String;
219 -- These functions query the replacement table. Is_Extension
220 -- checks if the given string is a known extension.
221 -- Get_Replacement returns the replacement extension.
223 Ug_Words
: GNAT
.Spitbol
.Table_VString
.Table
(200);
224 function Is_Known_Word
(Word
: String) return Boolean;
225 function Get_Replacement_Word
(Word
: String) return String;
226 -- The Ug_Words table lists replacement words for the VMS version
227 -- of the manual. Is_Known_Word and Get_Replacement_Word query
228 -- this table. The table is filled using Read_Dictionary_File.
230 function Rewrite_Source_Line
(Line
: String) return String;
231 -- This subprogram takes a line and rewrites it according to Target.
232 -- It relies on information in Source_File to generate error messages.
234 type Conditional
is (Set
, Clear
);
235 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
);
236 procedure Pop_Conditional
(Cond
: Conditional
);
237 -- These subprograms deal with conditional processing (@ifset/@ifclear).
238 -- They rely on information in Source_File to generate error messages.
240 function Currently_Excluding
return Boolean;
241 -- Returns true if conditional processing directives imply that the
242 -- current line should not be included in the output.
244 function VMS_Context_Determined
return Boolean;
245 -- Returns true if, in the current conditional preprocessing context, we
246 -- always have a VMS or a non-VMS version, regardless of the value of
249 function In_VMS_Section
return Boolean;
250 -- Returns True if in an "@ifset vms" section
252 procedure Check_No_Pending_Conditional
;
253 -- Checks that all preprocessing directives have been properly matched by
254 -- their @end counterpart. If this is not the case, print an error
257 -- The following definitions implement a stack to track the conditional
258 -- preprocessing context.
260 type Conditional_Context
is record
261 Starting_Line
: Positive;
267 Conditional_Stack_Depth
: constant := 3;
270 array (1 .. Conditional_Stack_Depth
) of Conditional_Context
;
272 Conditional_TOS
: Natural := 0;
273 -- Pointer to the Top Of Stack for Conditional_Stack
281 Put_Line
(Standard_Error
,
282 "usage: xgnatugn TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
284 Put_Line
(Standard_Error
, "TARGET is one of:");
286 for T
in Target_Type
'Range loop
287 Put_Line
(Standard_Error
, " " & Target_Type
'Image (T
));
291 Put_Line
(Standard_Error
, "SOURCE is the source file to process.");
293 Put_Line
(Standard_Error
, "DICTIONARY is the name of a file "
294 & "that contains word replacements");
295 Put_Line
(Standard_Error
, "for the VMS version.");
297 Put_Line
(Standard_Error
,
298 "OUT-FILE, if present, is the output file to be created;");
299 Put_Line
(Standard_Error
,
300 "If OUT-FILE is absent, the output file is either " &
301 "gnat_ugn_unw.texi, ");
302 Put_Line
(Standard_Error
,
303 "or gnat_ugn_vms.texi, depending on TARGET.");
305 Put_Line
(Standard_Error
,
306 "WARNINGS, if present, is any string;");
307 Put_Line
(Standard_Error
,
308 "it will result in warning messages (e.g., line too long))");
309 Put_Line
(Standard_Error
,
310 "being output to Standard_Error.");
317 function Get_Line
(Input
: access Input_File
) return String is
318 Line_Buffer
: String (1 .. Fatal_Line_Length_Limit
);
322 Input
.Line
:= Input
.Line
+ 1;
323 Get_Line
(Input
.Data
, Line_Buffer
, Last
);
325 if Last
= Line_Buffer
'Last then
326 Error
(Input
.all, "line exceeds fatal line length limit");
327 raise Fatal_Line_Length
;
331 Line
: String renames Line_Buffer
(Line_Buffer
'First .. Last
);
334 for J
in Line
'Range loop
335 if not Is_In
(Line
(J
), Valid_Characters
) then
336 Error
(Input
.all, J
, "invalid character");
341 if Line
'Length > Maximum_Line_Length
then
342 Warning
(Input
.all, Maximum_Line_Length
+ 1, "line too long");
345 if Reject_Trailing_Spaces
346 and then Line
'Length > 0
347 and then Line
(Line
'Last) = ' '
349 Error
(Input
.all, Line
'Last, "trailing space character");
352 return Trim
(Line
, Right
);
365 Error
(Input
, 0, Message
);
370 At_Character
: Natural;
373 Line_Image
: constant String := Integer'Image (Input
.Line
);
374 At_Character_Image
: constant String := Integer'Image (At_Character
);
375 -- These variables are required because we have to drop the leading
379 Number_Of_Errors
:= Number_Of_Errors
+ 1;
381 if At_Character
> 0 then
382 Put_Line
(Standard_Error
,
384 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
385 & At_Character_Image
(At_Character_Image
'First + 1
386 .. At_Character_Image
'Last)
390 Put_Line
(Standard_Error
,
392 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
407 if Warnings_Enabled
then
408 Warning
(Input
, 0, Message
);
414 At_Character
: Natural;
417 Line_Image
: constant String := Integer'Image (Input
.Line
);
418 At_Character_Image
: constant String := Integer'Image (At_Character
);
419 -- These variables are required because we have to drop the leading
423 if not Warnings_Enabled
then
427 Number_Of_Warnings
:= Number_Of_Warnings
+ 1;
429 if At_Character
> 0 then
430 Put_Line
(Standard_Error
,
432 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
433 & At_Character_Image
(At_Character_Image
'First + 1
434 .. At_Character_Image
'Last)
438 Put_Line
(Standard_Error
,
440 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
446 --------------------------
447 -- Read_Dictionary_File --
448 --------------------------
450 procedure Read_Dictionary_File
is
452 while not End_Of_File
(Dictionary_File
.Data
) loop
454 Line
: constant String :=
455 Get_Line
(Dictionary_File
'Access);
456 Split
: constant Natural :=
457 Index
(Line
, (1 => VMS_Escape_Character
));
460 if Line
'Length = 0 then
461 Error
(Dictionary_File
, "empty line in dictionary file");
463 elsif Line
(Line
'First) = ' ' then
464 Error
(Dictionary_File
, 1, "line starts with space character");
467 Error
(Dictionary_File
, "line does not contain "
468 & VMS_Escape_Character
& " character");
471 Source
: constant String :=
472 Trim
(Line
(1 .. Split
- 1), Both
);
473 Target
: constant String :=
474 Trim
(Line
(Split
+ 1 .. Line
'Last), Both
);
475 Two_Spaces
: constant Natural :=
477 Non_Word_Character
: constant Natural :=
484 if Two_Spaces
/= 0 then
485 Error
(Dictionary_File
, Two_Spaces
,
486 "multiple space characters in source word");
489 if Non_Word_Character
/= 0 then
490 Error
(Dictionary_File
, Non_Word_Character
,
491 "illegal character in source word");
494 if Source
'Length = 0 then
495 Error
(Dictionary_File
, "source is empty");
497 elsif Target
'Length = 0 then
498 Error
(Dictionary_File
, "target is empty");
501 Set
(Ug_Words
, Source
, V
(Target
));
503 -- Ensure that if Source is a sequence of words
504 -- "WORD1 WORD2 ...", we already have a mapping for
507 for J
in Source
'Range loop
508 if Source
(J
) = ' ' then
510 Prefix
: String renames
511 Source
(Source
'First .. J
- 1);
514 if not Is_Known_Word
(Prefix
) then
515 Error
(Dictionary_File
,
517 & "' not known at this point");
527 end Read_Dictionary_File
;
529 -------------------------
530 -- Rewrite_Source_Line --
531 -------------------------
533 function Rewrite_Source_Line
(Line
: String) return String is
535 -- We use a simple lexer to split the line into tokens:
537 -- Word consisting entirely of Word_Characters
538 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
539 -- Space a space character
540 -- Other everything else (sequence of non-word characters)
541 -- VMS_Error incomplete VMS alternative
542 -- End_Of_Line no more characters on this line
544 -- A sequence of three VMS_Escape_Characters is automatically
545 -- collapsed to an Other token.
547 type Token_Span
is record
548 First
, Last
: Positive;
550 -- The character range covered by a token in Line
552 type Token_Kind
is (End_Of_Line
, Word
, Other
,
553 VMS_Alternative
, VMS_Error
);
554 type Token_Record
(Kind
: Token_Kind
:= End_Of_Line
) is record
559 when VMS_Alternative
=>
560 Non_VMS
, VMS
: Token_Span
;
561 when VMS_Error | End_Of_Line
=>
566 Input_Position
: Positive := Line
'First;
567 Token
: Token_Record
;
568 -- The position of the next character to be processed by Next_Token
570 procedure Next_Token
;
571 -- Returns the next token in Line, starting at Input_Position
573 Rewritten_Line
: VString
;
574 -- Collects the line as it is rewritten
576 procedure Rewrite_Word
;
577 -- The current token is assumed to be a Word. When processing the VMS
578 -- version of the manual, additional tokens are gathered to check if
579 -- we have a file name or a sequence of known words.
581 procedure Maybe_Rewrite_Extension
;
582 -- The current token is assumed to be Other. When processing the VMS
583 -- version of the manual and the token represents a single dot ".",
584 -- the following word is rewritten according to the rules for
587 VMS_Token_Seen
: Boolean := False;
588 -- This is set to true if a VMS_Alternative has been encountered, or a
595 procedure Next_Token
is
596 Remaining_Line
: String renames Line
(Input_Position
.. Line
'Last);
597 Last_Character
: Natural;
600 if Remaining_Line
'Length = 0 then
601 Token
:= (End_Of_Line
, Remaining_Line
'First);
605 -- ^alpha^beta^, the VMS_Alternative case
607 if Remaining_Line
(Remaining_Line
'First) = VMS_Escape_Character
then
609 VMS_Second_Character
, VMS_Third_Character
: Natural;
612 if VMS_Token_Seen
then
613 Error
(Source_File
, Remaining_Line
'First,
614 "multiple " & VMS_Escape_Character
615 & " characters on a single line");
617 VMS_Token_Seen
:= True;
620 -- Find the second and third escape character. If one of
621 -- them is not present, generate an error token.
623 VMS_Second_Character
:=
624 Index
(Remaining_Line
(Remaining_Line
'First + 1
625 .. Remaining_Line
'Last),
626 (1 => VMS_Escape_Character
));
628 if VMS_Second_Character
= 0 then
629 Input_Position
:= Remaining_Line
'Last + 1;
630 Token
:= (VMS_Error
, Remaining_Line
'First);
634 VMS_Third_Character
:=
635 Index
(Remaining_Line
(VMS_Second_Character
+ 1
636 .. Remaining_Line
'Last),
637 (1 => VMS_Escape_Character
));
639 if VMS_Third_Character
= 0 then
640 Input_Position
:= Remaining_Line
'Last + 1;
641 Token
:= (VMS_Error
, Remaining_Line
'First);
645 -- Consume all the characters we are about to include in
648 Input_Position
:= VMS_Third_Character
+ 1;
650 -- Check if we are in a ^^^ situation, and return an Other
651 -- token in this case.
653 if Remaining_Line
'First + 1 = VMS_Second_Character
654 and then Remaining_Line
'First + 2 = VMS_Third_Character
656 Token
:= (Other
, Remaining_Line
'First,
657 (Remaining_Line
'First, Remaining_Line
'First));
661 Token
:= (VMS_Alternative
, Remaining_Line
'First,
662 (Remaining_Line
'First + 1, VMS_Second_Character
- 1),
663 (VMS_Second_Character
+ 1, VMS_Third_Character
- 1));
666 end if; -- VMS_Alternative
668 -- The Word case. Search for characters not in Word_Characters.
669 -- We have found a word if the first non-word character is not
670 -- the first character in Remaining_Line, i.e. if Remaining_Line
671 -- starts with a word character.
673 Last_Character
:= Index
(Remaining_Line
, Word_Characters
, Outside
);
674 if Last_Character
/= Remaining_Line
'First then
676 -- If we haven't found a character which is not in
677 -- Word_Characters, all remaining characters are part of the
678 -- current Word token.
680 if Last_Character
= 0 then
681 Last_Character
:= Remaining_Line
'Last + 1;
684 Input_Position
:= Last_Character
;
685 Token
:= (Word
, Remaining_Line
'First,
686 (Remaining_Line
'First, Last_Character
- 1));
690 -- Remaining characters are in the Other category. To speed
691 -- up processing, we collect them together if there are several
694 Input_Position
:= Last_Character
+ 1;
696 Remaining_Line
'First,
697 (Remaining_Line
'First, Last_Character
));
704 procedure Rewrite_Word
is
706 renames Line
(Token
.Span
.First
.. Token
.Span
.Last
);
709 -- We do not perform any error checking below, so we can just skip
710 -- all processing for the non-VMS version.
712 if Target
/= VMS
then
713 Append
(Rewritten_Line
, First_Word
);
718 if Is_Known_Word
(First_Word
) then
720 -- If we have a word from the dictionary, we look for the
721 -- longest possible sequence we can rewrite.
724 Seq
: Token_Span
:= Token
.Span
;
725 Lost_Space
: Boolean := False;
730 if Token
.Kind
= Other
731 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = " "
734 if Token
.Kind
/= Word
735 or else not Is_Known_Word
(Line
(Seq
.First
738 -- When we reach this point, the following
739 -- conditions are true:
741 -- Seq is a known word.
742 -- The previous token was a space character.
743 -- Seq extended to the current token is not a
751 -- Extend Seq to cover the current (known) word.
753 Seq
.Last
:= Token
.Span
.Last
;
758 -- When we reach this point, the following conditions
761 -- Seq is a known word.
762 -- The previous token was a word.
763 -- The current token is not a space character.
769 -- Rewrite Seq, and add the lost space if necessary
771 Append
(Rewritten_Line
,
772 Get_Replacement_Word
(Line
(Seq
.First
.. Seq
.Last
)));
774 Append
(Rewritten_Line
, ' ');
777 -- The unknown token will be processed during the
778 -- next iteration of the main loop.
785 if Token
.Kind
= Other
786 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
788 -- Deal with extensions
792 and then Is_Extension
(Line
(Token
.Span
.First
795 -- We have discovered a file extension. Convert the file
796 -- name to upper case.
798 Append
(Rewritten_Line
,
799 Translate
(First_Word
, Upper_Case_Map
) & '.');
800 Append
(Rewritten_Line
,
801 Get_Replacement_Extension
802 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
805 -- We already have: Word ".", followed by an unknown token
807 Append
(Rewritten_Line
, First_Word
& '.');
809 -- The unknown token will be processed during the next
810 -- iteration of the main loop.
814 -- We have an unknown Word, followed by an unknown token.
815 -- The unknown token will be processed by the outer loop.
817 Append
(Rewritten_Line
, First_Word
);
821 -----------------------------
822 -- Maybe_Rewrite_Extension --
823 -----------------------------
825 procedure Maybe_Rewrite_Extension
is
827 -- Again, we need no special processing in the non-VMS case
830 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
832 -- This extension is not preceded by a word, otherwise
833 -- Rewrite_Word would have handled it.
837 and then Is_Extension
(Line
(Token
.Span
.First
840 Append
(Rewritten_Line
, '.' & Get_Replacement_Extension
841 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
844 Append
(Rewritten_Line
, '.');
847 Append
(Rewritten_Line
, Line
(Token
.Span
.First
848 .. Token
.Span
.Last
));
851 end Maybe_Rewrite_Extension
;
853 -- Start of processing for Process_Source_Line
856 -- The following parser recognizes the following special token
859 -- Word "." Word rewrite as file name if second word is extension
860 -- Word " " Word rewrite as a single word using Ug_Words table
872 Maybe_Rewrite_Extension
;
874 when VMS_Alternative
=>
875 if VMS_Context_Determined
then
876 if (not In_VMS_Section
)
878 Line
(Token
.VMS
.First
.. Token
.VMS
.Last
) /=
879 Line
(Token
.Non_VMS
.First
.. Token
.Non_VMS
.Last
)
881 Warning
(Source_File
, Token
.First
,
882 "VMS alternative already determined "
883 & "by conditionals");
887 Append
(Rewritten_Line
, Line
(Token
.VMS
.First
890 Append
(Rewritten_Line
, Line
(Token
.Non_VMS
.First
891 .. Token
.Non_VMS
.Last
));
896 Error
(Source_File
, Token
.First
, "invalid VMS alternative");
901 return S
(Rewritten_Line
);
902 end Rewrite_Source_Line
;
904 -------------------------
905 -- Process_Source_File --
906 -------------------------
908 procedure Process_Source_File
is
909 Ifset
: constant String := "@ifset ";
910 Ifclear
: constant String := "@ifclear ";
911 Endsetclear
: constant String := "@end ";
912 -- Strings to be recognized for conditional processing
915 while not End_Of_File
(Source_File
.Data
) loop
917 Line
: constant String := Get_Line
(Source_File
'Access);
918 Rewritten
: constant String := Rewrite_Source_Line
(Line
);
919 -- We unconditionally rewrite the line so that we can check the
920 -- syntax of all lines, and not only those which are actually
921 -- included in the output.
923 Have_Conditional
: Boolean := False;
924 -- True if we have encountered a conditional preprocessing
928 -- The kind of the directive
934 -- If the line starts with @ifset or @ifclear, we try to convert
935 -- the following flag to one of our flag types. If we fail,
936 -- Have_Conditional remains False.
938 if Line
'Length >= Ifset
'Length
939 and then Line
(1 .. Ifset
'Length) = Ifset
944 Arg
: constant String :=
945 Trim
(Line
(Ifset
'Length + 1 .. Line
'Last), Both
);
948 Flag
:= Flag_Type
'Value (Arg
);
949 Have_Conditional
:= True;
953 if Translate
(Target_Type
'Image (Flag
),
957 Error
(Source_File
, "flag has to be lowercase");
964 when Constraint_Error
=>
965 Error
(Source_File
, "unknown flag for '@ifset'");
968 elsif Line
'Length >= Ifclear
'Length
969 and then Line
(1 .. Ifclear
'Length) = Ifclear
974 Arg
: constant String :=
975 Trim
(Line
(Ifclear
'Length + 1 .. Line
'Last), Both
);
978 Flag
:= Flag_Type
'Value (Arg
);
979 Have_Conditional
:= True;
983 if Translate
(Target_Type
'Image (Flag
),
987 Error
(Source_File
, "flag has to be lowercase");
994 when Constraint_Error
=>
995 Error
(Source_File
, "unknown flag for '@ifclear'");
999 if Have_Conditional
and (Flag
in Target_Type
) then
1001 -- We create a new conditional context and suppress the
1002 -- directive in the output.
1004 Push_Conditional
(Cond
, Flag
);
1006 elsif Line
'Length >= Endsetclear
'Length
1007 and then Line
(1 .. Endsetclear
'Length) = Endsetclear
1008 and then (Flag
in Target_Type
)
1010 -- The '@end ifset'/'@end ifclear' case is handled here. We
1011 -- have to pop the conditional context.
1014 First
, Last
: Natural;
1017 Find_Token
(Source
=> Line
(Endsetclear
'Length + 1
1025 Error
(Source_File
, "'@end' without argument");
1027 if Line
(First
.. Last
) = "ifset" then
1028 Have_Conditional
:= True;
1030 elsif Line
(First
.. Last
) = "ifclear" then
1031 Have_Conditional
:= True;
1035 if Have_Conditional
then
1036 Pop_Conditional
(Cond
);
1039 -- We fall through to the ordinary case for other @end
1042 end if; -- @end without argument
1044 end if; -- Have_Conditional
1046 if (not Have_Conditional
) or (Flag
in Edition_Type
) then
1048 -- The ordinary case.
1050 if not Currently_Excluding
then
1051 Put_Line
(Output_File
, Rewritten
);
1057 Check_No_Pending_Conditional
;
1058 end Process_Source_File
;
1060 ---------------------------
1061 -- Initialize_Extensions --
1062 ---------------------------
1064 procedure Initialize_Extensions
is
1066 procedure Add
(Extension
: String);
1067 -- Adds an extension which is replaced with itself (in upper
1070 procedure Add
(Extension
, Replacement
: String);
1071 -- Adds an extension with a custom replacement.
1077 procedure Add
(Extension
: String) is
1079 Add
(Extension
, Translate
(Extension
, Upper_Case_Map
));
1082 procedure Add
(Extension
, Replacement
: String) is
1084 Set
(Extensions
, Extension
, V
(Replacement
));
1087 -- Start of processing for Initialize_Extensions
1090 -- To avoid performance degradation, increase the constant in the
1091 -- definition of Extensions above if you add more extensions here.
1102 end Initialize_Extensions
;
1108 function Is_Extension
(Extension
: String) return Boolean is
1110 return Present
(Extensions
, Extension
);
1113 -------------------------------
1114 -- Get_Replacement_Extension --
1115 -------------------------------
1117 function Get_Replacement_Extension
(Extension
: String) return String is
1119 return S
(Get
(Extensions
, Extension
));
1120 end Get_Replacement_Extension
;
1126 function Is_Known_Word
(Word
: String) return Boolean is
1128 return Present
(Ug_Words
, Word
);
1131 --------------------------
1132 -- Get_Replacement_Word --
1133 --------------------------
1135 function Get_Replacement_Word
(Word
: String) return String is
1137 return S
(Get
(Ug_Words
, Word
));
1138 end Get_Replacement_Word
;
1140 ----------------------
1141 -- Push_Conditional --
1142 ----------------------
1144 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
) is
1145 Will_Exclude
: Boolean;
1148 -- If we are already in an excluding context, inherit this property,
1149 -- otherwise calculate it from scratch.
1151 if Conditional_TOS
> 0
1152 and then Conditional_Stack
(Conditional_TOS
).Excluding
1154 Will_Exclude
:= True;
1158 Will_Exclude
:= Flag
/= Target
;
1160 Will_Exclude
:= Flag
= Target
;
1164 -- Check if the current directive is pointless because of a previous,
1165 -- enclosing directive.
1167 for J
in 1 .. Conditional_TOS
loop
1168 if Conditional_Stack
(J
).Flag
= Flag
then
1169 Warning
(Source_File
, "directive without effect because of line"
1170 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1174 Conditional_TOS
:= Conditional_TOS
+ 1;
1175 Conditional_Stack
(Conditional_TOS
) :=
1176 (Starting_Line
=> Source_File
.Line
,
1179 Excluding
=> Will_Exclude
);
1180 end Push_Conditional
;
1182 ---------------------
1183 -- Pop_Conditional --
1184 ---------------------
1186 procedure Pop_Conditional
(Cond
: Conditional
) is
1188 if Conditional_TOS
> 0 then
1191 if Conditional_Stack
(Conditional_TOS
).Cond
/= Set
then
1193 "'@end ifset' does not match '@ifclear' at line"
1194 & Integer'Image (Conditional_Stack
1195 (Conditional_TOS
).Starting_Line
));
1199 if Conditional_Stack
(Conditional_TOS
).Cond
/= Clear
then
1201 "'@end ifclear' does not match '@ifset' at line"
1202 & Integer'Image (Conditional_Stack
1203 (Conditional_TOS
).Starting_Line
));
1207 Conditional_TOS
:= Conditional_TOS
- 1;
1213 "'@end ifset' without corresponding '@ifset'");
1217 "'@end ifclear' without corresponding '@ifclear'");
1220 end Pop_Conditional
;
1222 -------------------------
1223 -- Currently_Excluding --
1224 -------------------------
1226 function Currently_Excluding
return Boolean is
1228 return Conditional_TOS
> 0
1229 and then Conditional_Stack
(Conditional_TOS
).Excluding
;
1230 end Currently_Excluding
;
1232 ----------------------------
1233 -- VMS_Context_Determined --
1234 ----------------------------
1236 function VMS_Context_Determined
return Boolean is
1238 for J
in 1 .. Conditional_TOS
loop
1239 if Conditional_Stack
(J
).Flag
= VMS
then
1245 end VMS_Context_Determined
;
1247 --------------------
1248 -- In_VMS_Section --
1249 --------------------
1251 function In_VMS_Section
return Boolean is
1253 for J
in 1 .. Conditional_TOS
loop
1254 if Conditional_Stack
(J
).Flag
= VMS
then
1255 return Conditional_Stack
(J
).Cond
= Set
;
1262 ----------------------------------
1263 -- Check_No_Pending_Conditional --
1264 ----------------------------------
1266 procedure Check_No_Pending_Conditional
is
1268 for J
in 1 .. Conditional_TOS
loop
1269 case Conditional_Stack
(J
).Cond
is
1271 Error
(Source_File
, "Missing '@end ifset' for '@ifset' at line"
1272 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1276 "Missing '@end ifclear' for '@ifclear' at line"
1277 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1280 end Check_No_Pending_Conditional
;
1282 -- Start of processing for Xgnatugn
1284 Valid_Command_Line
: Boolean;
1285 Output_File_Name
: VString
;
1288 Initialize_Extensions
;
1289 Valid_Command_Line
:= Argument_Count
in 3 .. 5;
1291 -- First argument: Target
1293 if Valid_Command_Line
then
1295 Target
:= Flag_Type
'Value (Argument
(1));
1297 if Target
not in Target_Type
then
1298 Valid_Command_Line
:= False;
1302 when Constraint_Error
=>
1303 Valid_Command_Line
:= False;
1307 -- Second argument: Source_File
1309 if Valid_Command_Line
then
1311 Source_File
.Name
:= V
(Argument
(2));
1312 Open
(Source_File
.Data
, In_File
, Argument
(2));
1316 Valid_Command_Line
:= False;
1320 -- Third argument: Dictionary_File
1322 if Valid_Command_Line
then
1324 Dictionary_File
.Name
:= V
(Argument
(3));
1325 Open
(Dictionary_File
.Data
, In_File
, Argument
(3));
1329 Valid_Command_Line
:= False;
1333 -- Fourth argument: Output_File
1335 if Valid_Command_Line
then
1336 if Argument_Count
in 4 .. 5 then
1337 Output_File_Name
:= V
(Argument
(4));
1341 Output_File_Name
:= V
("gnat_ugn_unw.texi");
1343 Output_File_Name
:= V
("gnat_ugn_vms.texi");
1347 Warnings_Enabled
:= Argument_Count
= 5;
1350 Create
(Output_File
, Out_File
, S
(Output_File_Name
));
1353 when Name_Error | Use_Error
=>
1354 Valid_Command_Line
:= False;
1358 if not Valid_Command_Line
then
1360 Set_Exit_Status
(Failure
);
1363 Read_Dictionary_File
;
1364 Close
(Dictionary_File
.Data
);
1366 -- Main processing starts here
1368 Process_Source_File
;
1369 Close
(Output_File
);
1370 Close
(Source_File
.Data
);
1372 New_Line
(Standard_Error
);
1374 if Number_Of_Warnings
= 0 then
1375 Put_Line
(Standard_Error
, " NO Warnings");
1378 Put
(Standard_Error
, Integer'Image (Number_Of_Warnings
));
1379 Put
(Standard_Error
, " Warning");
1381 if Number_Of_Warnings
> 1 then
1382 Put
(Standard_Error
, "s");
1385 New_Line
(Standard_Error
);
1388 if Number_Of_Errors
= 0 then
1389 Put_Line
(Standard_Error
, " NO Errors");
1392 Put
(Standard_Error
, Integer'Image (Number_Of_Errors
));
1393 Put
(Standard_Error
, " Error");
1395 if Number_Of_Errors
> 1 then
1396 Put
(Standard_Error
, "s");
1399 New_Line
(Standard_Error
);
1402 if Number_Of_Errors
/= 0 then
1403 Set_Exit_Status
(Failure
);
1405 Set_Exit_Status
(Success
);