1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-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 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
29 with Osint; use Osint;
31 with Ada.Characters.Handling; use Ada.Characters.Handling;
32 with Ada.Command_Line; use Ada.Command_Line;
33 with Ada.Text_IO; use Ada.Text_IO;
35 package body VMS_Conv is
37 Param_Count : Natural := 0;
38 -- Number of parameter arguments so far
44 -- Pointer to head of list of command items, one for each command, with
45 -- the end of the list marked by a null pointer.
47 Last_Command : Item_Ptr;
48 -- Pointer to last item in Commands list
51 -- Pointer to command item for current command
53 Make_Commands_Active : Item_Ptr := null;
54 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
55 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
58 package Buffer is new Table.Table
59 (Table_Component_Type => Character,
60 Table_Index_Type => Integer,
62 Table_Initial => 4096,
64 Table_Name => "Buffer");
66 function Init_Object_Dirs return Argument_List;
67 -- Get the list of the object directories
69 function Invert_Sense (S : String) return VMS_Data.String_Ptr;
70 -- Given a unix switch string S, computes the inverse (adding or
71 -- removing ! characters as required), and returns a pointer to
72 -- the allocated result on the heap.
74 function Is_Extensionless (F : String) return Boolean;
75 -- Returns true if the filename has no extension.
77 function Match (S1, S2 : String) return Boolean;
78 -- Determines whether S1 and S2 match. This is a case insensitive match.
80 function Match_Prefix (S1, S2 : String) return Boolean;
81 -- Determines whether S1 matches a prefix of S2. This is also a case
82 -- insensitive match (for example Match ("AB","abc") is True).
84 function Matching_Name
87 Quiet : Boolean := False) return Item_Ptr;
88 -- Determines if the item list headed by Itm and threaded through the
89 -- Next fields (with null marking the end of the list), contains an
90 -- entry that uniquely matches the given string. The match is case
91 -- insensitive and permits unique abbreviation. If the match succeeds,
92 -- then a pointer to the matching item is returned. Otherwise, an
93 -- appropriate error message is written. Note that the discriminant
94 -- of Itm is used to determine the appropriate form of this message.
95 -- Quiet is normally False as shown, if it is set to True, then no
96 -- error message is generated in a not found situation (null is still
97 -- returned to indicate the not-found situation).
99 function OK_Alphanumerplus (S : String) return Boolean;
100 -- Checks that S is a string of alphanumeric characters,
101 -- returning True if all alphanumeric characters,
102 -- False if empty or a non-alphanumeric character is present.
104 function OK_Integer (S : String) return Boolean;
105 -- Checks that S is a string of digits, returning True if all digits,
106 -- False if empty or a non-digit is present.
108 procedure Place (C : Character);
109 -- Place a single character in the buffer, updating Ptr
111 procedure Place (S : String);
112 -- Place a string character in the buffer, updating Ptr
114 procedure Place_Lower (S : String);
115 -- Place string in buffer, forcing letters to lower case, updating Ptr
117 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr);
118 -- Given a unix switch string, place corresponding switches in Buffer,
119 -- updating Ptr appropriatelly. Note that in the case of use of ! the
120 -- result may be to remove a previously placed switch.
122 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr);
123 -- Check that N is a valid command or option name, i.e. that it is of the
124 -- form of an Ada identifier with upper case letters and underscores.
126 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr);
127 -- Check that S is a valid switch string as described in the syntax for
128 -- the switch table item UNIX_SWITCH or else begins with a backquote.
130 ----------------------
131 -- Init_Object_Dirs --
132 ----------------------
134 function Init_Object_Dirs return Argument_List is
135 Object_Dirs : Integer;
136 Object_Dir : Argument_List (1 .. 256);
137 Object_Dir_Name : String_Access;
141 Object_Dir_Name := new String'(Object_Dir_Default_Prefix);
142 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
146 Dir : constant String_Access :=
147 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
149 exit when Dir = null;
150 Object_Dirs := Object_Dirs + 1;
151 Object_Dir (Object_Dirs) :=
153 To_Canonical_Dir_Spec
155 (Normalize_Directory_Name (Dir.all).all,
156 True).all, True).all);
160 Object_Dirs := Object_Dirs + 1;
161 Object_Dir (Object_Dirs) := new String'("-lgnat");
163 if Hostparm.OpenVMS then
164 Object_Dirs := Object_Dirs + 1;
165 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
168 return Object_Dir (1 .. Object_Dirs);
169 end Init_Object_Dirs;
175 procedure Initialize is
179 (Cname => new S'("BIND"),
180 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
182 Unixcmd => new S'("gnatbind"),
184 Switches => Bind_Switches'Access,
185 Params => new Parameter_Array'(1 => File),
189 (Cname => new S'("CHOP"),
190 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
192 Unixcmd => new S'("gnatchop"),
194 Switches => Chop_Switches'Access,
195 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
199 (Cname => new S'("CLEAN"),
200 Usage => new S'("GNAT CLEAN /qualifiers files"),
202 Unixcmd => new S'("gnatclean"),
204 Switches => Clean_Switches'Access,
205 Params => new Parameter_Array'(1 => File),
209 (Cname => new S'("COMPILE"),
210 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
212 Unixcmd => new S'("gnatmake"),
213 Unixsws => new Argument_List'(1 => new String'("-f"),
214 2 => new String'("-u"),
215 3 => new String'("-c")),
216 Switches => GCC_Switches'Access,
217 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
221 (Cname => new S'("ELIM"),
222 Usage => new S'("GNAT ELIM name /qualifiers"),
224 Unixcmd => new S'("gnatelim"),
226 Switches => Elim_Switches'Access,
227 Params => new Parameter_Array'(1 => Other_As_Is),
231 (Cname => new S'("FIND"),
232 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
233 & "[:column]]] filespec[,...] /qualifiers"),
235 Unixcmd => new S'("gnatfind"),
237 Switches => Find_Switches'Access,
238 Params => new Parameter_Array'(1 => Other_As_Is,
239 2 => Files_Or_Wildcard),
243 (Cname => new S'("KRUNCH"),
244 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
246 Unixcmd => new S'("gnatkr"),
248 Switches => Krunch_Switches'Access,
249 Params => new Parameter_Array'(1 => File),
253 (Cname => new S'("LIBRARY"),
254 Usage => new S'("GNAT LIBRARY /[CREATE | SET | DELETE]"
255 & "=directory [/CONFIG=file]"),
257 Unixcmd => new S'("gnatlbr"),
259 Switches => Lbr_Switches'Access,
260 Params => new Parameter_Array'(1 .. 0 => File),
264 (Cname => new S'("LINK"),
265 Usage => new S'("GNAT LINK file[.ali]"
266 & " [extra obj_&_lib_&_exe_&_opt files]"
269 Unixcmd => new S'("gnatlink"),
271 Switches => Link_Switches'Access,
272 Params => new Parameter_Array'(1 => Unlimited_Files),
276 (Cname => new S'("LIST"),
277 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
279 Unixcmd => new S'("gnatls"),
281 Switches => List_Switches'Access,
282 Params => new Parameter_Array'(1 => Unlimited_Files),
286 (Cname => new S'("MAKE"),
287 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
288 & "COMPILE /qualifiers)"),
290 Unixcmd => new S'("gnatmake"),
292 Switches => Make_Switches'Access,
293 Params => new Parameter_Array'(1 => Unlimited_Files),
297 (Cname => new S'("NAME"),
298 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
299 & "[naming-patterns]"),
301 Unixcmd => new S'("gnatname"),
303 Switches => Name_Switches'Access,
304 Params => new Parameter_Array'(1 => Unlimited_As_Is),
308 (Cname => new S'("PREPROCESS"),
310 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
312 Unixcmd => new S'("gnatprep"),
314 Switches => Prep_Switches'Access,
315 Params => new Parameter_Array'(1 .. 3 => File),
319 (Cname => new S'("PRETTY"),
320 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
322 Unixcmd => new S'("gnatpp"),
324 Switches => Pretty_Switches'Access,
325 Params => new Parameter_Array'(1 => File),
329 (Cname => new S'("SHARED"),
330 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
331 & "files] /qualifiers"),
333 Unixcmd => new S'("gcc"),
335 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
336 Switches => Shared_Switches'Access,
337 Params => new Parameter_Array'(1 => Unlimited_Files),
341 (Cname => new S'("STUB"),
342 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
344 Unixcmd => new S'("gnatstub"),
346 Switches => Stub_Switches'Access,
347 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
351 (Cname => new S'("XREF"),
352 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
354 Unixcmd => new S'("gnatxref"),
356 Switches => Xref_Switches'Access,
357 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
366 function Invert_Sense (S : String) return VMS_Data.String_Ptr is
367 Sinv : String (1 .. S'Length * 2);
368 -- Result (for sure long enough)
370 Sinvp : Natural := 0;
371 -- Pointer to output string
374 for Sp in S'Range loop
375 if Sp = S'First or else S (Sp - 1) = ',' then
379 Sinv (Sinvp + 1) := '!';
380 Sinv (Sinvp + 2) := S (Sp);
385 Sinv (Sinvp + 1) := S (Sp);
390 return new String'(Sinv (1 .. Sinvp));
393 ----------------------
394 -- Is_Extensionless --
395 ----------------------
397 function Is_Extensionless (F : String) return Boolean is
399 for J in reverse F'Range loop
402 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
408 end Is_Extensionless;
414 function Match (S1, S2 : String) return Boolean is
415 Dif : constant Integer := S2'First - S1'First;
419 if S1'Length /= S2'Length then
423 for J in S1'Range loop
424 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
437 function Match_Prefix (S1, S2 : String) return Boolean is
439 if S1'Length > S2'Length then
442 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
450 function Matching_Name
453 Quiet : Boolean := False) return Item_Ptr
458 -- Little procedure to output command/qualifier/option as appropriate
459 -- and bump error count.
471 Errors := Errors + 1;
476 Put (Standard_Error, "command");
479 if Hostparm.OpenVMS then
480 Put (Standard_Error, "qualifier");
482 Put (Standard_Error, "switch");
486 Put (Standard_Error, "option");
490 Put (Standard_Error, "input");
494 Put (Standard_Error, ": ");
495 Put (Standard_Error, S);
498 -- Start of processing for Matching_Name
501 -- If exact match, that's the one we want
504 while P1 /= null loop
505 if Match (S, P1.Name.all) then
512 -- Now check for prefix matches
515 while P1 /= null loop
516 if P1.Name.all = "/<other>" then
519 elsif not Match_Prefix (S, P1.Name.all) then
523 -- Here we have found one matching prefix, so see if there is
524 -- another one (which is an ambiguity)
527 while P2 /= null loop
528 if Match_Prefix (S, P2.Name.all) then
530 Put (Standard_Error, "ambiguous ");
532 Put (Standard_Error, " (matches ");
533 Put (Standard_Error, P1.Name.all);
535 while P2 /= null loop
536 if Match_Prefix (S, P2.Name.all) then
537 Put (Standard_Error, ',');
538 Put (Standard_Error, P2.Name.all);
544 Put_Line (Standard_Error, ")");
553 -- If we fall through that loop, then there was only one match
559 -- If we fall through outer loop, there was no match
562 Put (Standard_Error, "unrecognized ");
564 New_Line (Standard_Error);
570 -----------------------
571 -- OK_Alphanumerplus --
572 -----------------------
574 function OK_Alphanumerplus (S : String) return Boolean is
580 for J in S'Range loop
581 if not (Is_Alphanumeric (S (J)) or else
582 S (J) = '_' or else S (J) = '$')
590 end OK_Alphanumerplus;
596 function OK_Integer (S : String) return Boolean is
602 for J in S'Range loop
603 if not Is_Digit (S (J)) then
616 procedure Output_Version is
619 Put (Gnatvsn.Gnat_Version_String);
620 Put_Line (" Copyright 1996-2004 Free Software Foundation, Inc.");
627 procedure Place (C : Character) is
629 Buffer.Increment_Last;
630 Buffer.Table (Buffer.Last) := C;
633 procedure Place (S : String) is
635 for J in S'Range loop
644 procedure Place_Lower (S : String) is
646 for J in S'Range loop
647 Place (To_Lower (S (J)));
651 -------------------------
652 -- Place_Unix_Switches --
653 -------------------------
655 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
656 P1, P2, P3 : Natural;
658 Slen, Sln2 : Natural;
659 Wild_Card : Boolean := False;
663 while P1 <= S'Last loop
672 pragma Assert (S (P1) = '-' or else S (P1) = '`');
674 while P2 < S'Last and then S (P2 + 1) /= ',' loop
678 -- Switch is now in S (P1 .. P2)
683 Wild_Card := S (P2) = '*';
691 while P3 <= Buffer.Last - Slen loop
692 if Buffer.Table (P3) = ' '
693 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
697 P3 + Slen = Buffer.Last
699 Buffer.Table (P3 + Slen + 1) = ' ')
704 while P3 + Sln2 /= Buffer.Last
705 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
711 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
712 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
713 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
725 pragma Assert (S (P2) /= '*');
732 Place (S (P1 .. P2));
737 end Place_Unix_Switches;
739 --------------------------------
740 -- Validate_Command_Or_Option --
741 --------------------------------
743 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
745 pragma Assert (N'Length > 0);
747 for J in N'Range loop
749 pragma Assert (N (J - 1) /= '_');
752 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
756 end Validate_Command_Or_Option;
758 --------------------------
759 -- Validate_Unix_Switch --
760 --------------------------
762 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
764 if S (S'First) = '`' then
768 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
770 for J in S'First + 1 .. S'Last loop
771 pragma Assert (S (J) /= ' ');
774 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
778 end Validate_Unix_Switch;
784 -- This function is *far* too long and *far* too heavily nested, it
785 -- needs procedural abstraction ???
787 procedure VMS_Conversion (The_Command : out Command_Type) is
791 -- First we must preprocess the string form of the command and options
792 -- list into the internal form that we use.
794 for C in Real_Command_Type loop
796 Command : constant Item_Ptr := new Command_Item;
798 Last_Switch : Item_Ptr;
799 -- Last switch in list
802 -- Link new command item into list of commands
804 if Last_Command = null then
807 Last_Command.Next := Command;
810 Last_Command := Command;
812 -- Fill in fields of new command item
814 Command.Name := Command_List (C).Cname;
815 Command.Usage := Command_List (C).Usage;
816 Command.Command := C;
818 if Command_List (C).Unixsws = null then
819 Command.Unix_String := Command_List (C).Unixcmd;
822 Cmd : String (1 .. 5_000);
824 Sws : constant Argument_List_Access :=
825 Command_List (C).Unixsws;
828 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
829 Command_List (C).Unixcmd.all;
830 Last := Command_List (C).Unixcmd'Length;
832 for J in Sws'Range loop
835 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
837 Last := Last + Sws (J)'Length;
840 Command.Unix_String := new String'(Cmd (1 .. Last));
844 Command.Params := Command_List (C).Params;
845 Command.Defext := Command_List (C).Defext;
847 Validate_Command_Or_Option (Command.Name);
849 -- Process the switch list
851 for S in Command_List (C).Switches'Range loop
853 SS : constant VMS_Data.String_Ptr :=
854 Command_List (C).Switches (S);
855 P : Natural := SS'First;
856 Sw : Item_Ptr := new Switch_Item;
859 -- Pointer to last option
862 -- Link new switch item into list of switches
864 if Last_Switch = null then
865 Command.Switches := Sw;
867 Last_Switch.Next := Sw;
872 -- Process switch string, first get name
874 while SS (P) /= ' ' and SS (P) /= '=' loop
878 Sw.Name := new String'(SS (SS'First .. P - 1));
880 -- Direct translation case
883 Sw.Translation := T_Direct;
884 Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
885 Validate_Unix_Switch (Sw.Unix_String);
887 if SS (P - 1) = '>' then
888 Sw.Translation := T_Other;
890 elsif SS (P + 1) = '`' then
893 -- Create the inverted case (/NO ..)
895 elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
896 Sw := new Switch_Item;
897 Last_Switch.Next := Sw;
901 new String'("/NO" & SS (SS'First + 1 .. P - 1));
902 Sw.Translation := T_Direct;
903 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
904 Validate_Unix_Switch (Sw.Unix_String);
907 -- Directories translation case
909 elsif SS (P + 1) = '*' then
910 pragma Assert (SS (SS'Last) = '*');
911 Sw.Translation := T_Directories;
912 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
913 Validate_Unix_Switch (Sw.Unix_String);
915 -- Directory translation case
917 elsif SS (P + 1) = '%' then
918 pragma Assert (SS (SS'Last) = '%');
919 Sw.Translation := T_Directory;
920 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
921 Validate_Unix_Switch (Sw.Unix_String);
923 -- File translation case
925 elsif SS (P + 1) = '@' then
926 pragma Assert (SS (SS'Last) = '@');
927 Sw.Translation := T_File;
928 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
929 Validate_Unix_Switch (Sw.Unix_String);
931 -- No space file translation case
933 elsif SS (P + 1) = '<' then
934 pragma Assert (SS (SS'Last) = '>');
935 Sw.Translation := T_No_Space_File;
936 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
937 Validate_Unix_Switch (Sw.Unix_String);
939 -- Numeric translation case
941 elsif SS (P + 1) = '#' then
942 pragma Assert (SS (SS'Last) = '#');
943 Sw.Translation := T_Numeric;
944 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
945 Validate_Unix_Switch (Sw.Unix_String);
947 -- Alphanumerplus translation case
949 elsif SS (P + 1) = '|' then
950 pragma Assert (SS (SS'Last) = '|');
951 Sw.Translation := T_Alphanumplus;
952 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
953 Validate_Unix_Switch (Sw.Unix_String);
955 -- String translation case
957 elsif SS (P + 1) = '"' then
958 pragma Assert (SS (SS'Last) = '"');
959 Sw.Translation := T_String;
960 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
961 Validate_Unix_Switch (Sw.Unix_String);
963 -- Commands translation case
965 elsif SS (P + 1) = '?' then
966 Sw.Translation := T_Commands;
967 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));
969 -- Options translation case
972 Sw.Translation := T_Options;
973 Sw.Unix_String := new String'("");
975 P := P + 1; -- bump past =
976 while P <= SS'Last loop
978 Opt : constant Item_Ptr := new Option_Item;
982 -- Link new option item into options list
984 if Last_Opt = null then
987 Last_Opt.Next := Opt;
992 -- Fill in fields of new option item
995 while SS (Q) /= ' ' loop
999 Opt.Name := new String'(SS (P .. Q - 1));
1000 Validate_Command_Or_Option (Opt.Name);
1005 while Q <= SS'Last and then SS (Q) /= ' ' loop
1009 Opt.Unix_String := new String'(SS (P .. Q - 1));
1010 Validate_Unix_Switch (Opt.Unix_String);
1020 -- If no parameters, give complete list of commands
1022 if Argument_Count = 0 then
1025 Put_Line ("List of available commands");
1028 while Commands /= null loop
1029 Put (Commands.Usage.all);
1031 Put_Line (Commands.Unix_String.all);
1032 Commands := Commands.Next;
1040 -- Loop through arguments
1042 while Arg_Num <= Argument_Count loop
1044 Process_Argument : declare
1045 Argv : String_Access;
1048 function Get_Arg_End
1050 Arg_Idx : Integer) return Integer;
1051 -- Begins looking at Arg_Idx + 1 and returns the index of the
1052 -- last character before a slash or else the index of the last
1053 -- character in the string Argv.
1059 function Get_Arg_End
1061 Arg_Idx : Integer) return Integer
1064 for J in Arg_Idx + 1 .. Argv'Last loop
1065 if Argv (J) = '/' then
1073 -- Start of processing for Process_Argument
1076 Argv := new String'(Argument (Arg_Num));
1077 Arg_Idx := Argv'First;
1079 <<Tryagain_After_Coalesce>>
1082 Next_Arg_Idx : Integer;
1083 Arg : String_Access;
1086 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1087 Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1089 -- The first one must be a command name
1091 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1092 Command := Matching_Name (Arg.all, Commands);
1094 if Command = null then
1098 The_Command := Command.Command;
1100 -- Give usage information if only command given
1102 if Argument_Count = 1
1103 and then Next_Arg_Idx = Argv'Last
1108 ("List of available qualifiers and options");
1111 Put (Command.Usage.all);
1113 Put_Line (Command.Unix_String.all);
1116 Sw : Item_Ptr := Command.Switches;
1119 while Sw /= null loop
1123 case Sw.Translation is
1127 Put_Line (Sw.Unix_String.all &
1132 Put_Line (Sw.Unix_String.all);
1134 when T_Directories =>
1135 Put ("=(direc,direc,..direc)");
1137 Put (Sw.Unix_String.all);
1139 Put (Sw.Unix_String.all);
1140 Put_Line (" direc ...");
1145 Put (Sw.Unix_String.all);
1147 if Sw.Unix_String (Sw.Unix_String'Last)
1153 Put_Line ("directory ");
1155 when T_File | T_No_Space_File =>
1158 Put (Sw.Unix_String.all);
1160 if Sw.Translation = T_File
1161 and then Sw.Unix_String
1162 (Sw.Unix_String'Last) /= '='
1174 (Sw.Unix_String'First) = '`'
1177 (Sw.Unix_String'First + 1
1178 .. Sw.Unix_String'Last));
1180 Put (Sw.Unix_String.all);
1185 when T_Alphanumplus =>
1190 (Sw.Unix_String'First) = '`'
1193 (Sw.Unix_String'First + 1
1194 .. Sw.Unix_String'Last));
1196 Put (Sw.Unix_String.all);
1208 Put (Sw.Unix_String.all);
1211 (Sw.Unix_String'Last) /= '='
1220 Put (" (switches for ");
1222 (Sw.Unix_String'First + 7
1223 .. Sw.Unix_String'Last));
1227 (Sw.Unix_String'First
1228 .. Sw.Unix_String'First + 5));
1229 Put_Line (" switches");
1233 Opt : Item_Ptr := Sw.Options;
1236 Put_Line ("=(option,option..)");
1238 while Opt /= null loop
1242 if Opt = Sw.Options then
1247 Put_Line (Opt.Unix_String.all);
1261 -- Special handling for internal debugging switch /?
1263 elsif Arg.all = "/?" then
1264 Display_Command := True;
1266 -- Copy -switch unchanged
1268 elsif Arg (Arg'First) = '-' then
1272 -- Copy quoted switch with quotes stripped
1274 elsif Arg (Arg'First) = '"' then
1275 if Arg (Arg'Last) /= '"' then
1276 Put (Standard_Error, "misquoted argument: ");
1277 Put_Line (Standard_Error, Arg.all);
1278 Errors := Errors + 1;
1282 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1285 -- Parameter Argument
1287 elsif Arg (Arg'First) /= '/'
1288 and then Make_Commands_Active = null
1290 Param_Count := Param_Count + 1;
1292 if Param_Count <= Command.Params'Length then
1294 case Command.Params (Param_Count) is
1296 when File | Optional_File =>
1298 Normal_File : constant String_Access :=
1299 To_Canonical_File_Spec
1304 Place_Lower (Normal_File.all);
1306 if Is_Extensionless (Normal_File.all)
1307 and then Command.Defext /= " "
1310 Place (Command.Defext);
1314 when Unlimited_Files =>
1316 Normal_File : constant String_Access :=
1317 To_Canonical_File_Spec
1320 File_Is_Wild : Boolean := False;
1321 File_List : String_Access_List_Access;
1324 for J in Arg'Range loop
1326 or else Arg (J) = '%'
1328 File_Is_Wild := True;
1332 if File_Is_Wild then
1333 File_List := To_Canonical_File_List
1336 for J in File_List.all'Range loop
1338 Place_Lower (File_List.all (J).all);
1343 Place_Lower (Normal_File.all);
1345 if Is_Extensionless (Normal_File.all)
1346 and then Command.Defext /= " "
1349 Place (Command.Defext);
1353 Param_Count := Param_Count - 1;
1360 when Unlimited_As_Is =>
1363 Param_Count := Param_Count - 1;
1365 when Files_Or_Wildcard =>
1367 -- Remove spaces from a comma separated list
1368 -- of file names and adjust control variables
1371 while Arg_Num < Argument_Count and then
1372 (Argv (Argv'Last) = ',' xor
1373 Argument (Arg_Num + 1)
1374 (Argument (Arg_Num + 1)'First) = ',')
1377 (Argv.all & Argument (Arg_Num + 1));
1378 Arg_Num := Arg_Num + 1;
1379 Arg_Idx := Argv'First;
1381 Get_Arg_End (Argv.all, Arg_Idx);
1383 (Argv (Arg_Idx .. Next_Arg_Idx));
1386 -- Parse the comma separated list of VMS
1387 -- filenames and place them on the command
1388 -- line as space separated Unix style
1389 -- filenames. Lower case and add default
1390 -- extension as appropriate.
1393 Arg1_Idx : Integer := Arg'First;
1395 function Get_Arg1_End
1397 Arg_Idx : Integer) return Integer;
1398 -- Begins looking at Arg_Idx + 1 and
1399 -- returns the index of the last character
1400 -- before a comma or else the index of the
1401 -- last character in the string Arg.
1407 function Get_Arg1_End
1409 Arg_Idx : Integer) return Integer
1412 for J in Arg_Idx + 1 .. Arg'Last loop
1413 if Arg (J) = ',' then
1426 Get_Arg1_End (Arg.all, Arg1_Idx);
1430 Arg (Arg1_Idx .. Next_Arg1_Idx);
1433 constant String_Access :=
1434 To_Canonical_File_Spec (Arg1);
1438 Place_Lower (Normal_File.all);
1440 if Is_Extensionless (Normal_File.all)
1441 and then Command.Defext /= " "
1444 Place (Command.Defext);
1447 Arg1_Idx := Next_Arg1_Idx + 1;
1450 exit when Arg1_Idx > Arg'Last;
1452 -- Don't allow two or more commas in
1455 if Arg (Arg1_Idx) = ',' then
1456 Arg1_Idx := Arg1_Idx + 1;
1457 if Arg1_Idx > Arg'Last or else
1458 Arg (Arg1_Idx) = ','
1462 "Malformed Parameter: " &
1464 Put (Standard_Error, "usage: ");
1465 Put_Line (Standard_Error,
1476 -- Qualifier argument
1479 -- This code is too heavily nested, should be
1480 -- separated out as separate subprogram ???
1486 Endp : Natural := 0; -- avoid warning!
1491 while SwP < Arg'Last
1492 and then Arg (SwP + 1) /= '='
1497 -- At this point, the switch name is in
1498 -- Arg (Arg'First..SwP) and if that is not the
1499 -- whole switch, then there is an equal sign at
1500 -- Arg (SwP + 1) and the rest of Arg is what comes
1501 -- after the equal sign.
1503 -- If make commands are active, see if we have
1504 -- another COMMANDS_TRANSLATION switch belonging
1507 if Make_Commands_Active /= null then
1510 (Arg (Arg'First .. SwP),
1515 and then Sw.Translation = T_Commands
1522 (Arg (Arg'First .. SwP),
1523 Make_Commands_Active.Switches,
1527 -- For case of GNAT MAKE or CHOP, if we cannot
1528 -- find the switch, then see if it is a
1529 -- recognized compiler switch instead, and if
1530 -- so process the compiler switch.
1532 elsif Command.Name.all = "MAKE"
1533 or else Command.Name.all = "CHOP" then
1536 (Arg (Arg'First .. SwP),
1543 (Arg (Arg'First .. SwP),
1545 ("COMPILE", Commands).Switches,
1549 -- For all other cases, just search the relevant
1555 (Arg (Arg'First .. SwP),
1561 case Sw.Translation is
1564 Place_Unix_Switches (Sw.Unix_String);
1566 and then Arg (SwP + 1) = '='
1568 Put (Standard_Error,
1569 "qualifier options ignored: ");
1570 Put_Line (Standard_Error, Arg.all);
1573 when T_Directories =>
1574 if SwP + 1 > Arg'Last then
1575 Put (Standard_Error,
1576 "missing directories for: ");
1577 Put_Line (Standard_Error, Arg.all);
1578 Errors := Errors + 1;
1580 elsif Arg (SwP + 2) /= '(' then
1584 elsif Arg (Arg'Last) /= ')' then
1586 -- Remove spaces from a comma separated
1587 -- list of file names and adjust
1588 -- control variables accordingly.
1590 if Arg_Num < Argument_Count and then
1591 (Argv (Argv'Last) = ',' xor
1592 Argument (Arg_Num + 1)
1593 (Argument (Arg_Num + 1)'First) = ',')
1596 new String'(Argv.all
1599 Arg_Num := Arg_Num + 1;
1600 Arg_Idx := Argv'First;
1602 Get_Arg_End (Argv.all, Arg_Idx);
1604 (Argv (Arg_Idx .. Next_Arg_Idx));
1605 goto Tryagain_After_Coalesce;
1608 Put (Standard_Error,
1609 "incorrectly parenthesized " &
1610 "or malformed argument: ");
1611 Put_Line (Standard_Error, Arg.all);
1612 Errors := Errors + 1;
1616 Endp := Arg'Last - 1;
1619 while SwP <= Endp loop
1621 Dir_Is_Wild : Boolean := False;
1622 Dir_Maybe_Is_Wild : Boolean := False;
1624 Dir_List : String_Access_List_Access;
1630 and then Arg (P2 + 1) /= ','
1632 -- A wildcard directory spec on
1633 -- VMS will contain either * or
1636 if Arg (P2) = '*' then
1637 Dir_Is_Wild := True;
1639 elsif Arg (P2) = '%' then
1640 Dir_Is_Wild := True;
1642 elsif Dir_Maybe_Is_Wild
1643 and then Arg (P2) = '.'
1644 and then Arg (P2 + 1) = '.'
1646 Dir_Is_Wild := True;
1647 Dir_Maybe_Is_Wild := False;
1649 elsif Dir_Maybe_Is_Wild then
1650 Dir_Maybe_Is_Wild := False;
1652 elsif Arg (P2) = '.'
1653 and then Arg (P2 + 1) = '.'
1655 Dir_Maybe_Is_Wild := True;
1664 To_Canonical_File_List
1665 (Arg (SwP .. P2), True);
1667 for J in Dir_List.all'Range loop
1671 (Dir_List.all (J).all);
1678 (To_Canonical_Dir_Spec
1679 (Arg (SwP .. P2), False).all);
1687 if SwP + 1 > Arg'Last then
1688 Put (Standard_Error,
1689 "missing directory for: ");
1690 Put_Line (Standard_Error, Arg.all);
1691 Errors := Errors + 1;
1694 Place_Unix_Switches (Sw.Unix_String);
1696 -- Some switches end in "=". No space
1700 (Sw.Unix_String'Last) /= '='
1706 (To_Canonical_Dir_Spec
1707 (Arg (SwP + 2 .. Arg'Last),
1711 when T_File | T_No_Space_File =>
1712 if SwP + 1 > Arg'Last then
1713 Put (Standard_Error,
1714 "missing file for: ");
1715 Put_Line (Standard_Error, Arg.all);
1716 Errors := Errors + 1;
1719 Place_Unix_Switches (Sw.Unix_String);
1721 -- Some switches end in "=". No space
1724 if Sw.Translation = T_File
1725 and then Sw.Unix_String
1726 (Sw.Unix_String'Last) /= '='
1732 (To_Canonical_File_Spec
1733 (Arg (SwP + 2 .. Arg'Last)).all);
1737 if OK_Integer (Arg (SwP + 2 .. Arg'Last)) then
1738 Place_Unix_Switches (Sw.Unix_String);
1739 Place (Arg (SwP + 2 .. Arg'Last));
1742 Put (Standard_Error, "argument for ");
1743 Put (Standard_Error, Sw.Name.all);
1745 (Standard_Error, " must be numeric");
1746 Errors := Errors + 1;
1749 when T_Alphanumplus =>
1750 if OK_Alphanumerplus
1751 (Arg (SwP + 2 .. Arg'Last))
1753 Place_Unix_Switches (Sw.Unix_String);
1754 Place (Arg (SwP + 2 .. Arg'Last));
1757 Put (Standard_Error, "argument for ");
1758 Put (Standard_Error, Sw.Name.all);
1759 Put_Line (Standard_Error,
1760 " must be alphanumeric");
1761 Errors := Errors + 1;
1766 -- A String value must be extended to the
1767 -- end of the Argv, otherwise strings like
1768 -- "foo/bar" get split at the slash.
1770 -- The begining and ending of the string
1771 -- are flagged with embedded nulls which
1772 -- are removed when building the Spawn
1773 -- call. Nulls are use because they won't
1774 -- show up in a /? output. Quotes aren't
1775 -- used because that would make it
1776 -- difficult to embed them.
1778 Place_Unix_Switches (Sw.Unix_String);
1780 if Next_Arg_Idx /= Argv'Last then
1781 Next_Arg_Idx := Argv'Last;
1783 (Argv (Arg_Idx .. Next_Arg_Idx));
1786 while SwP < Arg'Last and then
1787 Arg (SwP + 1) /= '=' loop
1793 Place (Arg (SwP + 2 .. Arg'Last));
1798 -- Output -largs/-bargs/-cargs
1801 Place (Sw.Unix_String
1802 (Sw.Unix_String'First ..
1803 Sw.Unix_String'First + 5));
1806 (Sw.Unix_String'First + 7 ..
1807 Sw.Unix_String'Last) = "MAKE"
1809 Make_Commands_Active := null;
1812 -- Set source of new commands, also
1813 -- setting this non-null indicates that
1814 -- we are in the special commands mode
1815 -- for processing the -xargs case.
1817 Make_Commands_Active :=
1820 (Sw.Unix_String'First + 7 ..
1821 Sw.Unix_String'Last),
1826 if SwP + 1 > Arg'Last then
1828 (Sw.Options.Unix_String);
1831 elsif Arg (SwP + 2) /= '(' then
1835 elsif Arg (Arg'Last) /= ')' then
1838 "incorrectly parenthesized " &
1840 Put_Line (Standard_Error, Arg.all);
1841 Errors := Errors + 1;
1846 Endp := Arg'Last - 1;
1849 while SwP <= Endp loop
1853 and then Arg (P2 + 1) /= ','
1858 -- Option name is in Arg (SwP .. P2)
1860 Opt := Matching_Name (Arg (SwP .. P2),
1873 (new String'(Sw.Unix_String.all &
1881 Arg_Idx := Next_Arg_Idx + 1;
1884 exit when Arg_Idx > Argv'Last;
1887 end Process_Argument;
1889 Arg_Num := Arg_Num + 1;
1892 -- Gross error checking that the number of parameters is correct.
1893 -- Not applicable to Unlimited_Files parameters.
1895 if (Param_Count = Command.Params'Length - 1
1896 and then Command.Params (Param_Count + 1) = Unlimited_Files)
1897 or else Param_Count <= Command.Params'Length
1902 Put_Line (Standard_Error,
1903 "Parameter count of "
1904 & Integer'Image (Param_Count)
1905 & " not equal to expected "
1906 & Integer'Image (Command.Params'Length));
1907 Put (Standard_Error, "usage: ");
1908 Put_Line (Standard_Error, Command.Usage.all);
1909 Errors := Errors + 1;
1915 -- Prepare arguments for a call to spawn, filtering out
1916 -- embedded nulls place there to delineate strings.
1920 Inside_Nul : Boolean := False;
1921 Arg : String (1 .. 1024);
1927 while P1 <= Buffer.Last and then Buffer.Table (P1) = ' ' loop
1932 Arg (Arg_Ctr) := Buffer.Table (P1);
1934 while P1 <= Buffer.Last loop
1936 if Buffer.Table (P1) = ASCII.NUL then
1938 Inside_Nul := False;
1944 if Buffer.Table (P1) = ' ' and then not Inside_Nul then
1946 Arg_Ctr := Arg_Ctr + 1;
1947 Arg (Arg_Ctr) := Buffer.Table (P1);
1950 Last_Switches.Increment_Last;
1953 while P2 < Buffer.Last
1954 and then (Buffer.Table (P2 + 1) /= ' ' or else
1958 Arg_Ctr := Arg_Ctr + 1;
1959 Arg (Arg_Ctr) := Buffer.Table (P2);
1960 if Buffer.Table (P2) = ASCII.NUL then
1961 Arg_Ctr := Arg_Ctr - 1;
1963 Inside_Nul := False;
1970 Last_Switches.Table (Last_Switches.Last) :=
1971 new String'(String (Arg (1 .. Arg_Ctr)));
1974 Arg (Arg_Ctr) := Buffer.Table (P1);