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;
29 with Sdefault; use Sdefault;
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;
37 package body VMS_Conv is
39 Param_Count : Natural := 0;
40 -- Number of parameter arguments so far
46 -- Pointer to head of list of command items, one for each command, with
47 -- the end of the list marked by a null pointer.
49 Last_Command : Item_Ptr;
50 -- Pointer to last item in Commands list
53 -- Pointer to command item for current command
55 Make_Commands_Active : Item_Ptr := null;
56 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
57 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
60 package Buffer is new Table.Table
61 (Table_Component_Type => Character,
62 Table_Index_Type => Integer,
64 Table_Initial => 4096,
66 Table_Name => "Buffer");
68 function Init_Object_Dirs return Argument_List;
69 -- Get the list of the object directories
71 function Invert_Sense (S : String) return VMS_Data.String_Ptr;
72 -- Given a unix switch string S, computes the inverse (adding or
73 -- removing ! characters as required), and returns a pointer to
74 -- the allocated result on the heap.
76 function Is_Extensionless (F : String) return Boolean;
77 -- Returns true if the filename has no extension.
79 function Match (S1, S2 : String) return Boolean;
80 -- Determines whether S1 and S2 match. This is a case insensitive match.
82 function Match_Prefix (S1, S2 : String) return Boolean;
83 -- Determines whether S1 matches a prefix of S2. This is also a case
84 -- insensitive match (for example Match ("AB","abc") is True).
86 function Matching_Name
89 Quiet : Boolean := False)
91 -- Determines if the item list headed by Itm and threaded through the
92 -- Next fields (with null marking the end of the list), contains an
93 -- entry that uniquely matches the given string. The match is case
94 -- insensitive and permits unique abbreviation. If the match succeeds,
95 -- then a pointer to the matching item is returned. Otherwise, an
96 -- appropriate error message is written. Note that the discriminant
97 -- of Itm is used to determine the appropriate form of this message.
98 -- Quiet is normally False as shown, if it is set to True, then no
99 -- error message is generated in a not found situation (null is still
100 -- returned to indicate the not-found situation).
102 function OK_Alphanumerplus (S : String) return Boolean;
103 -- Checks that S is a string of alphanumeric characters,
104 -- returning True if all alphanumeric characters,
105 -- False if empty or a non-alphanumeric character is present.
107 function OK_Integer (S : String) return Boolean;
108 -- Checks that S is a string of digits, returning True if all digits,
109 -- False if empty or a non-digit is present.
111 procedure Place (C : Character);
112 -- Place a single character in the buffer, updating Ptr
114 procedure Place (S : String);
115 -- Place a string character in the buffer, updating Ptr
117 procedure Place_Lower (S : String);
118 -- Place string in buffer, forcing letters to lower case, updating Ptr
120 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr);
121 -- Given a unix switch string, place corresponding switches in Buffer,
122 -- updating Ptr appropriatelly. Note that in the case of use of ! the
123 -- result may be to remove a previously placed switch.
125 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr);
126 -- Check that N is a valid command or option name, i.e. that it is of the
127 -- form of an Ada identifier with upper case letters and underscores.
129 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr);
130 -- Check that S is a valid switch string as described in the syntax for
131 -- the switch table item UNIX_SWITCH or else begins with a backquote.
133 ----------------------
134 -- Init_Object_Dirs --
135 ----------------------
137 function Init_Object_Dirs return Argument_List is
138 Object_Dirs : Integer;
139 Object_Dir : Argument_List (1 .. 256);
140 Object_Dir_Name : String_Access;
144 Object_Dir_Name := String_Access (Object_Dir_Default_Name);
145 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
149 Dir : constant String_Access :=
150 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
152 exit when Dir = null;
153 Object_Dirs := Object_Dirs + 1;
154 Object_Dir (Object_Dirs) :=
156 To_Canonical_Dir_Spec
158 (Normalize_Directory_Name (Dir.all).all,
159 True).all, True).all);
163 Object_Dirs := Object_Dirs + 1;
164 Object_Dir (Object_Dirs) := new String'("-lgnat");
166 if Hostparm.OpenVMS then
167 Object_Dirs := Object_Dirs + 1;
168 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
171 return Object_Dir (1 .. Object_Dirs);
172 end Init_Object_Dirs;
178 procedure Initialize is
182 (Cname => new S'("BIND"),
183 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
185 Unixcmd => new S'("gnatbind"),
187 Switches => Bind_Switches'Access,
188 Params => new Parameter_Array'(1 => File),
192 (Cname => new S'("CHOP"),
193 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
195 Unixcmd => new S'("gnatchop"),
197 Switches => Chop_Switches'Access,
198 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
202 (Cname => new S'("CLEAN"),
203 Usage => new S'("GNAT CLEAN /qualifiers files"),
205 Unixcmd => new S'("gnatclean"),
207 Switches => Clean_Switches'Access,
208 Params => new Parameter_Array'(1 => File),
212 (Cname => new S'("COMPILE"),
213 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
215 Unixcmd => new S'("gnatmake"),
216 Unixsws => new Argument_List'(1 => new String'("-f"),
217 2 => new String'("-u"),
218 3 => new String'("-c")),
219 Switches => GCC_Switches'Access,
220 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
224 (Cname => new S'("ELIM"),
225 Usage => new S'("GNAT ELIM name /qualifiers"),
227 Unixcmd => new S'("gnatelim"),
229 Switches => Elim_Switches'Access,
230 Params => new Parameter_Array'(1 => Other_As_Is),
234 (Cname => new S'("FIND"),
235 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
236 & "[:column]]] filespec[,...] /qualifiers"),
238 Unixcmd => new S'("gnatfind"),
240 Switches => Find_Switches'Access,
241 Params => new Parameter_Array'(1 => Other_As_Is,
242 2 => Files_Or_Wildcard),
246 (Cname => new S'("KRUNCH"),
247 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
249 Unixcmd => new S'("gnatkr"),
251 Switches => Krunch_Switches'Access,
252 Params => new Parameter_Array'(1 => File),
256 (Cname => new S'("LIBRARY"),
257 Usage => new S'("GNAT LIBRARY /[CREATE | SET | DELETE]"
258 & "=directory [/CONFIG=file]"),
260 Unixcmd => new S'("gnatlbr"),
262 Switches => Lbr_Switches'Access,
263 Params => new Parameter_Array'(1 .. 0 => File),
267 (Cname => new S'("LINK"),
268 Usage => new S'("GNAT LINK file[.ali]"
269 & " [extra obj_&_lib_&_exe_&_opt files]"
272 Unixcmd => new S'("gnatlink"),
274 Switches => Link_Switches'Access,
275 Params => new Parameter_Array'(1 => Unlimited_Files),
279 (Cname => new S'("LIST"),
280 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
282 Unixcmd => new S'("gnatls"),
284 Switches => List_Switches'Access,
285 Params => new Parameter_Array'(1 => Unlimited_Files),
289 (Cname => new S'("MAKE"),
290 Usage => new S'("GNAT MAKE file /qualifiers (includes "
291 & "COMPILE /qualifiers)"),
293 Unixcmd => new S'("gnatmake"),
295 Switches => Make_Switches'Access,
296 Params => new Parameter_Array'(1 => File),
300 (Cname => new S'("NAME"),
301 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
302 & "[naming-patterns]"),
304 Unixcmd => new S'("gnatname"),
306 Switches => Name_Switches'Access,
307 Params => new Parameter_Array'(1 => Unlimited_As_Is),
311 (Cname => new S'("PREPROCESS"),
313 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
315 Unixcmd => new S'("gnatprep"),
317 Switches => Prep_Switches'Access,
318 Params => new Parameter_Array'(1 .. 3 => File),
322 (Cname => new S'("PRETTY"),
323 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
325 Unixcmd => new S'("gnatpp"),
327 Switches => Pretty_Switches'Access,
328 Params => new Parameter_Array'(1 => File),
332 (Cname => new S'("SHARED"),
333 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
334 & "files] /qualifiers"),
336 Unixcmd => new S'("gcc"),
338 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
339 Switches => Shared_Switches'Access,
340 Params => new Parameter_Array'(1 => Unlimited_Files),
344 (Cname => new S'("STANDARD"),
345 Usage => new S'("GNAT STANDARD"),
347 Unixcmd => new S'("gnatpsta"),
349 Switches => Standard_Switches'Access,
350 Params => new Parameter_Array'(1 .. 0 => File),
354 (Cname => new S'("STUB"),
355 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
357 Unixcmd => new S'("gnatstub"),
359 Switches => Stub_Switches'Access,
360 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
364 (Cname => new S'("XREF"),
365 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
367 Unixcmd => new S'("gnatxref"),
369 Switches => Xref_Switches'Access,
370 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
379 function Invert_Sense (S : String) return VMS_Data.String_Ptr is
380 Sinv : String (1 .. S'Length * 2);
381 -- Result (for sure long enough)
383 Sinvp : Natural := 0;
384 -- Pointer to output string
387 for Sp in S'Range loop
388 if Sp = S'First or else S (Sp - 1) = ',' then
392 Sinv (Sinvp + 1) := '!';
393 Sinv (Sinvp + 2) := S (Sp);
398 Sinv (Sinvp + 1) := S (Sp);
403 return new String'(Sinv (1 .. Sinvp));
406 ----------------------
407 -- Is_Extensionless --
408 ----------------------
410 function Is_Extensionless (F : String) return Boolean is
412 for J in reverse F'Range loop
415 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
421 end Is_Extensionless;
427 function Match (S1, S2 : String) return Boolean is
428 Dif : constant Integer := S2'First - S1'First;
432 if S1'Length /= S2'Length then
436 for J in S1'Range loop
437 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
450 function Match_Prefix (S1, S2 : String) return Boolean is
452 if S1'Length > S2'Length then
455 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
463 function Matching_Name
466 Quiet : Boolean := False)
472 -- Little procedure to output command/qualifier/option as appropriate
473 -- and bump error count.
485 Errors := Errors + 1;
490 Put (Standard_Error, "command");
493 if Hostparm.OpenVMS then
494 Put (Standard_Error, "qualifier");
496 Put (Standard_Error, "switch");
500 Put (Standard_Error, "option");
504 Put (Standard_Error, "input");
508 Put (Standard_Error, ": ");
509 Put (Standard_Error, S);
512 -- Start of processing for Matching_Name
515 -- If exact match, that's the one we want
518 while P1 /= null loop
519 if Match (S, P1.Name.all) then
526 -- Now check for prefix matches
529 while P1 /= null loop
530 if P1.Name.all = "/<other>" then
533 elsif not Match_Prefix (S, P1.Name.all) then
537 -- Here we have found one matching prefix, so see if there is
538 -- another one (which is an ambiguity)
541 while P2 /= null loop
542 if Match_Prefix (S, P2.Name.all) then
544 Put (Standard_Error, "ambiguous ");
546 Put (Standard_Error, " (matches ");
547 Put (Standard_Error, P1.Name.all);
549 while P2 /= null loop
550 if Match_Prefix (S, P2.Name.all) then
551 Put (Standard_Error, ',');
552 Put (Standard_Error, P2.Name.all);
558 Put_Line (Standard_Error, ")");
567 -- If we fall through that loop, then there was only one match
573 -- If we fall through outer loop, there was no match
576 Put (Standard_Error, "unrecognized ");
578 New_Line (Standard_Error);
584 -----------------------
585 -- OK_Alphanumerplus --
586 -----------------------
588 function OK_Alphanumerplus (S : String) return Boolean is
594 for J in S'Range loop
595 if not (Is_Alphanumeric (S (J)) or else
596 S (J) = '_' or else S (J) = '$')
604 end OK_Alphanumerplus;
610 function OK_Integer (S : String) return Boolean is
616 for J in S'Range loop
617 if not Is_Digit (S (J)) then
630 procedure Output_Version is
633 Put (Gnatvsn.Gnat_Version_String);
634 Put_Line (" Copyright 1996-2003 Free Software Foundation, Inc.");
641 procedure Place (C : Character) is
643 Buffer.Increment_Last;
644 Buffer.Table (Buffer.Last) := C;
647 procedure Place (S : String) is
649 for J in S'Range loop
658 procedure Place_Lower (S : String) is
660 for J in S'Range loop
661 Place (To_Lower (S (J)));
665 -------------------------
666 -- Place_Unix_Switches --
667 -------------------------
669 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
670 P1, P2, P3 : Natural;
672 Slen, Sln2 : Natural;
673 Wild_Card : Boolean := False;
677 while P1 <= S'Last loop
686 pragma Assert (S (P1) = '-' or else S (P1) = '`');
688 while P2 < S'Last and then S (P2 + 1) /= ',' loop
692 -- Switch is now in S (P1 .. P2)
697 Wild_Card := S (P2) = '*';
705 while P3 <= Buffer.Last - Slen loop
706 if Buffer.Table (P3) = ' '
707 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
711 P3 + Slen = Buffer.Last
713 Buffer.Table (P3 + Slen + 1) = ' ')
718 while P3 + Sln2 /= Buffer.Last
719 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
725 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
726 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
727 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
739 pragma Assert (S (P2) /= '*');
746 Place (S (P1 .. P2));
751 end Place_Unix_Switches;
753 --------------------------------
754 -- Validate_Command_Or_Option --
755 --------------------------------
757 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
759 pragma Assert (N'Length > 0);
761 for J in N'Range loop
763 pragma Assert (N (J - 1) /= '_');
766 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
770 end Validate_Command_Or_Option;
772 --------------------------
773 -- Validate_Unix_Switch --
774 --------------------------
776 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
778 if S (S'First) = '`' then
782 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
784 for J in S'First + 1 .. S'Last loop
785 pragma Assert (S (J) /= ' ');
788 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
792 end Validate_Unix_Switch;
798 -- This function is *far* too long and *far* too heavily nested, it
799 -- needs procedural abstraction ???
801 procedure VMS_Conversion (The_Command : out Command_Type) is
805 -- First we must preprocess the string form of the command and options
806 -- list into the internal form that we use.
808 for C in Real_Command_Type loop
810 Command : Item_Ptr := new Command_Item;
812 Last_Switch : Item_Ptr;
813 -- Last switch in list
816 -- Link new command item into list of commands
818 if Last_Command = null then
821 Last_Command.Next := Command;
824 Last_Command := Command;
826 -- Fill in fields of new command item
828 Command.Name := Command_List (C).Cname;
829 Command.Usage := Command_List (C).Usage;
830 Command.Command := C;
832 if Command_List (C).Unixsws = null then
833 Command.Unix_String := Command_List (C).Unixcmd;
836 Cmd : String (1 .. 5_000);
838 Sws : constant Argument_List_Access :=
839 Command_List (C).Unixsws;
842 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
843 Command_List (C).Unixcmd.all;
844 Last := Command_List (C).Unixcmd'Length;
846 for J in Sws'Range loop
849 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
851 Last := Last + Sws (J)'Length;
854 Command.Unix_String := new String'(Cmd (1 .. Last));
858 Command.Params := Command_List (C).Params;
859 Command.Defext := Command_List (C).Defext;
861 Validate_Command_Or_Option (Command.Name);
863 -- Process the switch list
865 for S in Command_List (C).Switches'Range loop
867 SS : constant VMS_Data.String_Ptr :=
868 Command_List (C).Switches (S);
869 P : Natural := SS'First;
870 Sw : Item_Ptr := new Switch_Item;
873 -- Pointer to last option
876 -- Link new switch item into list of switches
878 if Last_Switch = null then
879 Command.Switches := Sw;
881 Last_Switch.Next := Sw;
886 -- Process switch string, first get name
888 while SS (P) /= ' ' and SS (P) /= '=' loop
892 Sw.Name := new String'(SS (SS'First .. P - 1));
894 -- Direct translation case
897 Sw.Translation := T_Direct;
898 Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
899 Validate_Unix_Switch (Sw.Unix_String);
901 if SS (P - 1) = '>' then
902 Sw.Translation := T_Other;
904 elsif SS (P + 1) = '`' then
907 -- Create the inverted case (/NO ..)
909 elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
910 Sw := new Switch_Item;
911 Last_Switch.Next := Sw;
915 new String'("/NO" & SS (SS'First + 1 .. P - 1));
916 Sw.Translation := T_Direct;
917 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
918 Validate_Unix_Switch (Sw.Unix_String);
921 -- Directories translation case
923 elsif SS (P + 1) = '*' then
924 pragma Assert (SS (SS'Last) = '*');
925 Sw.Translation := T_Directories;
926 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
927 Validate_Unix_Switch (Sw.Unix_String);
929 -- Directory translation case
931 elsif SS (P + 1) = '%' then
932 pragma Assert (SS (SS'Last) = '%');
933 Sw.Translation := T_Directory;
934 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
935 Validate_Unix_Switch (Sw.Unix_String);
937 -- File translation case
939 elsif SS (P + 1) = '@' then
940 pragma Assert (SS (SS'Last) = '@');
941 Sw.Translation := T_File;
942 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
943 Validate_Unix_Switch (Sw.Unix_String);
945 -- No space file translation case
947 elsif SS (P + 1) = '<' then
948 pragma Assert (SS (SS'Last) = '>');
949 Sw.Translation := T_No_Space_File;
950 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
951 Validate_Unix_Switch (Sw.Unix_String);
953 -- Numeric translation case
955 elsif SS (P + 1) = '#' then
956 pragma Assert (SS (SS'Last) = '#');
957 Sw.Translation := T_Numeric;
958 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
959 Validate_Unix_Switch (Sw.Unix_String);
961 -- Alphanumerplus translation case
963 elsif SS (P + 1) = '|' then
964 pragma Assert (SS (SS'Last) = '|');
965 Sw.Translation := T_Alphanumplus;
966 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
967 Validate_Unix_Switch (Sw.Unix_String);
969 -- String translation case
971 elsif SS (P + 1) = '"' then
972 pragma Assert (SS (SS'Last) = '"');
973 Sw.Translation := T_String;
974 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
975 Validate_Unix_Switch (Sw.Unix_String);
977 -- Commands translation case
979 elsif SS (P + 1) = '?' then
980 Sw.Translation := T_Commands;
981 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));
983 -- Options translation case
986 Sw.Translation := T_Options;
987 Sw.Unix_String := new String'("");
989 P := P + 1; -- bump past =
990 while P <= SS'Last loop
992 Opt : Item_Ptr := new Option_Item;
995 -- Link new option item into options list
997 if Last_Opt = null then
1000 Last_Opt.Next := Opt;
1005 -- Fill in fields of new option item
1008 while SS (Q) /= ' ' loop
1012 Opt.Name := new String'(SS (P .. Q - 1));
1013 Validate_Command_Or_Option (Opt.Name);
1018 while Q <= SS'Last and then SS (Q) /= ' ' loop
1022 Opt.Unix_String := new String'(SS (P .. Q - 1));
1023 Validate_Unix_Switch (Opt.Unix_String);
1033 -- If no parameters, give complete list of commands
1035 if Argument_Count = 0 then
1038 Put_Line ("List of available commands");
1041 while Commands /= null loop
1042 Put (Commands.Usage.all);
1044 Put_Line (Commands.Unix_String.all);
1045 Commands := Commands.Next;
1053 -- Loop through arguments
1055 while Arg_Num <= Argument_Count loop
1057 Process_Argument : declare
1058 Argv : String_Access;
1061 function Get_Arg_End
1065 -- Begins looking at Arg_Idx + 1 and returns the index of the
1066 -- last character before a slash or else the index of the last
1067 -- character in the string Argv.
1073 function Get_Arg_End
1079 for J in Arg_Idx + 1 .. Argv'Last loop
1080 if Argv (J) = '/' then
1088 -- Start of processing for Process_Argument
1091 Argv := new String'(Argument (Arg_Num));
1092 Arg_Idx := Argv'First;
1094 <<Tryagain_After_Coalesce>>
1097 Next_Arg_Idx : Integer;
1098 Arg : String_Access;
1101 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1102 Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1104 -- The first one must be a command name
1106 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1108 Command := Matching_Name (Arg.all, Commands);
1110 if Command = null then
1114 The_Command := Command.Command;
1116 -- Give usage information if only command given
1118 if Argument_Count = 1 and then Next_Arg_Idx = Argv'Last
1119 and then Command.Command /= VMS_Conv.Standard
1124 ("List of available qualifiers and options");
1127 Put (Command.Usage.all);
1129 Put_Line (Command.Unix_String.all);
1132 Sw : Item_Ptr := Command.Switches;
1135 while Sw /= null loop
1139 case Sw.Translation is
1143 Put_Line (Sw.Unix_String.all &
1148 Put_Line (Sw.Unix_String.all);
1150 when T_Directories =>
1151 Put ("=(direc,direc,..direc)");
1153 Put (Sw.Unix_String.all);
1155 Put (Sw.Unix_String.all);
1156 Put_Line (" direc ...");
1161 Put (Sw.Unix_String.all);
1163 if Sw.Unix_String (Sw.Unix_String'Last)
1169 Put_Line ("directory ");
1171 when T_File | T_No_Space_File =>
1174 Put (Sw.Unix_String.all);
1176 if Sw.Translation = T_File
1177 and then Sw.Unix_String
1178 (Sw.Unix_String'Last)
1190 if Sw.Unix_String (Sw.Unix_String'First)
1194 (Sw.Unix_String'First + 1
1195 .. Sw.Unix_String'Last));
1197 Put (Sw.Unix_String.all);
1202 when T_Alphanumplus =>
1206 if Sw.Unix_String (Sw.Unix_String'First)
1210 (Sw.Unix_String'First + 1
1211 .. Sw.Unix_String'Last));
1213 Put (Sw.Unix_String.all);
1225 Put (Sw.Unix_String.all);
1227 if Sw.Unix_String (Sw.Unix_String'Last)
1237 Put (" (switches for ");
1239 (Sw.Unix_String'First + 7
1240 .. Sw.Unix_String'Last));
1244 (Sw.Unix_String'First
1245 .. Sw.Unix_String'First + 5));
1246 Put_Line (" switches");
1250 Opt : Item_Ptr := Sw.Options;
1253 Put_Line ("=(option,option..)");
1255 while Opt /= null loop
1259 if Opt = Sw.Options then
1264 Put_Line (Opt.Unix_String.all);
1278 -- Special handling for internal debugging switch /?
1280 elsif Arg.all = "/?" then
1281 Display_Command := True;
1283 -- Copy -switch unchanged
1285 elsif Arg (Arg'First) = '-' then
1289 -- Copy quoted switch with quotes stripped
1291 elsif Arg (Arg'First) = '"' then
1292 if Arg (Arg'Last) /= '"' then
1293 Put (Standard_Error, "misquoted argument: ");
1294 Put_Line (Standard_Error, Arg.all);
1295 Errors := Errors + 1;
1299 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1302 -- Parameter Argument
1304 elsif Arg (Arg'First) /= '/'
1305 and then Make_Commands_Active = null
1307 Param_Count := Param_Count + 1;
1309 if Param_Count <= Command.Params'Length then
1311 case Command.Params (Param_Count) is
1313 when File | Optional_File =>
1315 Normal_File : constant String_Access :=
1316 To_Canonical_File_Spec
1321 Place_Lower (Normal_File.all);
1323 if Is_Extensionless (Normal_File.all)
1324 and then Command.Defext /= " "
1327 Place (Command.Defext);
1331 when Unlimited_Files =>
1334 constant String_Access :=
1335 To_Canonical_File_Spec (Arg.all);
1337 File_Is_Wild : Boolean := False;
1338 File_List : String_Access_List_Access;
1341 for J in Arg'Range loop
1343 or else Arg (J) = '%'
1345 File_Is_Wild := True;
1349 if File_Is_Wild then
1350 File_List := To_Canonical_File_List
1353 for J in File_List.all'Range loop
1355 Place_Lower (File_List.all (J).all);
1360 Place_Lower (Normal_File.all);
1362 if Is_Extensionless (Normal_File.all)
1363 and then Command.Defext /= " "
1366 Place (Command.Defext);
1370 Param_Count := Param_Count - 1;
1377 when Unlimited_As_Is =>
1380 Param_Count := Param_Count - 1;
1382 when Files_Or_Wildcard =>
1384 -- Remove spaces from a comma separated list
1385 -- of file names and adjust control variables
1388 while Arg_Num < Argument_Count and then
1389 (Argv (Argv'Last) = ',' xor
1390 Argument (Arg_Num + 1)
1391 (Argument (Arg_Num + 1)'First) = ',')
1394 (Argv.all & Argument (Arg_Num + 1));
1395 Arg_Num := Arg_Num + 1;
1396 Arg_Idx := Argv'First;
1398 Get_Arg_End (Argv.all, Arg_Idx);
1400 (Argv (Arg_Idx .. Next_Arg_Idx));
1403 -- Parse the comma separated list of VMS
1404 -- filenames and place them on the command
1405 -- line as space separated Unix style
1406 -- filenames. Lower case and add default
1407 -- extension as appropriate.
1410 Arg1_Idx : Integer := Arg'First;
1412 function Get_Arg1_End
1413 (Arg : String; Arg_Idx : Integer)
1415 -- Begins looking at Arg_Idx + 1 and
1416 -- returns the index of the last character
1417 -- before a comma or else the index of the
1418 -- last character in the string Arg.
1424 function Get_Arg1_End
1425 (Arg : String; Arg_Idx : Integer)
1429 for J in Arg_Idx + 1 .. Arg'Last loop
1430 if Arg (J) = ',' then
1443 Get_Arg1_End (Arg.all, Arg1_Idx);
1447 Arg (Arg1_Idx .. Next_Arg1_Idx);
1450 constant String_Access :=
1451 To_Canonical_File_Spec (Arg1);
1455 Place_Lower (Normal_File.all);
1457 if Is_Extensionless (Normal_File.all)
1458 and then Command.Defext /= " "
1461 Place (Command.Defext);
1464 Arg1_Idx := Next_Arg1_Idx + 1;
1467 exit when Arg1_Idx > Arg'Last;
1469 -- Don't allow two or more commas in
1472 if Arg (Arg1_Idx) = ',' then
1473 Arg1_Idx := Arg1_Idx + 1;
1474 if Arg1_Idx > Arg'Last or else
1475 Arg (Arg1_Idx) = ','
1479 "Malformed Parameter: " &
1481 Put (Standard_Error, "usage: ");
1482 Put_Line (Standard_Error,
1493 -- Qualifier argument
1496 -- This code is too heavily nested, should be
1497 -- separated out as separate subprogram ???
1503 Endp : Natural := 0; -- avoid warning!
1508 while SwP < Arg'Last
1509 and then Arg (SwP + 1) /= '='
1514 -- At this point, the switch name is in
1515 -- Arg (Arg'First..SwP) and if that is not the
1516 -- whole switch, then there is an equal sign at
1517 -- Arg (SwP + 1) and the rest of Arg is what comes
1518 -- after the equal sign.
1520 -- If make commands are active, see if we have
1521 -- another COMMANDS_TRANSLATION switch belonging
1524 if Make_Commands_Active /= null then
1527 (Arg (Arg'First .. SwP),
1532 and then Sw.Translation = T_Commands
1539 (Arg (Arg'First .. SwP),
1540 Make_Commands_Active.Switches,
1544 -- For case of GNAT MAKE or CHOP, if we cannot
1545 -- find the switch, then see if it is a
1546 -- recognized compiler switch instead, and if
1547 -- so process the compiler switch.
1549 elsif Command.Name.all = "MAKE"
1550 or else Command.Name.all = "CHOP" then
1553 (Arg (Arg'First .. SwP),
1560 (Arg (Arg'First .. SwP),
1562 ("COMPILE", Commands).Switches,
1566 -- For all other cases, just search the relevant
1572 (Arg (Arg'First .. SwP),
1578 case Sw.Translation is
1581 Place_Unix_Switches (Sw.Unix_String);
1583 and then Arg (SwP + 1) = '='
1585 Put (Standard_Error,
1586 "qualifier options ignored: ");
1587 Put_Line (Standard_Error, Arg.all);
1590 when T_Directories =>
1591 if SwP + 1 > Arg'Last then
1592 Put (Standard_Error,
1593 "missing directories for: ");
1594 Put_Line (Standard_Error, Arg.all);
1595 Errors := Errors + 1;
1597 elsif Arg (SwP + 2) /= '(' then
1601 elsif Arg (Arg'Last) /= ')' then
1603 -- Remove spaces from a comma separated
1604 -- list of file names and adjust
1605 -- control variables accordingly.
1607 if Arg_Num < Argument_Count and then
1608 (Argv (Argv'Last) = ',' xor
1609 Argument (Arg_Num + 1)
1610 (Argument (Arg_Num + 1)'First) = ',')
1613 new String'(Argv.all
1616 Arg_Num := Arg_Num + 1;
1617 Arg_Idx := Argv'First;
1619 := Get_Arg_End (Argv.all, Arg_Idx);
1621 (Argv (Arg_Idx .. Next_Arg_Idx));
1622 goto Tryagain_After_Coalesce;
1625 Put (Standard_Error,
1626 "incorrectly parenthesized " &
1627 "or malformed argument: ");
1628 Put_Line (Standard_Error, Arg.all);
1629 Errors := Errors + 1;
1633 Endp := Arg'Last - 1;
1636 while SwP <= Endp loop
1638 Dir_Is_Wild : Boolean := False;
1639 Dir_Maybe_Is_Wild : Boolean := False;
1640 Dir_List : String_Access_List_Access;
1645 and then Arg (P2 + 1) /= ','
1648 -- A wildcard directory spec on
1649 -- VMS will contain either * or
1652 if Arg (P2) = '*' then
1653 Dir_Is_Wild := True;
1655 elsif Arg (P2) = '%' then
1656 Dir_Is_Wild := True;
1658 elsif Dir_Maybe_Is_Wild
1659 and then Arg (P2) = '.'
1660 and then Arg (P2 + 1) = '.'
1662 Dir_Is_Wild := True;
1663 Dir_Maybe_Is_Wild := False;
1665 elsif Dir_Maybe_Is_Wild then
1666 Dir_Maybe_Is_Wild := False;
1668 elsif Arg (P2) = '.'
1669 and then Arg (P2 + 1) = '.'
1671 Dir_Maybe_Is_Wild := True;
1679 Dir_List := To_Canonical_File_List
1680 (Arg (SwP .. P2), True);
1682 for J in Dir_List.all'Range loop
1686 (Dir_List.all (J).all);
1693 (To_Canonical_Dir_Spec
1694 (Arg (SwP .. P2), False).all);
1702 if SwP + 1 > Arg'Last then
1703 Put (Standard_Error,
1704 "missing directory for: ");
1705 Put_Line (Standard_Error, Arg.all);
1706 Errors := Errors + 1;
1709 Place_Unix_Switches (Sw.Unix_String);
1711 -- Some switches end in "=". No space
1715 (Sw.Unix_String'Last) /= '='
1721 (To_Canonical_Dir_Spec
1722 (Arg (SwP + 2 .. Arg'Last),
1726 when T_File | T_No_Space_File =>
1727 if SwP + 1 > Arg'Last then
1728 Put (Standard_Error,
1729 "missing file for: ");
1730 Put_Line (Standard_Error, Arg.all);
1731 Errors := Errors + 1;
1734 Place_Unix_Switches (Sw.Unix_String);
1736 -- Some switches end in "=". No space
1739 if Sw.Translation = T_File
1740 and then Sw.Unix_String
1741 (Sw.Unix_String'Last) /= '='
1747 (To_Canonical_File_Spec
1748 (Arg (SwP + 2 .. Arg'Last)).all);
1753 OK_Integer (Arg (SwP + 2 .. Arg'Last))
1755 Place_Unix_Switches (Sw.Unix_String);
1756 Place (Arg (SwP + 2 .. Arg'Last));
1759 Put (Standard_Error, "argument for ");
1760 Put (Standard_Error, Sw.Name.all);
1762 (Standard_Error, " must be numeric");
1763 Errors := Errors + 1;
1766 when T_Alphanumplus =>
1769 (Arg (SwP + 2 .. Arg'Last))
1771 Place_Unix_Switches (Sw.Unix_String);
1772 Place (Arg (SwP + 2 .. Arg'Last));
1775 Put (Standard_Error, "argument for ");
1776 Put (Standard_Error, Sw.Name.all);
1777 Put_Line (Standard_Error,
1778 " must be alphanumeric");
1779 Errors := Errors + 1;
1784 -- A String value must be extended to the
1785 -- end of the Argv, otherwise strings like
1786 -- "foo/bar" get split at the slash.
1788 -- The begining and ending of the string
1789 -- are flagged with embedded nulls which
1790 -- are removed when building the Spawn
1791 -- call. Nulls are use because they won't
1792 -- show up in a /? output. Quotes aren't
1793 -- used because that would make it
1794 -- difficult to embed them.
1796 Place_Unix_Switches (Sw.Unix_String);
1797 if Next_Arg_Idx /= Argv'Last then
1798 Next_Arg_Idx := Argv'Last;
1800 (Argv (Arg_Idx .. Next_Arg_Idx));
1803 while SwP < Arg'Last and then
1804 Arg (SwP + 1) /= '=' loop
1809 Place (Arg (SwP + 2 .. Arg'Last));
1814 -- Output -largs/-bargs/-cargs
1817 Place (Sw.Unix_String
1818 (Sw.Unix_String'First ..
1819 Sw.Unix_String'First + 5));
1822 (Sw.Unix_String'First + 7 ..
1823 Sw.Unix_String'Last) =
1826 Make_Commands_Active := null;
1829 -- Set source of new commands, also
1830 -- setting this non-null indicates that
1831 -- we are in the special commands mode
1832 -- for processing the -xargs case.
1834 Make_Commands_Active :=
1837 (Sw.Unix_String'First + 7 ..
1838 Sw.Unix_String'Last),
1843 if SwP + 1 > Arg'Last then
1845 (Sw.Options.Unix_String);
1848 elsif Arg (SwP + 2) /= '(' then
1852 elsif Arg (Arg'Last) /= ')' then
1855 "incorrectly parenthesized " &
1857 Put_Line (Standard_Error, Arg.all);
1858 Errors := Errors + 1;
1863 Endp := Arg'Last - 1;
1866 while SwP <= Endp loop
1870 and then Arg (P2 + 1) /= ','
1875 -- Option name is in Arg (SwP .. P2)
1877 Opt := Matching_Name (Arg (SwP .. P2),
1890 (new String'(Sw.Unix_String.all &
1898 Arg_Idx := Next_Arg_Idx + 1;
1901 exit when Arg_Idx > Argv'Last;
1904 end Process_Argument;
1906 Arg_Num := Arg_Num + 1;
1909 -- Gross error checking that the number of parameters is correct.
1910 -- Not applicable to Unlimited_Files parameters.
1912 if (Param_Count = Command.Params'Length - 1
1913 and then Command.Params (Param_Count + 1) = Unlimited_Files)
1914 or else Param_Count <= Command.Params'Length
1919 Put_Line (Standard_Error,
1920 "Parameter count of "
1921 & Integer'Image (Param_Count)
1922 & " not equal to expected "
1923 & Integer'Image (Command.Params'Length));
1924 Put (Standard_Error, "usage: ");
1925 Put_Line (Standard_Error, Command.Usage.all);
1926 Errors := Errors + 1;
1932 -- Prepare arguments for a call to spawn, filtering out
1933 -- embedded nulls place there to delineate strings.
1937 Inside_Nul : Boolean := False;
1938 Arg : String (1 .. 1024);
1944 while P1 <= Buffer.Last and then Buffer.Table (P1) = ' ' loop
1949 Arg (Arg_Ctr) := Buffer.Table (P1);
1951 while P1 <= Buffer.Last loop
1953 if Buffer.Table (P1) = ASCII.NUL then
1955 Inside_Nul := False;
1961 if Buffer.Table (P1) = ' ' and then not Inside_Nul then
1963 Arg_Ctr := Arg_Ctr + 1;
1964 Arg (Arg_Ctr) := Buffer.Table (P1);
1967 Last_Switches.Increment_Last;
1970 while P2 < Buffer.Last
1971 and then (Buffer.Table (P2 + 1) /= ' ' or else
1975 Arg_Ctr := Arg_Ctr + 1;
1976 Arg (Arg_Ctr) := Buffer.Table (P2);
1977 if Buffer.Table (P2) = ASCII.NUL then
1978 Arg_Ctr := Arg_Ctr - 1;
1980 Inside_Nul := False;
1987 Last_Switches.Table (Last_Switches.Last) :=
1988 new String'(String (Arg (1 .. Arg_Ctr)));
1991 Arg (Arg_Ctr) := Buffer.Table (P1);