1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2005 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, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
30 with Osint
; use Osint
;
32 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
33 with Ada
.Command_Line
; use Ada
.Command_Line
;
34 with Ada
.Text_IO
; use Ada
.Text_IO
;
36 package body VMS_Conv
is
38 Keep_Temps_Option
: constant Item_Ptr
:=
42 new String'("/KEEP_TEMPORARY_FILES"),
47 Param_Count
: Natural := 0;
48 -- Number of parameter arguments so far
53 Arg_File
: Ada
.Text_IO
.File_Type
;
54 -- A file where arguments are read from
57 -- Pointer to head of list of command items, one for each command, with
58 -- the end of the list marked by a null pointer.
60 Last_Command
: Item_Ptr
;
61 -- Pointer to last item in Commands list
64 -- Pointer to command item for current command
66 Make_Commands_Active
: Item_Ptr
:= null;
67 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
68 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
71 Output_File_Expected
: Boolean := False;
72 -- True for GNAT LINK after -o switch, so that the ".ali" extension is
73 -- not added to the executable file name.
75 package Buffer
is new Table
.Table
76 (Table_Component_Type
=> Character,
77 Table_Index_Type
=> Integer,
79 Table_Initial
=> 4096,
81 Table_Name
=> "Buffer");
83 function Init_Object_Dirs
return Argument_List
;
84 -- Get the list of the object directories
86 function Invert_Sense
(S
: String) return VMS_Data
.String_Ptr
;
87 -- Given a unix switch string S, computes the inverse (adding or
88 -- removing ! characters as required), and returns a pointer to
89 -- the allocated result on the heap.
91 function Is_Extensionless
(F
: String) return Boolean;
92 -- Returns true if the filename has no extension
94 function Match
(S1
, S2
: String) return Boolean;
95 -- Determines whether S1 and S2 match (this is a case insensitive match)
97 function Match_Prefix
(S1
, S2
: String) return Boolean;
98 -- Determines whether S1 matches a prefix of S2. This is also a case
99 -- insensitive match (for example Match ("AB","abc") is True).
101 function Matching_Name
104 Quiet
: Boolean := False) return Item_Ptr
;
105 -- Determines if the item list headed by Itm and threaded through the
106 -- Next fields (with null marking the end of the list), contains an
107 -- entry that uniquely matches the given string. The match is case
108 -- insensitive and permits unique abbreviation. If the match succeeds,
109 -- then a pointer to the matching item is returned. Otherwise, an
110 -- appropriate error message is written. Note that the discriminant
111 -- of Itm is used to determine the appropriate form of this message.
112 -- Quiet is normally False as shown, if it is set to True, then no
113 -- error message is generated in a not found situation (null is still
114 -- returned to indicate the not-found situation).
116 function OK_Alphanumerplus
(S
: String) return Boolean;
117 -- Checks that S is a string of alphanumeric characters,
118 -- returning True if all alphanumeric characters,
119 -- False if empty or a non-alphanumeric character is present.
121 function OK_Integer
(S
: String) return Boolean;
122 -- Checks that S is a string of digits, returning True if all digits,
123 -- False if empty or a non-digit is present.
125 procedure Place
(C
: Character);
126 -- Place a single character in the buffer, updating Ptr
128 procedure Place
(S
: String);
129 -- Place a string character in the buffer, updating Ptr
131 procedure Place_Lower
(S
: String);
132 -- Place string in buffer, forcing letters to lower case, updating Ptr
134 procedure Place_Unix_Switches
(S
: VMS_Data
.String_Ptr
);
135 -- Given a unix switch string, place corresponding switches in Buffer,
136 -- updating Ptr appropriatelly. Note that in the case of use of ! the
137 -- result may be to remove a previously placed switch.
139 procedure Preprocess_Command_Data
;
140 -- Preprocess the string form of the command and options list into the
143 procedure Process_Argument
(The_Command
: in out Command_Type
);
144 -- Process one argument from the command line, or one line from
145 -- from a command line file. For the first call, set The_Command.
147 procedure Validate_Command_Or_Option
(N
: VMS_Data
.String_Ptr
);
148 -- Check that N is a valid command or option name, i.e. that it is of the
149 -- form of an Ada identifier with upper case letters and underscores.
151 procedure Validate_Unix_Switch
(S
: VMS_Data
.String_Ptr
);
152 -- Check that S is a valid switch string as described in the syntax for
153 -- the switch table item UNIX_SWITCH or else begins with a backquote.
155 ----------------------
156 -- Init_Object_Dirs --
157 ----------------------
159 function Init_Object_Dirs
return Argument_List
is
160 Object_Dirs
: Integer;
161 Object_Dir
: Argument_List
(1 .. 256);
162 Object_Dir_Name
: String_Access
;
166 Object_Dir_Name
:= new String'(Object_Dir_Default_Prefix);
167 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
171 Dir : constant String_Access :=
172 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
174 exit when Dir = null;
175 Object_Dirs := Object_Dirs + 1;
176 Object_Dir (Object_Dirs) :=
178 To_Canonical_Dir_Spec
180 (Normalize_Directory_Name
(Dir
.all).all,
181 True).all, True).all);
185 Object_Dirs
:= Object_Dirs
+ 1;
186 Object_Dir
(Object_Dirs
) := new String'("-lgnat");
188 if Hostparm.OpenVMS then
189 Object_Dirs := Object_Dirs + 1;
190 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
193 return Object_Dir
(1 .. Object_Dirs
);
194 end Init_Object_Dirs
;
200 procedure Initialize
is
204 (Cname
=> new S
'("BIND"),
205 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
207 Unixcmd
=> new S
'("gnatbind"),
209 Switches => Bind_Switches'Access,
210 Params => new Parameter_Array'(1 => Unlimited_Files
),
214 (Cname
=> new S
'("CHOP"),
215 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
217 Unixcmd
=> new S
'("gnatchop"),
219 Switches => Chop_Switches'Access,
220 Params => new Parameter_Array'(1 => File
, 2 => Optional_File
),
224 (Cname
=> new S
'("CLEAN"),
225 Usage => new S'("GNAT CLEAN /qualifiers files"),
227 Unixcmd
=> new S
'("gnatclean"),
229 Switches => Clean_Switches'Access,
230 Params => new Parameter_Array'(1 => File
),
234 (Cname
=> new S
'("COMPILE"),
235 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
237 Unixcmd
=> new S
'("gnatmake"),
238 Unixsws => new Argument_List'(1 => new String'("-f"),
239 2 => new String'("-u"),
240 3 => new String'("-c")),
241 Switches => GCC_Switches'Access,
242 Params => new Parameter_Array'(1 => Files_Or_Wildcard
),
246 (Cname
=> new S
'("ELIM"),
247 Usage => new S'("GNAT ELIM name /qualifiers"),
249 Unixcmd
=> new S
'("gnatelim"),
251 Switches => Elim_Switches'Access,
252 Params => new Parameter_Array'(1 => Other_As_Is
),
256 (Cname
=> new S
'("FIND"),
257 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
258 & "[:column]]] filespec[,...] /qualifiers"),
260 Unixcmd
=> new S
'("gnatfind"),
262 Switches => Find_Switches'Access,
263 Params => new Parameter_Array'(1 => Other_As_Is
,
264 2 => Files_Or_Wildcard
),
268 (Cname
=> new S
'("KRUNCH"),
269 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
271 Unixcmd
=> new S
'("gnatkr"),
273 Switches => Krunch_Switches'Access,
274 Params => new Parameter_Array'(1 => File
),
278 (Cname
=> new S
'("LINK"),
279 Usage => new S'("GNAT LINK file[.ali]"
280 & " [extra obj_&_lib_&_exe_&_opt files]"
283 Unixcmd
=> new S
'("gnatlink"),
285 Switches => Link_Switches'Access,
286 Params => new Parameter_Array'(1 => Unlimited_Files
),
290 (Cname
=> new S
'("LIST"),
291 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
293 Unixcmd
=> new S
'("gnatls"),
295 Switches => List_Switches'Access,
296 Params => new Parameter_Array'(1 => Unlimited_Files
),
300 (Cname
=> new S
'("MAKE"),
301 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
302 & "COMPILE /qualifiers)"),
304 Unixcmd
=> new S
'("gnatmake"),
306 Switches => Make_Switches'Access,
307 Params => new Parameter_Array'(1 => Unlimited_Files
),
311 (Cname
=> new S
'("METRIC"),
312 Usage => new S'("GNAT METRIC /qualifiers source_file"),
314 Unixcmd
=> new S
'("gnatmetric"),
316 Switches => Metric_Switches'Access,
317 Params => new Parameter_Array'(1 => Unlimited_Files
),
321 (Cname
=> new S
'("NAME"),
322 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
323 & "[naming-patterns]"),
325 Unixcmd
=> new S
'("gnatname"),
327 Switches => Name_Switches'Access,
328 Params => new Parameter_Array'(1 => Unlimited_As_Is
),
332 (Cname
=> new S
'("PREPROCESS"),
334 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
336 Unixcmd
=> new S
'("gnatprep"),
338 Switches => Prep_Switches'Access,
339 Params => new Parameter_Array'(1 .. 3 => File
),
343 (Cname
=> new S
'("PRETTY"),
344 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
346 Unixcmd
=> new S
'("gnatpp"),
348 Switches => Pretty_Switches'Access,
349 Params => new Parameter_Array'(1 => Unlimited_Files
),
353 (Cname
=> new S
'("SETUP"),
354 Usage => new S'("GNAT SETUP /qualifiers"),
356 Unixcmd
=> new S
'(""),
358 Switches => Setup_Switches'Access,
359 Params => new Parameter_Array'(1 => Unlimited_Files
),
363 (Cname
=> new S
'("SHARED"),
364 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
365 & "files] /qualifiers"),
367 Unixcmd
=> new S
'("gcc"),
369 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
370 Switches => Shared_Switches'Access,
371 Params => new Parameter_Array'(1 => Unlimited_Files
),
375 (Cname
=> new S
'("STUB"),
376 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
378 Unixcmd
=> new S
'("gnatstub"),
380 Switches => Stub_Switches'Access,
381 Params => new Parameter_Array'(1 => File
, 2 => Optional_File
),
385 (Cname
=> new S
'("XREF"),
386 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
388 Unixcmd
=> new S
'("gnatxref"),
390 Switches => Xref_Switches'Access,
391 Params => new Parameter_Array'(1 => Files_Or_Wildcard
),
400 function Invert_Sense
(S
: String) return VMS_Data
.String_Ptr
is
401 Sinv
: String (1 .. S
'Length * 2);
402 -- Result (for sure long enough)
404 Sinvp
: Natural := 0;
405 -- Pointer to output string
408 for Sp
in S
'Range loop
409 if Sp
= S
'First or else S
(Sp
- 1) = ',' then
413 Sinv
(Sinvp
+ 1) := '!';
414 Sinv
(Sinvp
+ 2) := S
(Sp
);
419 Sinv
(Sinvp
+ 1) := S
(Sp
);
424 return new String'(Sinv (1 .. Sinvp));
427 ----------------------
428 -- Is_Extensionless --
429 ----------------------
431 function Is_Extensionless (F : String) return Boolean is
433 for J in reverse F'Range loop
436 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
442 end Is_Extensionless;
448 function Match (S1, S2 : String) return Boolean is
449 Dif : constant Integer := S2'First - S1'First;
453 if S1'Length /= S2'Length then
457 for J in S1'Range loop
458 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
471 function Match_Prefix (S1, S2 : String) return Boolean is
473 if S1'Length > S2'Length then
476 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
484 function Matching_Name
487 Quiet : Boolean := False) return Item_Ptr
492 -- Little procedure to output command/qualifier/option as appropriate
493 -- and bump error count.
505 Errors := Errors + 1;
510 Put (Standard_Error, "command");
513 if Hostparm.OpenVMS then
514 Put (Standard_Error, "qualifier");
516 Put (Standard_Error, "switch");
520 Put (Standard_Error, "option");
524 Put (Standard_Error, "input");
528 Put (Standard_Error, ": ");
529 Put (Standard_Error, S);
532 -- Start of processing for Matching_Name
535 -- If exact match, that's the one we want
538 while P1 /= null loop
539 if Match (S, P1.Name.all) then
546 -- Now check for prefix matches
549 while P1 /= null loop
550 if P1.Name.all = "/<other>" then
553 elsif not Match_Prefix (S, P1.Name.all) then
557 -- Here we have found one matching prefix, so see if there is
558 -- another one (which is an ambiguity)
561 while P2 /= null loop
562 if Match_Prefix (S, P2.Name.all) then
564 Put (Standard_Error, "ambiguous ");
566 Put (Standard_Error, " (matches ");
567 Put (Standard_Error, P1.Name.all);
569 while P2 /= null loop
570 if Match_Prefix (S, P2.Name.all) then
571 Put (Standard_Error, ',');
572 Put (Standard_Error, P2.Name.all);
578 Put_Line (Standard_Error, ")");
587 -- If we fall through that loop, then there was only one match
593 -- If we fall through outer loop, there was no match
596 Put (Standard_Error, "unrecognized ");
598 New_Line (Standard_Error);
604 -----------------------
605 -- OK_Alphanumerplus --
606 -----------------------
608 function OK_Alphanumerplus (S : String) return Boolean is
614 for J in S'Range loop
615 if not (Is_Alphanumeric (S (J)) or else
616 S (J) = '_
' or else S (J) = '$
')
624 end OK_Alphanumerplus;
630 function OK_Integer (S : String) return Boolean is
636 for J in S'Range loop
637 if not Is_Digit (S (J)) then
650 procedure Output_Version is
653 Put_Line (Gnatvsn.Gnat_Version_String);
654 Put_Line ("Copyright 1996-2005 Free Software Foundation, Inc.");
661 procedure Place (C : Character) is
663 Buffer.Increment_Last;
664 Buffer.Table (Buffer.Last) := C;
667 procedure Place (S : String) is
669 for J in S'Range loop
678 procedure Place_Lower (S : String) is
680 for J in S'Range loop
681 Place (To_Lower (S (J)));
685 -------------------------
686 -- Place_Unix_Switches --
687 -------------------------
689 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
690 P1, P2, P3 : Natural;
692 Slen, Sln2 : Natural;
693 Wild_Card : Boolean := False;
697 while P1 <= S'Last loop
706 pragma Assert (S (P1) = '-' or else S (P1) = '`
');
708 while P2 < S'Last and then S (P2 + 1) /= ',' loop
712 -- Switch is now in S (P1 .. P2)
717 Wild_Card := S (P2) = '*';
725 while P3 <= Buffer.Last - Slen loop
726 if Buffer.Table (P3) = ' '
727 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
731 P3 + Slen = Buffer.Last
733 Buffer.Table (P3 + Slen + 1) = ' ')
738 while P3 + Sln2 /= Buffer.Last
739 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
745 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
746 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
747 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
759 pragma Assert (S (P2) /= '*');
766 Place (S (P1 .. P2));
771 end Place_Unix_Switches;
773 -----------------------------
774 -- Preprocess_Command_Data --
775 -----------------------------
777 procedure Preprocess_Command_Data is
779 for C in Real_Command_Type loop
781 Command : constant Item_Ptr := new Command_Item;
783 Last_Switch : Item_Ptr;
784 -- Last switch in list
787 -- Link new command item into list of commands
789 if Last_Command = null then
792 Last_Command.Next := Command;
795 Last_Command := Command;
797 -- Fill in fields of new command item
799 Command.Name := Command_List (C).Cname;
800 Command.Usage := Command_List (C).Usage;
801 Command.Command := C;
803 if Command_List (C).Unixsws = null then
804 Command.Unix_String := Command_List (C).Unixcmd;
807 Cmd : String (1 .. 5_000);
809 Sws : constant Argument_List_Access :=
810 Command_List (C).Unixsws;
813 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
814 Command_List (C).Unixcmd.all;
815 Last := Command_List (C).Unixcmd'Length;
817 for J in Sws'Range loop
820 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
822 Last := Last + Sws (J)'Length;
825 Command.Unix_String := new String'(Cmd
(1 .. Last
));
829 Command
.Params
:= Command_List
(C
).Params
;
830 Command
.Defext
:= Command_List
(C
).Defext
;
832 Validate_Command_Or_Option
(Command
.Name
);
834 -- Process the switch list
836 for S
in Command_List
(C
).Switches
'Range loop
838 SS
: constant VMS_Data
.String_Ptr
:=
839 Command_List
(C
).Switches
(S
);
840 P
: Natural := SS
'First;
841 Sw
: Item_Ptr
:= new Switch_Item
;
844 -- Pointer to last option
847 -- Link new switch item into list of switches
849 if Last_Switch
= null then
850 Command
.Switches
:= Sw
;
852 Last_Switch
.Next
:= Sw
;
857 -- Process switch string, first get name
859 while SS
(P
) /= ' ' and SS
(P
) /= '=' loop
863 Sw
.Name
:= new String'(SS (SS'First .. P - 1));
865 -- Direct translation case
868 Sw.Translation := T_Direct;
869 Sw.Unix_String := new String'(SS
(P
+ 1 .. SS
'Last));
870 Validate_Unix_Switch
(Sw
.Unix_String
);
872 if SS
(P
- 1) = '>' then
873 Sw
.Translation
:= T_Other
;
875 elsif SS
(P
+ 1) = '`' then
878 -- Create the inverted case (/NO ..)
880 elsif SS
(SS
'First + 1 .. SS
'First + 2) /= "NO" then
881 Sw
:= new Switch_Item
;
882 Last_Switch
.Next
:= Sw
;
886 new String'("/NO" & SS (SS'First + 1 .. P - 1));
887 Sw.Translation := T_Direct;
888 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
889 Validate_Unix_Switch (Sw.Unix_String);
892 -- Directories translation case
894 elsif SS (P + 1) = '*' then
895 pragma Assert (SS (SS'Last) = '*');
896 Sw.Translation := T_Directories;
897 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
898 Validate_Unix_Switch
(Sw
.Unix_String
);
900 -- Directory translation case
902 elsif SS
(P
+ 1) = '%' then
903 pragma Assert
(SS
(SS
'Last) = '%');
904 Sw
.Translation
:= T_Directory
;
905 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
906 Validate_Unix_Switch (Sw.Unix_String);
908 -- File translation case
910 elsif SS (P + 1) = '@
' then
911 pragma Assert (SS (SS'Last) = '@
');
912 Sw.Translation := T_File;
913 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
914 Validate_Unix_Switch
(Sw
.Unix_String
);
916 -- No space file translation case
918 elsif SS
(P
+ 1) = '<' then
919 pragma Assert
(SS
(SS
'Last) = '>');
920 Sw
.Translation
:= T_No_Space_File
;
921 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
922 Validate_Unix_Switch (Sw.Unix_String);
924 -- Numeric translation case
926 elsif SS (P + 1) = '#
' then
927 pragma Assert (SS (SS'Last) = '#
');
928 Sw.Translation := T_Numeric;
929 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
930 Validate_Unix_Switch
(Sw
.Unix_String
);
932 -- Alphanumerplus translation case
934 elsif SS
(P
+ 1) = '|' then
935 pragma Assert
(SS
(SS
'Last) = '|');
936 Sw
.Translation
:= T_Alphanumplus
;
937 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
938 Validate_Unix_Switch (Sw.Unix_String);
940 -- String translation case
942 elsif SS (P + 1) = '"' then
943 pragma Assert (SS (SS'Last) = '"');
944 Sw.Translation := T_String;
945 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
946 Validate_Unix_Switch
(Sw
.Unix_String
);
948 -- Commands translation case
950 elsif SS
(P
+ 1) = '?' then
951 Sw
.Translation
:= T_Commands
;
952 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last));
954 -- Options translation case
957 Sw.Translation := T_Options;
958 Sw.Unix_String := new String'("");
960 P
:= P
+ 1; -- bump past =
961 while P
<= SS
'Last loop
963 Opt
: constant Item_Ptr
:= new Option_Item
;
967 -- Link new option item into options list
969 if Last_Opt
= null then
972 Last_Opt
.Next
:= Opt
;
977 -- Fill in fields of new option item
980 while SS
(Q
) /= ' ' loop
984 Opt
.Name
:= new String'(SS (P .. Q - 1));
985 Validate_Command_Or_Option (Opt.Name);
990 while Q <= SS'Last and then SS (Q) /= ' ' loop
994 Opt.Unix_String := new String'(SS
(P
.. Q
- 1));
995 Validate_Unix_Switch
(Opt
.Unix_String
);
1004 end Preprocess_Command_Data
;
1006 ----------------------
1007 -- Process_Argument --
1008 ----------------------
1010 procedure Process_Argument
(The_Command
: in out Command_Type
) is
1011 Argv
: String_Access
;
1014 function Get_Arg_End
1016 Arg_Idx
: Integer) return Integer;
1017 -- Begins looking at Arg_Idx + 1 and returns the index of the
1018 -- last character before a slash or else the index of the last
1019 -- character in the string Argv.
1025 function Get_Arg_End
1027 Arg_Idx
: Integer) return Integer
1030 for J
in Arg_Idx
+ 1 .. Argv
'Last loop
1031 if Argv
(J
) = '/' then
1039 -- Start of processing for Process_Argument
1042 -- If an argument file is open, read the next non empty line
1044 if Is_Open
(Arg_File
) then
1046 Line
: String (1 .. 256);
1050 Get_Line
(Arg_File
, Line
, Last
);
1051 exit when Last
/= 0 or else End_Of_File
(Arg_File
);
1054 -- If the end of the argument file has been reached, close it
1056 if End_Of_File
(Arg_File
) then
1059 -- If the last line was empty, return after increasing Arg_Num
1060 -- to go to the next argument on the comment line.
1063 Arg_Num
:= Arg_Num
+ 1;
1068 Argv
:= new String'(Line (1 .. Last));
1071 if Argv (1) = '@
' then
1072 Put_Line (Standard_Error, "argument file cannot contain @cmd");
1078 -- No argument file is open, get the argument on the command line
1080 Argv := new String'(Argument
(Arg_Num
));
1081 Arg_Idx
:= Argv
'First;
1083 -- Check if this is the specification of an argument file
1085 if Argv
(Arg_Idx
) = '@' then
1086 -- The first argument on the command line cannot be an argument
1092 "Cannot specify argument line before command");
1096 -- Open the file, after conversion of the name to canonical form.
1097 -- Fail if file is not found.
1100 Canonical_File_Name
: String_Access
:=
1101 To_Canonical_File_Spec
(Argv
(Arg_Idx
+ 1 .. Argv
'Last));
1103 Open
(Arg_File
, In_File
, Canonical_File_Name
.all);
1104 Free
(Canonical_File_Name
);
1109 Put
(Standard_Error
, "Cannot open argument file """);
1110 Put
(Standard_Error
, Argv
(Arg_Idx
+ 1 .. Argv
'Last));
1111 Put_Line
(Standard_Error
, """");
1117 <<Tryagain_After_Coalesce
>>
1120 Next_Arg_Idx
: Integer;
1121 Arg
: String_Access
;
1124 Next_Arg_Idx
:= Get_Arg_End
(Argv
.all, Arg_Idx
);
1125 Arg
:= new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1127 -- The first one must be a command name
1129 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1130 Command := Matching_Name (Arg.all, Commands);
1132 if Command = null then
1136 The_Command := Command.Command;
1137 Output_File_Expected := False;
1139 -- Give usage information if only command given
1141 if Argument_Count = 1
1142 and then Next_Arg_Idx = Argv'Last
1147 ("List of available qualifiers and options");
1150 Put (Command.Usage.all);
1152 Put_Line (Command.Unix_String.all);
1155 Sw : Item_Ptr := Command.Switches;
1158 while Sw /= null loop
1162 case Sw.Translation is
1166 Put_Line (Sw.Unix_String.all &
1171 Put_Line (Sw.Unix_String.all);
1173 when T_Directories =>
1174 Put ("=(direc,direc,..direc)");
1176 Put (Sw.Unix_String.all);
1178 Put (Sw.Unix_String.all);
1179 Put_Line (" direc ...");
1184 Put (Sw.Unix_String.all);
1186 if Sw.Unix_String (Sw.Unix_String'Last)
1192 Put_Line ("directory ");
1194 when T_File | T_No_Space_File =>
1197 Put (Sw.Unix_String.all);
1199 if Sw.Translation = T_File
1200 and then Sw.Unix_String
1201 (Sw.Unix_String'Last) /= '='
1213 (Sw.Unix_String'First) = '`
'
1216 (Sw.Unix_String'First + 1
1217 .. Sw.Unix_String'Last));
1219 Put (Sw.Unix_String.all);
1224 when T_Alphanumplus =>
1229 (Sw.Unix_String'First) = '`
'
1232 (Sw.Unix_String'First + 1
1233 .. Sw.Unix_String'Last));
1235 Put (Sw.Unix_String.all);
1247 Put (Sw.Unix_String.all);
1250 (Sw.Unix_String'Last) /= '='
1259 Put (" (switches for ");
1261 (Sw.Unix_String'First + 7
1262 .. Sw.Unix_String'Last));
1266 (Sw.Unix_String'First
1267 .. Sw.Unix_String'First + 5));
1268 Put_Line (" switches");
1272 Opt : Item_Ptr := Sw.Options;
1275 Put_Line ("=(option,option..)");
1277 while Opt /= null loop
1281 if Opt = Sw.Options then
1286 Put_Line (Opt.Unix_String.all);
1300 -- Special handling for internal debugging switch /?
1302 elsif Arg.all = "/?" then
1303 Display_Command := True;
1304 Output_File_Expected := False;
1306 -- Special handling of internal option /KEEP_TEMPORARY_FILES
1308 elsif Arg'Length >= 7
1309 and then Matching_Name
1310 (Arg.all, Keep_Temps_Option, True) /= null
1312 Opt.Keep_Temporary_Files := True;
1314 -- Copy -switch unchanged
1316 elsif Arg (Arg'First) = '-' then
1320 -- Set Output_File_Expected for the next argument
1322 Output_File_Expected :=
1323 Arg.all = "-o" and then The_Command = Link;
1325 -- Copy quoted switch with quotes stripped
1327 elsif Arg (Arg'First) = '"' then
1328 if Arg (Arg'Last) /= '"' then
1329 Put (Standard_Error, "misquoted argument: ");
1330 Put_Line (Standard_Error, Arg.all);
1331 Errors := Errors + 1;
1335 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1338 Output_File_Expected := False;
1340 -- Parameter Argument
1342 elsif Arg (Arg'First) /= '/'
1343 and then Make_Commands_Active = null
1345 Param_Count := Param_Count + 1;
1347 if Param_Count <= Command.Params'Length then
1349 case Command.Params (Param_Count) is
1351 when File | Optional_File =>
1353 Normal_File : constant String_Access :=
1354 To_Canonical_File_Spec
1359 Place_Lower (Normal_File.all);
1361 if Is_Extensionless (Normal_File.all)
1362 and then Command.Defext /= " "
1365 Place (Command.Defext);
1369 when Unlimited_Files =>
1371 Normal_File : constant String_Access :=
1372 To_Canonical_File_Spec
1375 File_Is_Wild : Boolean := False;
1376 File_List : String_Access_List_Access;
1379 for J in Arg'Range loop
1381 or else Arg (J) = '%'
1383 File_Is_Wild := True;
1387 if File_Is_Wild then
1388 File_List := To_Canonical_File_List
1391 for J in File_List.all'Range loop
1393 Place_Lower (File_List.all (J).all);
1398 Place_Lower (Normal_File.all);
1400 -- Add extension if not present, except after
1403 if Is_Extensionless (Normal_File.all)
1404 and then Command.Defext /= " "
1405 and then not Output_File_Expected
1408 Place (Command.Defext);
1412 Param_Count := Param_Count - 1;
1419 when Unlimited_As_Is =>
1422 Param_Count := Param_Count - 1;
1424 when Files_Or_Wildcard =>
1426 -- Remove spaces from a comma separated list
1427 -- of file names and adjust control variables
1430 while Arg_Num < Argument_Count and then
1431 (Argv (Argv'Last) = ',' xor
1432 Argument (Arg_Num + 1)
1433 (Argument (Arg_Num + 1)'First) = ',')
1436 (Argv
.all & Argument
(Arg_Num
+ 1));
1437 Arg_Num
:= Arg_Num
+ 1;
1438 Arg_Idx
:= Argv
'First;
1440 Get_Arg_End
(Argv
.all, Arg_Idx
);
1442 (Argv (Arg_Idx .. Next_Arg_Idx));
1445 -- Parse the comma separated list of VMS
1446 -- filenames and place them on the command
1447 -- line as space separated Unix style
1448 -- filenames. Lower case and add default
1449 -- extension as appropriate.
1452 Arg1_Idx : Integer := Arg'First;
1454 function Get_Arg1_End
1456 Arg_Idx : Integer) return Integer;
1457 -- Begins looking at Arg_Idx + 1 and
1458 -- returns the index of the last character
1459 -- before a comma or else the index of the
1460 -- last character in the string Arg.
1466 function Get_Arg1_End
1468 Arg_Idx : Integer) return Integer
1471 for J in Arg_Idx + 1 .. Arg'Last loop
1472 if Arg (J) = ',' then
1485 Get_Arg1_End (Arg.all, Arg1_Idx);
1489 Arg (Arg1_Idx .. Next_Arg1_Idx);
1492 constant String_Access :=
1493 To_Canonical_File_Spec (Arg1);
1497 Place_Lower (Normal_File.all);
1499 if Is_Extensionless (Normal_File.all)
1500 and then Command.Defext /= " "
1503 Place (Command.Defext);
1506 Arg1_Idx := Next_Arg1_Idx + 1;
1509 exit when Arg1_Idx > Arg'Last;
1511 -- Don't allow two or more commas in
1514 if Arg (Arg1_Idx) = ',' then
1515 Arg1_Idx := Arg1_Idx + 1;
1516 if Arg1_Idx > Arg'Last or else
1517 Arg (Arg1_Idx) = ','
1521 "Malformed Parameter: " &
1523 Put (Standard_Error, "usage: ");
1524 Put_Line (Standard_Error,
1535 -- Reset Output_File_Expected, in case it was True
1537 Output_File_Expected := False;
1539 -- Qualifier argument
1542 Output_File_Expected := False;
1544 -- This code is too heavily nested, should be
1545 -- separated out as separate subprogram ???
1551 Endp : Natural := 0; -- avoid warning!
1556 while SwP < Arg'Last
1557 and then Arg (SwP + 1) /= '='
1562 -- At this point, the switch name is in
1563 -- Arg (Arg'First..SwP) and if that is not the
1564 -- whole switch, then there is an equal sign at
1565 -- Arg (SwP + 1) and the rest of Arg is what comes
1566 -- after the equal sign.
1568 -- If make commands are active, see if we have
1569 -- another COMMANDS_TRANSLATION switch belonging
1572 if Make_Commands_Active /= null then
1575 (Arg (Arg'First .. SwP),
1580 and then Sw.Translation = T_Commands
1587 (Arg (Arg'First .. SwP),
1588 Make_Commands_Active.Switches,
1592 -- For case of GNAT MAKE or CHOP, if we cannot
1593 -- find the switch, then see if it is a
1594 -- recognized compiler switch instead, and if
1595 -- so process the compiler switch.
1597 elsif Command.Name.all = "MAKE"
1598 or else Command.Name.all = "CHOP" then
1601 (Arg (Arg'First .. SwP),
1608 (Arg (Arg'First .. SwP),
1610 ("COMPILE", Commands).Switches,
1614 -- For all other cases, just search the relevant
1620 (Arg (Arg'First .. SwP),
1626 case Sw.Translation is
1629 Place_Unix_Switches (Sw.Unix_String);
1631 and then Arg (SwP + 1) = '='
1633 Put (Standard_Error,
1634 "qualifier options ignored: ");
1635 Put_Line (Standard_Error, Arg.all);
1638 when T_Directories =>
1639 if SwP + 1 > Arg'Last then
1640 Put (Standard_Error,
1641 "missing directories for: ");
1642 Put_Line (Standard_Error, Arg.all);
1643 Errors := Errors + 1;
1645 elsif Arg (SwP + 2) /= '(' then
1649 elsif Arg (Arg'Last) /= ')' then
1651 -- Remove spaces from a comma separated
1652 -- list of file names and adjust
1653 -- control variables accordingly.
1655 if Arg_Num < Argument_Count and then
1656 (Argv (Argv'Last) = ',' xor
1657 Argument (Arg_Num + 1)
1658 (Argument (Arg_Num + 1)'First) = ',')
1661 new String'(Argv
.all
1664 Arg_Num
:= Arg_Num
+ 1;
1665 Arg_Idx
:= Argv
'First;
1667 Get_Arg_End
(Argv
.all, Arg_Idx
);
1669 (Argv (Arg_Idx .. Next_Arg_Idx));
1670 goto Tryagain_After_Coalesce;
1673 Put (Standard_Error,
1674 "incorrectly parenthesized " &
1675 "or malformed argument: ");
1676 Put_Line (Standard_Error, Arg.all);
1677 Errors := Errors + 1;
1681 Endp := Arg'Last - 1;
1684 while SwP <= Endp loop
1686 Dir_Is_Wild : Boolean := False;
1687 Dir_Maybe_Is_Wild : Boolean := False;
1689 Dir_List : String_Access_List_Access;
1695 and then Arg (P2 + 1) /= ','
1697 -- A wildcard directory spec on
1698 -- VMS will contain either * or
1701 if Arg (P2) = '*' then
1702 Dir_Is_Wild := True;
1704 elsif Arg (P2) = '%' then
1705 Dir_Is_Wild := True;
1707 elsif Dir_Maybe_Is_Wild
1708 and then Arg (P2) = '.'
1709 and then Arg (P2 + 1) = '.'
1711 Dir_Is_Wild := True;
1712 Dir_Maybe_Is_Wild := False;
1714 elsif Dir_Maybe_Is_Wild then
1715 Dir_Maybe_Is_Wild := False;
1717 elsif Arg (P2) = '.'
1718 and then Arg (P2 + 1) = '.'
1720 Dir_Maybe_Is_Wild := True;
1729 To_Canonical_File_List
1730 (Arg (SwP .. P2), True);
1732 for J in Dir_List.all'Range loop
1736 (Dir_List.all (J).all);
1743 (To_Canonical_Dir_Spec
1744 (Arg (SwP .. P2), False).all);
1752 if SwP + 1 > Arg'Last then
1753 Put (Standard_Error,
1754 "missing directory for: ");
1755 Put_Line (Standard_Error, Arg.all);
1756 Errors := Errors + 1;
1759 Place_Unix_Switches (Sw.Unix_String);
1761 -- Some switches end in "=". No space
1765 (Sw.Unix_String'Last) /= '='
1771 (To_Canonical_Dir_Spec
1772 (Arg (SwP + 2 .. Arg'Last),
1776 when T_File | T_No_Space_File =>
1777 if SwP + 1 > Arg'Last then
1778 Put (Standard_Error,
1779 "missing file for: ");
1780 Put_Line (Standard_Error, Arg.all);
1781 Errors := Errors + 1;
1784 Place_Unix_Switches (Sw.Unix_String);
1786 -- Some switches end in "=". No space
1789 if Sw.Translation = T_File
1790 and then Sw.Unix_String
1791 (Sw.Unix_String'Last) /= '='
1797 (To_Canonical_File_Spec
1798 (Arg (SwP + 2 .. Arg'Last)).all);
1802 if OK_Integer (Arg (SwP + 2 .. Arg'Last)) then
1803 Place_Unix_Switches (Sw.Unix_String);
1804 Place (Arg (SwP + 2 .. Arg'Last));
1807 Put (Standard_Error, "argument for ");
1808 Put (Standard_Error, Sw.Name.all);
1810 (Standard_Error, " must be numeric");
1811 Errors := Errors + 1;
1814 when T_Alphanumplus =>
1815 if OK_Alphanumerplus
1816 (Arg (SwP + 2 .. Arg'Last))
1818 Place_Unix_Switches (Sw.Unix_String);
1819 Place (Arg (SwP + 2 .. Arg'Last));
1822 Put (Standard_Error, "argument for ");
1823 Put (Standard_Error, Sw.Name.all);
1824 Put_Line (Standard_Error,
1825 " must be alphanumeric");
1826 Errors := Errors + 1;
1831 -- A String value must be extended to the
1832 -- end of the Argv, otherwise strings like
1833 -- "foo/bar" get split at the slash.
1835 -- The begining and ending of the string
1836 -- are flagged with embedded nulls which
1837 -- are removed when building the Spawn
1838 -- call. Nulls are use because they won't
1839 -- show up in a /? output. Quotes aren't
1840 -- used because that would make it
1841 -- difficult to embed them.
1843 Place_Unix_Switches (Sw.Unix_String);
1845 if Next_Arg_Idx /= Argv'Last then
1846 Next_Arg_Idx := Argv'Last;
1848 (Argv
(Arg_Idx
.. Next_Arg_Idx
));
1851 while SwP
< Arg
'Last and then
1852 Arg
(SwP
+ 1) /= '=' loop
1858 Place
(Arg
(SwP
+ 2 .. Arg
'Last));
1863 -- Output -largs/-bargs/-cargs
1866 Place
(Sw
.Unix_String
1867 (Sw
.Unix_String
'First ..
1868 Sw
.Unix_String
'First + 5));
1871 (Sw
.Unix_String
'First + 7 ..
1872 Sw
.Unix_String
'Last) = "MAKE"
1874 Make_Commands_Active
:= null;
1877 -- Set source of new commands, also
1878 -- setting this non-null indicates that
1879 -- we are in the special commands mode
1880 -- for processing the -xargs case.
1882 Make_Commands_Active
:=
1885 (Sw
.Unix_String
'First + 7 ..
1886 Sw
.Unix_String
'Last),
1891 if SwP
+ 1 > Arg
'Last then
1893 (Sw
.Options
.Unix_String
);
1896 elsif Arg
(SwP
+ 2) /= '(' then
1900 elsif Arg
(Arg
'Last) /= ')' then
1901 Put
(Standard_Error
,
1902 "incorrectly parenthesized argument: ");
1903 Put_Line
(Standard_Error
, Arg
.all);
1904 Errors
:= Errors
+ 1;
1909 Endp
:= Arg
'Last - 1;
1912 while SwP
<= Endp
loop
1916 and then Arg
(P2
+ 1) /= ','
1921 -- Option name is in Arg (SwP .. P2)
1923 Opt
:= Matching_Name
(Arg
(SwP
.. P2
),
1936 (new String'(Sw.Unix_String.all &
1944 Arg_Idx := Next_Arg_Idx + 1;
1947 exit when Arg_Idx > Argv'Last;
1951 if not Is_Open (Arg_File) then
1952 Arg_Num := Arg_Num + 1;
1954 end Process_Argument;
1956 --------------------------------
1957 -- Validate_Command_Or_Option --
1958 --------------------------------
1960 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
1962 pragma Assert (N'Length > 0);
1964 for J in N'Range loop
1966 pragma Assert (N (J - 1) /= '_
');
1969 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
1973 end Validate_Command_Or_Option;
1975 --------------------------
1976 -- Validate_Unix_Switch --
1977 --------------------------
1979 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
1981 if S (S'First) = '`
' then
1985 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
1987 for J in S'First + 1 .. S'Last loop
1988 pragma Assert (S (J) /= ' ');
1991 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
1995 end Validate_Unix_Switch;
1997 --------------------
1998 -- VMS_Conversion --
1999 --------------------
2001 procedure VMS_Conversion (The_Command : out Command_Type) is
2002 Result : Command_Type := Undefined;
2003 Result_Set : Boolean := False;
2007 -- First we must preprocess the string form of the command and options
2008 -- list into the internal form that we use.
2010 Preprocess_Command_Data;
2012 -- If no parameters, give complete list of commands
2014 if Argument_Count = 0 then
2017 Put_Line ("List of available commands");
2020 while Commands /= null loop
2021 Put (Commands.Usage.all);
2023 Put_Line (Commands.Unix_String.all);
2024 Commands := Commands.Next;
2032 -- Loop through arguments
2034 while Arg_Num <= Argument_Count loop
2035 Process_Argument (Result);
2037 if not Result_Set then
2038 The_Command := Result;
2043 -- Gross error checking that the number of parameters is correct.
2044 -- Not applicable to Unlimited_Files parameters.
2046 if (Param_Count = Command.Params'Length - 1
2047 and then Command.Params (Param_Count + 1) = Unlimited_Files)
2048 or else Param_Count <= Command.Params'Length
2053 Put_Line (Standard_Error,
2054 "Parameter count of "
2055 & Integer'Image (Param_Count)
2056 & " not equal to expected "
2057 & Integer'Image (Command.Params'Length));
2058 Put (Standard_Error, "usage: ");
2059 Put_Line (Standard_Error, Command.Usage.all);
2060 Errors := Errors + 1;
2066 -- Prepare arguments for a call to spawn, filtering out
2067 -- embedded nulls place there to delineate strings.
2071 Inside_Nul : Boolean := False;
2072 Arg : String (1 .. 1024);
2078 while P1 <= Buffer.Last and then Buffer.Table (P1) = ' ' loop
2083 Arg (Arg_Ctr) := Buffer.Table (P1);
2085 while P1 <= Buffer.Last loop
2087 if Buffer.Table (P1) = ASCII.NUL then
2089 Inside_Nul := False;
2095 if Buffer.Table (P1) = ' ' and then not Inside_Nul then
2097 Arg_Ctr := Arg_Ctr + 1;
2098 Arg (Arg_Ctr) := Buffer.Table (P1);
2101 Last_Switches.Increment_Last;
2104 while P2 < Buffer.Last
2105 and then (Buffer.Table (P2 + 1) /= ' ' or else
2109 Arg_Ctr := Arg_Ctr + 1;
2110 Arg (Arg_Ctr) := Buffer.Table (P2);
2111 if Buffer.Table (P2) = ASCII.NUL then
2112 Arg_Ctr := Arg_Ctr - 1;
2114 Inside_Nul := False;
2121 Last_Switches.Table (Last_Switches.Last) :=
2122 new String'(String (Arg
(1 .. Arg_Ctr
)));
2125 Arg
(Arg_Ctr
) := Buffer
.Table
(P1
);