1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2003 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 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
28 with Osint
; use Osint
;
30 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
31 with Ada
.Command_Line
; use Ada
.Command_Line
;
32 with Ada
.Text_IO
; use Ada
.Text_IO
;
36 package body VMS_Conv
is
38 Param_Count
: Natural := 0;
39 -- Number of parameter arguments so far
45 -- Pointer to head of list of command items, one for each command, with
46 -- the end of the list marked by a null pointer.
48 Last_Command
: Item_Ptr
;
49 -- Pointer to last item in Commands list
52 -- Pointer to command item for current command
54 Make_Commands_Active
: Item_Ptr
:= null;
55 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
56 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
59 package Buffer
is new Table
.Table
60 (Table_Component_Type
=> Character,
61 Table_Index_Type
=> Integer,
63 Table_Initial
=> 4096,
65 Table_Name
=> "Buffer");
67 function Init_Object_Dirs
return Argument_List
;
68 -- Get the list of the object directories
70 function Invert_Sense
(S
: String) return VMS_Data
.String_Ptr
;
71 -- Given a unix switch string S, computes the inverse (adding or
72 -- removing ! characters as required), and returns a pointer to
73 -- the allocated result on the heap.
75 function Is_Extensionless
(F
: String) return Boolean;
76 -- Returns true if the filename has no extension.
78 function Match
(S1
, S2
: String) return Boolean;
79 -- Determines whether S1 and S2 match. This is a case insensitive match.
81 function Match_Prefix
(S1
, S2
: String) return Boolean;
82 -- Determines whether S1 matches a prefix of S2. This is also a case
83 -- insensitive match (for example Match ("AB","abc") is True).
85 function Matching_Name
88 Quiet
: Boolean := False)
90 -- Determines if the item list headed by Itm and threaded through the
91 -- Next fields (with null marking the end of the list), contains an
92 -- entry that uniquely matches the given string. The match is case
93 -- insensitive and permits unique abbreviation. If the match succeeds,
94 -- then a pointer to the matching item is returned. Otherwise, an
95 -- appropriate error message is written. Note that the discriminant
96 -- of Itm is used to determine the appropriate form of this message.
97 -- Quiet is normally False as shown, if it is set to True, then no
98 -- error message is generated in a not found situation (null is still
99 -- returned to indicate the not-found situation).
101 function OK_Alphanumerplus
(S
: String) return Boolean;
102 -- Checks that S is a string of alphanumeric characters,
103 -- returning True if all alphanumeric characters,
104 -- False if empty or a non-alphanumeric character is present.
106 function OK_Integer
(S
: String) return Boolean;
107 -- Checks that S is a string of digits, returning True if all digits,
108 -- False if empty or a non-digit is present.
110 procedure Place
(C
: Character);
111 -- Place a single character in the buffer, updating Ptr
113 procedure Place
(S
: String);
114 -- Place a string character in the buffer, updating Ptr
116 procedure Place_Lower
(S
: String);
117 -- Place string in buffer, forcing letters to lower case, updating Ptr
119 procedure Place_Unix_Switches
(S
: VMS_Data
.String_Ptr
);
120 -- Given a unix switch string, place corresponding switches in Buffer,
121 -- updating Ptr appropriatelly. Note that in the case of use of ! the
122 -- result may be to remove a previously placed switch.
124 procedure Validate_Command_Or_Option
(N
: VMS_Data
.String_Ptr
);
125 -- Check that N is a valid command or option name, i.e. that it is of the
126 -- form of an Ada identifier with upper case letters and underscores.
128 procedure Validate_Unix_Switch
(S
: VMS_Data
.String_Ptr
);
129 -- Check that S is a valid switch string as described in the syntax for
130 -- the switch table item UNIX_SWITCH or else begins with a backquote.
132 ----------------------
133 -- Init_Object_Dirs --
134 ----------------------
136 function Init_Object_Dirs
return Argument_List
is
137 Object_Dirs
: Integer;
138 Object_Dir
: Argument_List
(1 .. 256);
139 Object_Dir_Name
: String_Access
;
143 Object_Dir_Name
:= new String'(Object_Dir_Default_Prefix);
144 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
148 Dir : constant String_Access :=
149 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
151 exit when Dir = null;
152 Object_Dirs := Object_Dirs + 1;
153 Object_Dir (Object_Dirs) :=
155 To_Canonical_Dir_Spec
157 (Normalize_Directory_Name
(Dir
.all).all,
158 True).all, True).all);
162 Object_Dirs
:= Object_Dirs
+ 1;
163 Object_Dir
(Object_Dirs
) := new String'("-lgnat");
165 if Hostparm.OpenVMS then
166 Object_Dirs := Object_Dirs + 1;
167 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
170 return Object_Dir
(1 .. Object_Dirs
);
171 end Init_Object_Dirs
;
177 procedure Initialize
is
181 (Cname
=> new S
'("BIND"),
182 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
184 Unixcmd
=> new S
'("gnatbind"),
186 Switches => Bind_Switches'Access,
187 Params => new Parameter_Array'(1 => File
),
191 (Cname
=> new S
'("CHOP"),
192 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
194 Unixcmd
=> new S
'("gnatchop"),
196 Switches => Chop_Switches'Access,
197 Params => new Parameter_Array'(1 => File
, 2 => Optional_File
),
201 (Cname
=> new S
'("CLEAN"),
202 Usage => new S'("GNAT CLEAN /qualifiers files"),
204 Unixcmd
=> new S
'("gnatclean"),
206 Switches => Clean_Switches'Access,
207 Params => new Parameter_Array'(1 => File
),
211 (Cname
=> new S
'("COMPILE"),
212 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
214 Unixcmd
=> new S
'("gnatmake"),
215 Unixsws => new Argument_List'(1 => new String'("-f"),
216 2 => new String'("-u"),
217 3 => new String'("-c")),
218 Switches => GCC_Switches'Access,
219 Params => new Parameter_Array'(1 => Files_Or_Wildcard
),
223 (Cname
=> new S
'("ELIM"),
224 Usage => new S'("GNAT ELIM name /qualifiers"),
226 Unixcmd
=> new S
'("gnatelim"),
228 Switches => Elim_Switches'Access,
229 Params => new Parameter_Array'(1 => Other_As_Is
),
233 (Cname
=> new S
'("FIND"),
234 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
235 & "[:column]]] filespec[,...] /qualifiers"),
237 Unixcmd
=> new S
'("gnatfind"),
239 Switches => Find_Switches'Access,
240 Params => new Parameter_Array'(1 => Other_As_Is
,
241 2 => Files_Or_Wildcard
),
245 (Cname
=> new S
'("KRUNCH"),
246 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
248 Unixcmd
=> new S
'("gnatkr"),
250 Switches => Krunch_Switches'Access,
251 Params => new Parameter_Array'(1 => File
),
255 (Cname
=> new S
'("LIBRARY"),
256 Usage => new S'("GNAT LIBRARY /[CREATE | SET | DELETE]"
257 & "=directory [/CONFIG=file]"),
259 Unixcmd
=> new S
'("gnatlbr"),
261 Switches => Lbr_Switches'Access,
262 Params => new Parameter_Array'(1 .. 0 => File
),
266 (Cname
=> new S
'("LINK"),
267 Usage => new S'("GNAT LINK file[.ali]"
268 & " [extra obj_&_lib_&_exe_&_opt files]"
271 Unixcmd
=> new S
'("gnatlink"),
273 Switches => Link_Switches'Access,
274 Params => new Parameter_Array'(1 => Unlimited_Files
),
278 (Cname
=> new S
'("LIST"),
279 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
281 Unixcmd
=> new S
'("gnatls"),
283 Switches => List_Switches'Access,
284 Params => new Parameter_Array'(1 => Unlimited_Files
),
288 (Cname
=> new S
'("MAKE"),
289 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
290 & "COMPILE /qualifiers)"),
292 Unixcmd
=> new S
'("gnatmake"),
294 Switches => Make_Switches'Access,
295 Params => new Parameter_Array'(1 => Unlimited_Files
),
299 (Cname
=> new S
'("NAME"),
300 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
301 & "[naming-patterns]"),
303 Unixcmd
=> new S
'("gnatname"),
305 Switches => Name_Switches'Access,
306 Params => new Parameter_Array'(1 => Unlimited_As_Is
),
310 (Cname
=> new S
'("PREPROCESS"),
312 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
314 Unixcmd
=> new S
'("gnatprep"),
316 Switches => Prep_Switches'Access,
317 Params => new Parameter_Array'(1 .. 3 => File
),
321 (Cname
=> new S
'("PRETTY"),
322 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
324 Unixcmd
=> new S
'("gnatpp"),
326 Switches => Pretty_Switches'Access,
327 Params => new Parameter_Array'(1 => File
),
331 (Cname
=> new S
'("SHARED"),
332 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
333 & "files] /qualifiers"),
335 Unixcmd
=> new S
'("gcc"),
337 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
338 Switches => Shared_Switches'Access,
339 Params => new Parameter_Array'(1 => Unlimited_Files
),
343 (Cname
=> new S
'("STUB"),
344 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
346 Unixcmd
=> new S
'("gnatstub"),
348 Switches => Stub_Switches'Access,
349 Params => new Parameter_Array'(1 => File
, 2 => Optional_File
),
353 (Cname
=> new S
'("XREF"),
354 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
356 Unixcmd
=> new S
'("gnatxref"),
358 Switches => Xref_Switches'Access,
359 Params => new Parameter_Array'(1 => Files_Or_Wildcard
),
368 function Invert_Sense
(S
: String) return VMS_Data
.String_Ptr
is
369 Sinv
: String (1 .. S
'Length * 2);
370 -- Result (for sure long enough)
372 Sinvp
: Natural := 0;
373 -- Pointer to output string
376 for Sp
in S
'Range loop
377 if Sp
= S
'First or else S
(Sp
- 1) = ',' then
381 Sinv
(Sinvp
+ 1) := '!';
382 Sinv
(Sinvp
+ 2) := S
(Sp
);
387 Sinv
(Sinvp
+ 1) := S
(Sp
);
392 return new String'(Sinv (1 .. Sinvp));
395 ----------------------
396 -- Is_Extensionless --
397 ----------------------
399 function Is_Extensionless (F : String) return Boolean is
401 for J in reverse F'Range loop
404 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
410 end Is_Extensionless;
416 function Match (S1, S2 : String) return Boolean is
417 Dif : constant Integer := S2'First - S1'First;
421 if S1'Length /= S2'Length then
425 for J in S1'Range loop
426 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
439 function Match_Prefix (S1, S2 : String) return Boolean is
441 if S1'Length > S2'Length then
444 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
452 function Matching_Name
455 Quiet : Boolean := False)
461 -- Little procedure to output command/qualifier/option as appropriate
462 -- and bump error count.
474 Errors := Errors + 1;
479 Put (Standard_Error, "command");
482 if Hostparm.OpenVMS then
483 Put (Standard_Error, "qualifier");
485 Put (Standard_Error, "switch");
489 Put (Standard_Error, "option");
493 Put (Standard_Error, "input");
497 Put (Standard_Error, ": ");
498 Put (Standard_Error, S);
501 -- Start of processing for Matching_Name
504 -- If exact match, that's the one we want
507 while P1 /= null loop
508 if Match (S, P1.Name.all) then
515 -- Now check for prefix matches
518 while P1 /= null loop
519 if P1.Name.all = "/<other>" then
522 elsif not Match_Prefix (S, P1.Name.all) then
526 -- Here we have found one matching prefix, so see if there is
527 -- another one (which is an ambiguity)
530 while P2 /= null loop
531 if Match_Prefix (S, P2.Name.all) then
533 Put (Standard_Error, "ambiguous ");
535 Put (Standard_Error, " (matches ");
536 Put (Standard_Error, P1.Name.all);
538 while P2 /= null loop
539 if Match_Prefix (S, P2.Name.all) then
540 Put (Standard_Error, ',');
541 Put (Standard_Error, P2.Name.all);
547 Put_Line (Standard_Error, ")");
556 -- If we fall through that loop, then there was only one match
562 -- If we fall through outer loop, there was no match
565 Put (Standard_Error, "unrecognized ");
567 New_Line (Standard_Error);
573 -----------------------
574 -- OK_Alphanumerplus --
575 -----------------------
577 function OK_Alphanumerplus (S : String) return Boolean is
583 for J in S'Range loop
584 if not (Is_Alphanumeric (S (J)) or else
585 S (J) = '_
' or else S (J) = '$
')
593 end OK_Alphanumerplus;
599 function OK_Integer (S : String) return Boolean is
605 for J in S'Range loop
606 if not Is_Digit (S (J)) then
619 procedure Output_Version is
622 Put (Gnatvsn.Gnat_Version_String);
623 Put_Line (" Copyright 1996-2003 Free Software Foundation, Inc.");
630 procedure Place (C : Character) is
632 Buffer.Increment_Last;
633 Buffer.Table (Buffer.Last) := C;
636 procedure Place (S : String) is
638 for J in S'Range loop
647 procedure Place_Lower (S : String) is
649 for J in S'Range loop
650 Place (To_Lower (S (J)));
654 -------------------------
655 -- Place_Unix_Switches --
656 -------------------------
658 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
659 P1, P2, P3 : Natural;
661 Slen, Sln2 : Natural;
662 Wild_Card : Boolean := False;
666 while P1 <= S'Last loop
675 pragma Assert (S (P1) = '-' or else S (P1) = '`
');
677 while P2 < S'Last and then S (P2 + 1) /= ',' loop
681 -- Switch is now in S (P1 .. P2)
686 Wild_Card := S (P2) = '*';
694 while P3 <= Buffer.Last - Slen loop
695 if Buffer.Table (P3) = ' '
696 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
700 P3 + Slen = Buffer.Last
702 Buffer.Table (P3 + Slen + 1) = ' ')
707 while P3 + Sln2 /= Buffer.Last
708 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
714 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
715 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
716 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
728 pragma Assert (S (P2) /= '*');
735 Place (S (P1 .. P2));
740 end Place_Unix_Switches;
742 --------------------------------
743 -- Validate_Command_Or_Option --
744 --------------------------------
746 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
748 pragma Assert (N'Length > 0);
750 for J in N'Range loop
752 pragma Assert (N (J - 1) /= '_
');
755 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
759 end Validate_Command_Or_Option;
761 --------------------------
762 -- Validate_Unix_Switch --
763 --------------------------
765 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
767 if S (S'First) = '`
' then
771 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
773 for J in S'First + 1 .. S'Last loop
774 pragma Assert (S (J) /= ' ');
777 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
781 end Validate_Unix_Switch;
787 -- This function is *far* too long and *far* too heavily nested, it
788 -- needs procedural abstraction ???
790 procedure VMS_Conversion (The_Command : out Command_Type) is
794 -- First we must preprocess the string form of the command and options
795 -- list into the internal form that we use.
797 for C in Real_Command_Type loop
799 Command : Item_Ptr := new Command_Item;
801 Last_Switch : Item_Ptr;
802 -- Last switch in list
805 -- Link new command item into list of commands
807 if Last_Command = null then
810 Last_Command.Next := Command;
813 Last_Command := Command;
815 -- Fill in fields of new command item
817 Command.Name := Command_List (C).Cname;
818 Command.Usage := Command_List (C).Usage;
819 Command.Command := C;
821 if Command_List (C).Unixsws = null then
822 Command.Unix_String := Command_List (C).Unixcmd;
825 Cmd : String (1 .. 5_000);
827 Sws : constant Argument_List_Access :=
828 Command_List (C).Unixsws;
831 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
832 Command_List (C).Unixcmd.all;
833 Last := Command_List (C).Unixcmd'Length;
835 for J in Sws'Range loop
838 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
840 Last := Last + Sws (J)'Length;
843 Command.Unix_String := new String'(Cmd
(1 .. Last
));
847 Command
.Params
:= Command_List
(C
).Params
;
848 Command
.Defext
:= Command_List
(C
).Defext
;
850 Validate_Command_Or_Option
(Command
.Name
);
852 -- Process the switch list
854 for S
in Command_List
(C
).Switches
'Range loop
856 SS
: constant VMS_Data
.String_Ptr
:=
857 Command_List
(C
).Switches
(S
);
858 P
: Natural := SS
'First;
859 Sw
: Item_Ptr
:= new Switch_Item
;
862 -- Pointer to last option
865 -- Link new switch item into list of switches
867 if Last_Switch
= null then
868 Command
.Switches
:= Sw
;
870 Last_Switch
.Next
:= Sw
;
875 -- Process switch string, first get name
877 while SS
(P
) /= ' ' and SS
(P
) /= '=' loop
881 Sw
.Name
:= new String'(SS (SS'First .. P - 1));
883 -- Direct translation case
886 Sw.Translation := T_Direct;
887 Sw.Unix_String := new String'(SS
(P
+ 1 .. SS
'Last));
888 Validate_Unix_Switch
(Sw
.Unix_String
);
890 if SS
(P
- 1) = '>' then
891 Sw
.Translation
:= T_Other
;
893 elsif SS
(P
+ 1) = '`' then
896 -- Create the inverted case (/NO ..)
898 elsif SS
(SS
'First + 1 .. SS
'First + 2) /= "NO" then
899 Sw
:= new Switch_Item
;
900 Last_Switch
.Next
:= Sw
;
904 new String'("/NO" & SS (SS'First + 1 .. P - 1));
905 Sw.Translation := T_Direct;
906 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
907 Validate_Unix_Switch (Sw.Unix_String);
910 -- Directories translation case
912 elsif SS (P + 1) = '*' then
913 pragma Assert (SS (SS'Last) = '*');
914 Sw.Translation := T_Directories;
915 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
916 Validate_Unix_Switch
(Sw
.Unix_String
);
918 -- Directory translation case
920 elsif SS
(P
+ 1) = '%' then
921 pragma Assert
(SS
(SS
'Last) = '%');
922 Sw
.Translation
:= T_Directory
;
923 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
924 Validate_Unix_Switch (Sw.Unix_String);
926 -- File translation case
928 elsif SS (P + 1) = '@
' then
929 pragma Assert (SS (SS'Last) = '@
');
930 Sw.Translation := T_File;
931 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
932 Validate_Unix_Switch
(Sw
.Unix_String
);
934 -- No space file translation case
936 elsif SS
(P
+ 1) = '<' then
937 pragma Assert
(SS
(SS
'Last) = '>');
938 Sw
.Translation
:= T_No_Space_File
;
939 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
940 Validate_Unix_Switch (Sw.Unix_String);
942 -- Numeric translation case
944 elsif SS (P + 1) = '#
' then
945 pragma Assert (SS (SS'Last) = '#
');
946 Sw.Translation := T_Numeric;
947 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
948 Validate_Unix_Switch
(Sw
.Unix_String
);
950 -- Alphanumerplus translation case
952 elsif SS
(P
+ 1) = '|' then
953 pragma Assert
(SS
(SS
'Last) = '|');
954 Sw
.Translation
:= T_Alphanumplus
;
955 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last - 1));
956 Validate_Unix_Switch (Sw.Unix_String);
958 -- String translation case
960 elsif SS (P + 1) = '"' then
961 pragma Assert (SS (SS'Last) = '"');
962 Sw.Translation := T_String;
963 Sw.Unix_String := new String'(SS
(P
+ 2 .. SS
'Last - 1));
964 Validate_Unix_Switch
(Sw
.Unix_String
);
966 -- Commands translation case
968 elsif SS
(P
+ 1) = '?' then
969 Sw
.Translation
:= T_Commands
;
970 Sw
.Unix_String
:= new String'(SS (P + 2 .. SS'Last));
972 -- Options translation case
975 Sw.Translation := T_Options;
976 Sw.Unix_String := new String'("");
978 P
:= P
+ 1; -- bump past =
979 while P
<= SS
'Last loop
981 Opt
: Item_Ptr
:= new Option_Item
;
984 -- Link new option item into options list
986 if Last_Opt
= null then
989 Last_Opt
.Next
:= Opt
;
994 -- Fill in fields of new option item
997 while SS
(Q
) /= ' ' loop
1001 Opt
.Name
:= new String'(SS (P .. Q - 1));
1002 Validate_Command_Or_Option (Opt.Name);
1007 while Q <= SS'Last and then SS (Q) /= ' ' loop
1011 Opt.Unix_String := new String'(SS
(P
.. Q
- 1));
1012 Validate_Unix_Switch
(Opt
.Unix_String
);
1022 -- If no parameters, give complete list of commands
1024 if Argument_Count
= 0 then
1027 Put_Line
("List of available commands");
1030 while Commands
/= null loop
1031 Put
(Commands
.Usage
.all);
1033 Put_Line
(Commands
.Unix_String
.all);
1034 Commands
:= Commands
.Next
;
1042 -- Loop through arguments
1044 while Arg_Num
<= Argument_Count
loop
1046 Process_Argument
: declare
1047 Argv
: String_Access
;
1050 function Get_Arg_End
1054 -- Begins looking at Arg_Idx + 1 and returns the index of the
1055 -- last character before a slash or else the index of the last
1056 -- character in the string Argv.
1062 function Get_Arg_End
1068 for J
in Arg_Idx
+ 1 .. Argv
'Last loop
1069 if Argv
(J
) = '/' then
1077 -- Start of processing for Process_Argument
1080 Argv
:= new String'(Argument (Arg_Num));
1081 Arg_Idx := Argv'First;
1083 <<Tryagain_After_Coalesce>>
1086 Next_Arg_Idx : Integer;
1087 Arg : String_Access;
1090 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1091 Arg := new String'(Argv
(Arg_Idx
.. Next_Arg_Idx
));
1093 -- The first one must be a command name
1095 if Arg_Num
= 1 and then Arg_Idx
= Argv
'First then
1097 Command
:= Matching_Name
(Arg
.all, Commands
);
1099 if Command
= null then
1103 The_Command
:= Command
.Command
;
1105 -- Give usage information if only command given
1107 if Argument_Count
= 1
1108 and then Next_Arg_Idx
= Argv
'Last
1113 ("List of available qualifiers and options");
1116 Put
(Command
.Usage
.all);
1118 Put_Line
(Command
.Unix_String
.all);
1121 Sw
: Item_Ptr
:= Command
.Switches
;
1124 while Sw
/= null loop
1128 case Sw
.Translation
is
1132 Put_Line
(Sw
.Unix_String
.all &
1137 Put_Line
(Sw
.Unix_String
.all);
1139 when T_Directories
=>
1140 Put
("=(direc,direc,..direc)");
1142 Put
(Sw
.Unix_String
.all);
1144 Put
(Sw
.Unix_String
.all);
1145 Put_Line
(" direc ...");
1150 Put
(Sw
.Unix_String
.all);
1152 if Sw
.Unix_String
(Sw
.Unix_String
'Last)
1158 Put_Line
("directory ");
1160 when T_File | T_No_Space_File
=>
1163 Put
(Sw
.Unix_String
.all);
1165 if Sw
.Translation
= T_File
1166 and then Sw
.Unix_String
1167 (Sw
.Unix_String
'Last)
1179 if Sw
.Unix_String
(Sw
.Unix_String
'First)
1183 (Sw
.Unix_String
'First + 1
1184 .. Sw
.Unix_String
'Last));
1186 Put
(Sw
.Unix_String
.all);
1191 when T_Alphanumplus
=>
1195 if Sw
.Unix_String
(Sw
.Unix_String
'First)
1199 (Sw
.Unix_String
'First + 1
1200 .. Sw
.Unix_String
'Last));
1202 Put
(Sw
.Unix_String
.all);
1214 Put
(Sw
.Unix_String
.all);
1216 if Sw
.Unix_String
(Sw
.Unix_String
'Last)
1226 Put
(" (switches for ");
1228 (Sw
.Unix_String
'First + 7
1229 .. Sw
.Unix_String
'Last));
1233 (Sw
.Unix_String
'First
1234 .. Sw
.Unix_String
'First + 5));
1235 Put_Line
(" switches");
1239 Opt
: Item_Ptr
:= Sw
.Options
;
1242 Put_Line
("=(option,option..)");
1244 while Opt
/= null loop
1248 if Opt
= Sw
.Options
then
1253 Put_Line
(Opt
.Unix_String
.all);
1267 -- Special handling for internal debugging switch /?
1269 elsif Arg
.all = "/?" then
1270 Display_Command
:= True;
1272 -- Copy -switch unchanged
1274 elsif Arg
(Arg
'First) = '-' then
1278 -- Copy quoted switch with quotes stripped
1280 elsif Arg
(Arg
'First) = '"' then
1281 if Arg
(Arg
'Last) /= '"' then
1282 Put
(Standard_Error
, "misquoted argument: ");
1283 Put_Line
(Standard_Error
, Arg
.all);
1284 Errors
:= Errors
+ 1;
1288 Place
(Arg
(Arg
'First + 1 .. Arg
'Last - 1));
1291 -- Parameter Argument
1293 elsif Arg
(Arg
'First) /= '/'
1294 and then Make_Commands_Active
= null
1296 Param_Count
:= Param_Count
+ 1;
1298 if Param_Count
<= Command
.Params
'Length then
1300 case Command
.Params
(Param_Count
) is
1302 when File | Optional_File
=>
1304 Normal_File
: constant String_Access
:=
1305 To_Canonical_File_Spec
1310 Place_Lower
(Normal_File
.all);
1312 if Is_Extensionless
(Normal_File
.all)
1313 and then Command
.Defext
/= " "
1316 Place
(Command
.Defext
);
1320 when Unlimited_Files
=>
1323 constant String_Access
:=
1324 To_Canonical_File_Spec
(Arg
.all);
1326 File_Is_Wild
: Boolean := False;
1327 File_List
: String_Access_List_Access
;
1330 for J
in Arg
'Range loop
1332 or else Arg
(J
) = '%'
1334 File_Is_Wild
:= True;
1338 if File_Is_Wild
then
1339 File_List
:= To_Canonical_File_List
1342 for J
in File_List
.all'Range loop
1344 Place_Lower
(File_List
.all (J
).all);
1349 Place_Lower
(Normal_File
.all);
1351 if Is_Extensionless
(Normal_File
.all)
1352 and then Command
.Defext
/= " "
1355 Place
(Command
.Defext
);
1359 Param_Count
:= Param_Count
- 1;
1366 when Unlimited_As_Is
=>
1369 Param_Count
:= Param_Count
- 1;
1371 when Files_Or_Wildcard
=>
1373 -- Remove spaces from a comma separated list
1374 -- of file names and adjust control variables
1377 while Arg_Num
< Argument_Count
and then
1378 (Argv
(Argv
'Last) = ',' xor
1379 Argument
(Arg_Num
+ 1)
1380 (Argument
(Arg_Num
+ 1)'First) = ',')
1383 (Argv.all & Argument (Arg_Num + 1));
1384 Arg_Num := Arg_Num + 1;
1385 Arg_Idx := Argv'First;
1387 Get_Arg_End (Argv.all, Arg_Idx);
1389 (Argv
(Arg_Idx
.. Next_Arg_Idx
));
1392 -- Parse the comma separated list of VMS
1393 -- filenames and place them on the command
1394 -- line as space separated Unix style
1395 -- filenames. Lower case and add default
1396 -- extension as appropriate.
1399 Arg1_Idx
: Integer := Arg
'First;
1401 function Get_Arg1_End
1402 (Arg
: String; Arg_Idx
: Integer)
1404 -- Begins looking at Arg_Idx + 1 and
1405 -- returns the index of the last character
1406 -- before a comma or else the index of the
1407 -- last character in the string Arg.
1413 function Get_Arg1_End
1414 (Arg
: String; Arg_Idx
: Integer)
1418 for J
in Arg_Idx
+ 1 .. Arg
'Last loop
1419 if Arg
(J
) = ',' then
1432 Get_Arg1_End
(Arg
.all, Arg1_Idx
);
1436 Arg
(Arg1_Idx
.. Next_Arg1_Idx
);
1439 constant String_Access
:=
1440 To_Canonical_File_Spec
(Arg1
);
1444 Place_Lower
(Normal_File
.all);
1446 if Is_Extensionless
(Normal_File
.all)
1447 and then Command
.Defext
/= " "
1450 Place
(Command
.Defext
);
1453 Arg1_Idx
:= Next_Arg1_Idx
+ 1;
1456 exit when Arg1_Idx
> Arg
'Last;
1458 -- Don't allow two or more commas in
1461 if Arg
(Arg1_Idx
) = ',' then
1462 Arg1_Idx
:= Arg1_Idx
+ 1;
1463 if Arg1_Idx
> Arg
'Last or else
1464 Arg
(Arg1_Idx
) = ','
1468 "Malformed Parameter: " &
1470 Put
(Standard_Error
, "usage: ");
1471 Put_Line
(Standard_Error
,
1482 -- Qualifier argument
1485 -- This code is too heavily nested, should be
1486 -- separated out as separate subprogram ???
1492 Endp
: Natural := 0; -- avoid warning!
1497 while SwP
< Arg
'Last
1498 and then Arg
(SwP
+ 1) /= '='
1503 -- At this point, the switch name is in
1504 -- Arg (Arg'First..SwP) and if that is not the
1505 -- whole switch, then there is an equal sign at
1506 -- Arg (SwP + 1) and the rest of Arg is what comes
1507 -- after the equal sign.
1509 -- If make commands are active, see if we have
1510 -- another COMMANDS_TRANSLATION switch belonging
1513 if Make_Commands_Active
/= null then
1516 (Arg
(Arg
'First .. SwP
),
1521 and then Sw
.Translation
= T_Commands
1528 (Arg
(Arg
'First .. SwP
),
1529 Make_Commands_Active
.Switches
,
1533 -- For case of GNAT MAKE or CHOP, if we cannot
1534 -- find the switch, then see if it is a
1535 -- recognized compiler switch instead, and if
1536 -- so process the compiler switch.
1538 elsif Command
.Name
.all = "MAKE"
1539 or else Command
.Name
.all = "CHOP" then
1542 (Arg
(Arg
'First .. SwP
),
1549 (Arg
(Arg
'First .. SwP
),
1551 ("COMPILE", Commands
).Switches
,
1555 -- For all other cases, just search the relevant
1561 (Arg
(Arg
'First .. SwP
),
1567 case Sw
.Translation
is
1570 Place_Unix_Switches
(Sw
.Unix_String
);
1572 and then Arg
(SwP
+ 1) = '='
1574 Put
(Standard_Error
,
1575 "qualifier options ignored: ");
1576 Put_Line
(Standard_Error
, Arg
.all);
1579 when T_Directories
=>
1580 if SwP
+ 1 > Arg
'Last then
1581 Put
(Standard_Error
,
1582 "missing directories for: ");
1583 Put_Line
(Standard_Error
, Arg
.all);
1584 Errors
:= Errors
+ 1;
1586 elsif Arg
(SwP
+ 2) /= '(' then
1590 elsif Arg
(Arg
'Last) /= ')' then
1592 -- Remove spaces from a comma separated
1593 -- list of file names and adjust
1594 -- control variables accordingly.
1596 if Arg_Num
< Argument_Count
and then
1597 (Argv
(Argv
'Last) = ',' xor
1598 Argument
(Arg_Num
+ 1)
1599 (Argument
(Arg_Num
+ 1)'First) = ',')
1602 new String'(Argv.all
1605 Arg_Num := Arg_Num + 1;
1606 Arg_Idx := Argv'First;
1608 := Get_Arg_End (Argv.all, Arg_Idx);
1610 (Argv
(Arg_Idx
.. Next_Arg_Idx
));
1611 goto Tryagain_After_Coalesce
;
1614 Put
(Standard_Error
,
1615 "incorrectly parenthesized " &
1616 "or malformed argument: ");
1617 Put_Line
(Standard_Error
, Arg
.all);
1618 Errors
:= Errors
+ 1;
1622 Endp
:= Arg
'Last - 1;
1625 while SwP
<= Endp
loop
1627 Dir_Is_Wild
: Boolean := False;
1628 Dir_Maybe_Is_Wild
: Boolean := False;
1629 Dir_List
: String_Access_List_Access
;
1634 and then Arg
(P2
+ 1) /= ','
1637 -- A wildcard directory spec on
1638 -- VMS will contain either * or
1641 if Arg
(P2
) = '*' then
1642 Dir_Is_Wild
:= True;
1644 elsif Arg
(P2
) = '%' then
1645 Dir_Is_Wild
:= True;
1647 elsif Dir_Maybe_Is_Wild
1648 and then Arg
(P2
) = '.'
1649 and then Arg
(P2
+ 1) = '.'
1651 Dir_Is_Wild
:= True;
1652 Dir_Maybe_Is_Wild
:= False;
1654 elsif Dir_Maybe_Is_Wild
then
1655 Dir_Maybe_Is_Wild
:= False;
1657 elsif Arg
(P2
) = '.'
1658 and then Arg
(P2
+ 1) = '.'
1660 Dir_Maybe_Is_Wild
:= True;
1668 Dir_List
:= To_Canonical_File_List
1669 (Arg
(SwP
.. P2
), True);
1671 for J
in Dir_List
.all'Range loop
1675 (Dir_List
.all (J
).all);
1682 (To_Canonical_Dir_Spec
1683 (Arg
(SwP
.. P2
), False).all);
1691 if SwP
+ 1 > Arg
'Last then
1692 Put
(Standard_Error
,
1693 "missing directory for: ");
1694 Put_Line
(Standard_Error
, Arg
.all);
1695 Errors
:= Errors
+ 1;
1698 Place_Unix_Switches
(Sw
.Unix_String
);
1700 -- Some switches end in "=". No space
1704 (Sw
.Unix_String
'Last) /= '='
1710 (To_Canonical_Dir_Spec
1711 (Arg
(SwP
+ 2 .. Arg
'Last),
1715 when T_File | T_No_Space_File
=>
1716 if SwP
+ 1 > Arg
'Last then
1717 Put
(Standard_Error
,
1718 "missing file for: ");
1719 Put_Line
(Standard_Error
, Arg
.all);
1720 Errors
:= Errors
+ 1;
1723 Place_Unix_Switches
(Sw
.Unix_String
);
1725 -- Some switches end in "=". No space
1728 if Sw
.Translation
= T_File
1729 and then Sw
.Unix_String
1730 (Sw
.Unix_String
'Last) /= '='
1736 (To_Canonical_File_Spec
1737 (Arg
(SwP
+ 2 .. Arg
'Last)).all);
1742 OK_Integer
(Arg
(SwP
+ 2 .. Arg
'Last))
1744 Place_Unix_Switches
(Sw
.Unix_String
);
1745 Place
(Arg
(SwP
+ 2 .. Arg
'Last));
1748 Put
(Standard_Error
, "argument for ");
1749 Put
(Standard_Error
, Sw
.Name
.all);
1751 (Standard_Error
, " must be numeric");
1752 Errors
:= Errors
+ 1;
1755 when T_Alphanumplus
=>
1758 (Arg
(SwP
+ 2 .. Arg
'Last))
1760 Place_Unix_Switches
(Sw
.Unix_String
);
1761 Place
(Arg
(SwP
+ 2 .. Arg
'Last));
1764 Put
(Standard_Error
, "argument for ");
1765 Put
(Standard_Error
, Sw
.Name
.all);
1766 Put_Line
(Standard_Error
,
1767 " must be alphanumeric");
1768 Errors
:= Errors
+ 1;
1773 -- A String value must be extended to the
1774 -- end of the Argv, otherwise strings like
1775 -- "foo/bar" get split at the slash.
1777 -- The begining and ending of the string
1778 -- are flagged with embedded nulls which
1779 -- are removed when building the Spawn
1780 -- call. Nulls are use because they won't
1781 -- show up in a /? output. Quotes aren't
1782 -- used because that would make it
1783 -- difficult to embed them.
1785 Place_Unix_Switches
(Sw
.Unix_String
);
1786 if Next_Arg_Idx
/= Argv
'Last then
1787 Next_Arg_Idx
:= Argv
'Last;
1789 (Argv (Arg_Idx .. Next_Arg_Idx));
1792 while SwP < Arg'Last and then
1793 Arg (SwP + 1) /= '=' loop
1798 Place (Arg (SwP + 2 .. Arg'Last));
1803 -- Output -largs/-bargs/-cargs
1806 Place (Sw.Unix_String
1807 (Sw.Unix_String'First ..
1808 Sw.Unix_String'First + 5));
1811 (Sw.Unix_String'First + 7 ..
1812 Sw.Unix_String'Last) =
1815 Make_Commands_Active := null;
1818 -- Set source of new commands, also
1819 -- setting this non-null indicates that
1820 -- we are in the special commands mode
1821 -- for processing the -xargs case.
1823 Make_Commands_Active :=
1826 (Sw.Unix_String'First + 7 ..
1827 Sw.Unix_String'Last),
1832 if SwP + 1 > Arg'Last then
1834 (Sw.Options.Unix_String);
1837 elsif Arg (SwP + 2) /= '(' then
1841 elsif Arg (Arg'Last) /= ')' then
1844 "incorrectly parenthesized " &
1846 Put_Line (Standard_Error, Arg.all);
1847 Errors := Errors + 1;
1852 Endp := Arg'Last - 1;
1855 while SwP <= Endp loop
1859 and then Arg (P2 + 1) /= ','
1864 -- Option name is in Arg (SwP .. P2)
1866 Opt := Matching_Name (Arg (SwP .. P2),
1879 (new String'(Sw
.Unix_String
.all &
1887 Arg_Idx
:= Next_Arg_Idx
+ 1;
1890 exit when Arg_Idx
> Argv
'Last;
1893 end Process_Argument
;
1895 Arg_Num
:= Arg_Num
+ 1;
1898 -- Gross error checking that the number of parameters is correct.
1899 -- Not applicable to Unlimited_Files parameters.
1901 if (Param_Count
= Command
.Params
'Length - 1
1902 and then Command
.Params
(Param_Count
+ 1) = Unlimited_Files
)
1903 or else Param_Count
<= Command
.Params
'Length
1908 Put_Line
(Standard_Error
,
1909 "Parameter count of "
1910 & Integer'Image (Param_Count
)
1911 & " not equal to expected "
1912 & Integer'Image (Command
.Params
'Length));
1913 Put
(Standard_Error
, "usage: ");
1914 Put_Line
(Standard_Error
, Command
.Usage
.all);
1915 Errors
:= Errors
+ 1;
1921 -- Prepare arguments for a call to spawn, filtering out
1922 -- embedded nulls place there to delineate strings.
1926 Inside_Nul
: Boolean := False;
1927 Arg
: String (1 .. 1024);
1933 while P1
<= Buffer
.Last
and then Buffer
.Table
(P1
) = ' ' loop
1938 Arg
(Arg_Ctr
) := Buffer
.Table
(P1
);
1940 while P1
<= Buffer
.Last
loop
1942 if Buffer
.Table
(P1
) = ASCII
.NUL
then
1944 Inside_Nul
:= False;
1950 if Buffer
.Table
(P1
) = ' ' and then not Inside_Nul
then
1952 Arg_Ctr
:= Arg_Ctr
+ 1;
1953 Arg
(Arg_Ctr
) := Buffer
.Table
(P1
);
1956 Last_Switches
.Increment_Last
;
1959 while P2
< Buffer
.Last
1960 and then (Buffer
.Table
(P2
+ 1) /= ' ' or else
1964 Arg_Ctr
:= Arg_Ctr
+ 1;
1965 Arg
(Arg_Ctr
) := Buffer
.Table
(P2
);
1966 if Buffer
.Table
(P2
) = ASCII
.NUL
then
1967 Arg_Ctr
:= Arg_Ctr
- 1;
1969 Inside_Nul
:= False;
1976 Last_Switches
.Table
(Last_Switches
.Last
) :=
1977 new String'(String (Arg (1 .. Arg_Ctr)));
1980 Arg (Arg_Ctr) := Buffer.Table (P1);