1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1996-2007, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 with Gnatvsn; use Gnatvsn;
30 with Osint; use Osint;
31 with Targparm; use Targparm;
33 with Ada.Characters.Handling; use Ada.Characters.Handling;
34 with Ada.Command_Line; use Ada.Command_Line;
35 with Ada.Text_IO; use Ada.Text_IO;
37 package body VMS_Conv is
39 -------------------------
40 -- Internal Structures --
41 -------------------------
43 -- The switches and commands are defined by strings in the previous
44 -- section so that they are easy to modify, but internally, they are
45 -- kept in a more conveniently accessible form described in this
48 -- Commands, command qualifers and options have a similar common format
49 -- so that searching for matching names can be done in a common manner.
51 type Item_Id is (Id_Command, Id_Switch, Id_Option);
53 type Translation_Type is
56 -- A qualifier with no options.
57 -- Example: GNAT MAKE /VERBOSE
60 -- A qualifier followed by a list of directories
61 -- Example: GNAT COMPILE /SEARCH=([], [.FOO], [.BAR])
64 -- A qualifier followed by one directory
65 -- Example: GNAT LIBRARY /SET=[.VAXFLOATLIB]
68 -- A qualifier followed by a filename
69 -- Example: GNAT LINK /EXECUTABLE=FOO.EXE
72 -- A qualifier followed by a filename
73 -- Example: GNAT MAKE /PROJECT_FILE=PRJ.GPR
76 -- A qualifier followed by a numeric value.
77 -- Example: GNAT CHOP /FILE_NAME_MAX_LENGTH=39
80 -- A qualifier followed by a quoted string. Only used by
81 -- /IDENTIFICATION qualifier.
82 -- Example: GNAT LINK /IDENTIFICATION="3.14a1 version"
85 -- A qualifier followed by a list of options.
86 -- Example: GNAT COMPILE /REPRESENTATION_INFO=(ARRAYS,OBJECTS)
89 -- A qualifier followed by a list. Only used for
90 -- MAKE /COMPILER_QUALIFIERS /BINDER_QUALIFIERS /LINKER_QUALIFIERS
91 -- (gnatmake -cargs -bargs -largs )
92 -- Example: GNAT MAKE ... /LINKER_QUALIFIERS /VERBOSE FOOBAR.OBJ
95 -- A qualifier passed directly to the linker. Only used
96 -- for LINK and SHARED if no other match is found.
97 -- Example: GNAT LINK FOO.ALI /SYSSHR
100 -- A qualifier followed by a legal linker symbol prefix. Only used
101 -- for BIND /BUILD_LIBRARY (gnatbind -Lxyz).
102 -- Example: GNAT BIND /BUILD_LIBRARY=foobar
105 type Item (Id : Item_Id);
106 type Item_Ptr is access all Item;
108 type Item (Id : Item_Id) is record
110 -- Name of the command, switch (with slash) or option
113 -- Pointer to next item on list, always has the same Id value
115 Command : Command_Type := Undefined;
117 Unix_String : String_Ptr := null;
118 -- Corresponding Unix string. For a command, this is the unix command
119 -- name and possible default switches. For a switch or option it is
120 -- the unix switch string.
127 -- Pointer to list of switch items for the command, linked
128 -- through the Next fields with null terminating the list.
131 -- Usage information, used only for errors and the default
132 -- list of commands output.
134 Params : Parameter_Ref;
135 -- Array of parameters
137 Defext : String (1 .. 3);
138 -- Default extension. If non-blank, then this extension is
139 -- supplied by default as the extension for any file parameter
140 -- which does not have an extension already.
144 Translation : Translation_Type;
145 -- Type of switch translation. For all cases, except Options,
146 -- this is the only field needed, since the Unix translation
147 -- is found in Unix_String.
150 -- For the Options case, this field is set to point to a list
151 -- of options item (for this case Unix_String is null in the
152 -- main switch item). The end of the list is marked by null.
157 -- No special fields needed, since Name and Unix_String are
158 -- sufficient to completely described an option.
163 subtype Command_Item is Item (Id_Command);
164 subtype Switch_Item is Item (Id_Switch);
165 subtype Option_Item is Item (Id_Option);
167 Keep_Temps_Option : constant Item_Ptr :=
171 new String'("/KEEP_TEMPORARY_FILES"),
173 Command => Undefined,
174 Unix_String => null);
176 Param_Count : Natural := 0;
177 -- Number of parameter arguments so far
182 Arg_File : Ada.Text_IO.File_Type;
183 -- A file where arguments are read from
186 -- Pointer to head of list of command items, one for each command, with
187 -- the end of the list marked by a null pointer.
189 Last_Command : Item_Ptr;
190 -- Pointer to last item in Commands list
193 -- Pointer to command item for current command
195 Make_Commands_Active : Item_Ptr := null;
196 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
197 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
200 Output_File_Expected : Boolean := False;
201 -- True for GNAT LINK after -o switch, so that the ".ali" extension is
202 -- not added to the executable file name.
204 package Buffer is new Table.Table
205 (Table_Component_Type => Character,
206 Table_Index_Type => Integer,
207 Table_Low_Bound => 1,
208 Table_Initial => 4096,
209 Table_Increment => 100,
210 Table_Name => "Buffer");
211 -- Table to store the command to be used
213 package Cargs_Buffer is new Table.Table
214 (Table_Component_Type => Character,
215 Table_Index_Type => Integer,
216 Table_Low_Bound => 1,
217 Table_Initial => 4096,
218 Table_Increment => 100,
219 Table_Name => "Cargs_Buffer");
220 -- Table to store the compiler switches for GNAT COMPILE
222 Cargs : Boolean := False;
223 -- When True, commands should go to Cargs_Buffer instead of Buffer table
225 function Init_Object_Dirs return Argument_List;
226 -- Get the list of the object directories
228 function Invert_Sense (S : String) return VMS_Data.String_Ptr;
229 -- Given a unix switch string S, computes the inverse (adding or
230 -- removing ! characters as required), and returns a pointer to
231 -- the allocated result on the heap.
233 function Is_Extensionless (F : String) return Boolean;
234 -- Returns true if the filename has no extension
236 function Match (S1, S2 : String) return Boolean;
237 -- Determines whether S1 and S2 match (this is a case insensitive match)
239 function Match_Prefix (S1, S2 : String) return Boolean;
240 -- Determines whether S1 matches a prefix of S2. This is also a case
241 -- insensitive match (for example Match ("AB","abc") is True).
243 function Matching_Name
246 Quiet : Boolean := False) return Item_Ptr;
247 -- Determines if the item list headed by Itm and threaded through the
248 -- Next fields (with null marking the end of the list), contains an
249 -- entry that uniquely matches the given string. The match is case
250 -- insensitive and permits unique abbreviation. If the match succeeds,
251 -- then a pointer to the matching item is returned. Otherwise, an
252 -- appropriate error message is written. Note that the discriminant
253 -- of Itm is used to determine the appropriate form of this message.
254 -- Quiet is normally False as shown, if it is set to True, then no
255 -- error message is generated in a not found situation (null is still
256 -- returned to indicate the not-found situation).
258 function OK_Alphanumerplus (S : String) return Boolean;
259 -- Checks that S is a string of alphanumeric characters,
260 -- returning True if all alphanumeric characters,
261 -- False if empty or a non-alphanumeric character is present.
263 function OK_Integer (S : String) return Boolean;
264 -- Checks that S is a string of digits, returning True if all digits,
265 -- False if empty or a non-digit is present.
267 procedure Place (C : Character);
268 -- Place a single character in the buffer, updating Ptr
270 procedure Place (S : String);
271 -- Place a string character in the buffer, updating Ptr
273 procedure Place_Lower (S : String);
274 -- Place string in buffer, forcing letters to lower case, updating Ptr
276 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr);
277 -- Given a unix switch string, place corresponding switches in Buffer,
278 -- updating Ptr appropriatelly. Note that in the case of use of ! the
279 -- result may be to remove a previously placed switch.
281 procedure Preprocess_Command_Data;
282 -- Preprocess the string form of the command and options list into the
285 procedure Process_Argument (The_Command : in out Command_Type);
286 -- Process one argument from the command line, or one line from
287 -- from a command line file. For the first call, set The_Command.
289 procedure Process_Buffer (S : String);
290 -- Process the characters in the Buffer table or the Cargs_Buffer table
291 -- to convert these into arguments.
293 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr);
294 -- Check that N is a valid command or option name, i.e. that it is of the
295 -- form of an Ada identifier with upper case letters and underscores.
297 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr);
298 -- Check that S is a valid switch string as described in the syntax for
299 -- the switch table item UNIX_SWITCH or else begins with a backquote.
301 ----------------------
302 -- Init_Object_Dirs --
303 ----------------------
305 function Init_Object_Dirs return Argument_List is
306 Object_Dirs : Integer;
307 Object_Dir : Argument_List (1 .. 256);
308 Object_Dir_Name : String_Access;
312 Object_Dir_Name := new String'(Object_Dir_Default_Prefix);
313 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
317 Dir : constant String_Access :=
318 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
320 exit when Dir = null;
321 Object_Dirs := Object_Dirs + 1;
322 Object_Dir (Object_Dirs) :=
324 To_Canonical_Dir_Spec
326 (Normalize_Directory_Name (Dir.all).all,
327 True).all, True).all);
331 Object_Dirs := Object_Dirs + 1;
332 Object_Dir (Object_Dirs) := new String'("-lgnat");
334 if OpenVMS_On_Target then
335 Object_Dirs := Object_Dirs + 1;
336 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
339 return Object_Dir (1 .. Object_Dirs);
340 end Init_Object_Dirs;
346 procedure Initialize is
350 (Cname => new S'("BIND"),
351 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
353 Unixcmd => new S'("gnatbind"),
355 Switches => Bind_Switches'Access,
356 Params => new Parameter_Array'(1 => Unlimited_Files),
360 (Cname => new S'("CHOP"),
361 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
363 Unixcmd => new S'("gnatchop"),
365 Switches => Chop_Switches'Access,
366 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
370 (Cname => new S'("CLEAN"),
371 Usage => new S'("GNAT CLEAN /qualifiers files"),
373 Unixcmd => new S'("gnatclean"),
375 Switches => Clean_Switches'Access,
376 Params => new Parameter_Array'(1 => File),
380 (Cname => new S'("COMPILE"),
381 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
383 Unixcmd => new S'("gnatmake"),
384 Unixsws => new Argument_List'(1 => new String'("-f"),
385 2 => new String'("-u"),
386 3 => new String'("-c")),
387 Switches => GCC_Switches'Access,
388 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
392 (Cname => new S'("CHECK"),
393 Usage => new S'("GNAT CHECK name /qualifiers"),
395 Unixcmd => new S'("gnatcheck"),
397 Switches => Check_Switches'Access,
398 Params => new Parameter_Array'(1 => Unlimited_Files),
402 (Cname => new S'("ELIM"),
403 Usage => new S'("GNAT ELIM name /qualifiers"),
405 Unixcmd => new S'("gnatelim"),
407 Switches => Elim_Switches'Access,
408 Params => new Parameter_Array'(1 => Other_As_Is),
412 (Cname => new S'("FIND"),
413 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
414 & "[:column]]] filespec[,...] /qualifiers"),
416 Unixcmd => new S'("gnatfind"),
418 Switches => Find_Switches'Access,
419 Params => new Parameter_Array'(1 => Other_As_Is,
420 2 => Files_Or_Wildcard),
424 (Cname => new S'("KRUNCH"),
425 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
427 Unixcmd => new S'("gnatkr"),
429 Switches => Krunch_Switches'Access,
430 Params => new Parameter_Array'(1 => File),
434 (Cname => new S'("LINK"),
435 Usage => new S'("GNAT LINK file[.ali]"
436 & " [extra obj_&_lib_&_exe_&_opt files]"
439 Unixcmd => new S'("gnatlink"),
441 Switches => Link_Switches'Access,
442 Params => new Parameter_Array'(1 => Unlimited_Files),
446 (Cname => new S'("LIST"),
447 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
449 Unixcmd => new S'("gnatls"),
451 Switches => List_Switches'Access,
452 Params => new Parameter_Array'(1 => Unlimited_Files),
456 (Cname => new S'("MAKE"),
457 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
458 & "COMPILE /qualifiers)"),
460 Unixcmd => new S'("gnatmake"),
462 Switches => Make_Switches'Access,
463 Params => new Parameter_Array'(1 => Unlimited_Files),
467 (Cname => new S'("METRIC"),
468 Usage => new S'("GNAT METRIC /qualifiers source_file"),
470 Unixcmd => new S'("gnatmetric"),
472 Switches => Metric_Switches'Access,
473 Params => new Parameter_Array'(1 => Unlimited_Files),
477 (Cname => new S'("NAME"),
478 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
479 & "[naming-patterns]"),
481 Unixcmd => new S'("gnatname"),
483 Switches => Name_Switches'Access,
484 Params => new Parameter_Array'(1 => Unlimited_As_Is),
488 (Cname => new S'("PREPROCESS"),
490 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
492 Unixcmd => new S'("gnatprep"),
494 Switches => Prep_Switches'Access,
495 Params => new Parameter_Array'(1 .. 3 => File),
499 (Cname => new S'("PRETTY"),
500 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
502 Unixcmd => new S'("gnatpp"),
504 Switches => Pretty_Switches'Access,
505 Params => new Parameter_Array'(1 => Unlimited_Files),
509 (Cname => new S'("SHARED"),
510 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
511 & "files] /qualifiers"),
513 Unixcmd => new S'("gcc"),
515 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
516 Switches => Shared_Switches'Access,
517 Params => new Parameter_Array'(1 => Unlimited_Files),
521 (Cname => new S'("STACK"),
522 Usage => new S'("GNAT STACK /qualifiers ci_files"),
524 Unixcmd => new S'("gnatstack"),
526 Switches => Stack_Switches'Access,
527 Params => new Parameter_Array'(1 => Unlimited_Files),
528 Defext => "ci" & ASCII.NUL),
531 (Cname => new S'("STUB"),
532 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
534 Unixcmd => new S'("gnatstub"),
536 Switches => Stub_Switches'Access,
537 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
541 (Cname => new S'("XREF"),
542 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
544 Unixcmd => new S'("gnatxref"),
546 Switches => Xref_Switches'Access,
547 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
556 function Invert_Sense (S : String) return VMS_Data.String_Ptr is
557 Sinv : String (1 .. S'Length * 2);
558 -- Result (for sure long enough)
560 Sinvp : Natural := 0;
561 -- Pointer to output string
564 for Sp in S'Range loop
565 if Sp = S'First or else S (Sp - 1) = ',' then
569 Sinv (Sinvp + 1) := '!';
570 Sinv (Sinvp + 2) := S (Sp);
575 Sinv (Sinvp + 1) := S (Sp);
580 return new String'(Sinv (1 .. Sinvp));
583 ----------------------
584 -- Is_Extensionless --
585 ----------------------
587 function Is_Extensionless (F : String) return Boolean is
589 for J in reverse F'Range loop
592 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
598 end Is_Extensionless;
604 function Match (S1, S2 : String) return Boolean is
605 Dif : constant Integer := S2'First - S1'First;
609 if S1'Length /= S2'Length then
613 for J in S1'Range loop
614 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
627 function Match_Prefix (S1, S2 : String) return Boolean is
629 if S1'Length > S2'Length then
632 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
640 function Matching_Name
643 Quiet : Boolean := False) return Item_Ptr
648 -- Little procedure to output command/qualifier/option as appropriate
649 -- and bump error count.
661 Errors := Errors + 1;
666 Put (Standard_Error, "command");
669 if Hostparm.OpenVMS then
670 Put (Standard_Error, "qualifier");
672 Put (Standard_Error, "switch");
676 Put (Standard_Error, "option");
680 Put (Standard_Error, "input");
684 Put (Standard_Error, ": ");
685 Put (Standard_Error, S);
688 -- Start of processing for Matching_Name
691 -- If exact match, that's the one we want
694 while P1 /= null loop
695 if Match (S, P1.Name.all) then
702 -- Now check for prefix matches
705 while P1 /= null loop
706 if P1.Name.all = "/<other>" then
709 elsif not Match_Prefix (S, P1.Name.all) then
713 -- Here we have found one matching prefix, so see if there is
714 -- another one (which is an ambiguity)
717 while P2 /= null loop
718 if Match_Prefix (S, P2.Name.all) then
720 Put (Standard_Error, "ambiguous ");
722 Put (Standard_Error, " (matches ");
723 Put (Standard_Error, P1.Name.all);
725 while P2 /= null loop
726 if Match_Prefix (S, P2.Name.all) then
727 Put (Standard_Error, ',');
728 Put (Standard_Error, P2.Name.all);
734 Put_Line (Standard_Error, ")");
743 -- If we fall through that loop, then there was only one match
749 -- If we fall through outer loop, there was no match
752 Put (Standard_Error, "unrecognized ");
754 New_Line (Standard_Error);
760 -----------------------
761 -- OK_Alphanumerplus --
762 -----------------------
764 function OK_Alphanumerplus (S : String) return Boolean is
770 for J in S'Range loop
771 if not (Is_Alphanumeric (S (J)) or else
772 S (J) = '_' or else S (J) = '$')
780 end OK_Alphanumerplus;
786 function OK_Integer (S : String) return Boolean is
792 for J in S'Range loop
793 if not Is_Digit (S (J)) then
806 procedure Output_Version is
809 Put_Line (Gnatvsn.Gnat_Version_String);
810 Put_Line ("Copyright 1996-" &
812 ", Free Software Foundation, Inc.");
819 procedure Place (C : Character) is
822 Cargs_Buffer.Append (C);
828 procedure Place (S : String) is
830 for J in S'Range loop
839 procedure Place_Lower (S : String) is
841 for J in S'Range loop
842 Place (To_Lower (S (J)));
846 -------------------------
847 -- Place_Unix_Switches --
848 -------------------------
850 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
851 P1, P2, P3 : Natural;
853 Slen, Sln2 : Natural;
854 Wild_Card : Boolean := False;
858 while P1 <= S'Last loop
867 pragma Assert (S (P1) = '-' or else S (P1) = '`');
869 while P2 < S'Last and then S (P2 + 1) /= ',' loop
873 -- Switch is now in S (P1 .. P2)
878 Wild_Card := S (P2) = '*';
886 while P3 <= Buffer.Last - Slen loop
887 if Buffer.Table (P3) = ' '
888 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
892 P3 + Slen = Buffer.Last
894 Buffer.Table (P3 + Slen + 1) = ' ')
899 while P3 + Sln2 /= Buffer.Last
900 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
906 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
907 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
908 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
920 pragma Assert (S (P2) /= '*');
927 Place (S (P1 .. P2));
932 end Place_Unix_Switches;
934 -----------------------------
935 -- Preprocess_Command_Data --
936 -----------------------------
938 procedure Preprocess_Command_Data is
940 for C in Real_Command_Type loop
942 Command : constant Item_Ptr := new Command_Item;
944 Last_Switch : Item_Ptr;
945 -- Last switch in list
948 -- Link new command item into list of commands
950 if Last_Command = null then
953 Last_Command.Next := Command;
956 Last_Command := Command;
958 -- Fill in fields of new command item
960 Command.Name := Command_List (C).Cname;
961 Command.Usage := Command_List (C).Usage;
962 Command.Command := C;
964 if Command_List (C).Unixsws = null then
965 Command.Unix_String := Command_List (C).Unixcmd;
968 Cmd : String (1 .. 5_000);
970 Sws : constant Argument_List_Access :=
971 Command_List (C).Unixsws;
974 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
975 Command_List (C).Unixcmd.all;
976 Last := Command_List (C).Unixcmd'Length;
978 for J in Sws'Range loop
981 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
983 Last := Last + Sws (J)'Length;
986 Command.Unix_String := new String'(Cmd (1 .. Last));
990 Command.Params := Command_List (C).Params;
991 Command.Defext := Command_List (C).Defext;
993 Validate_Command_Or_Option (Command.Name);
995 -- Process the switch list
997 for S in Command_List (C).Switches'Range loop
999 SS : constant VMS_Data.String_Ptr :=
1000 Command_List (C).Switches (S);
1001 P : Natural := SS'First;
1002 Sw : Item_Ptr := new Switch_Item;
1004 Last_Opt : Item_Ptr;
1005 -- Pointer to last option
1008 -- Link new switch item into list of switches
1010 if Last_Switch = null then
1011 Command.Switches := Sw;
1013 Last_Switch.Next := Sw;
1018 -- Process switch string, first get name
1020 while SS (P) /= ' ' and SS (P) /= '=' loop
1024 Sw.Name := new String'(SS (SS'First .. P - 1));
1026 -- Direct translation case
1028 if SS (P) = ' ' then
1029 Sw.Translation := T_Direct;
1030 Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
1031 Validate_Unix_Switch (Sw.Unix_String);
1033 if SS (P - 1) = '>' then
1034 Sw.Translation := T_Other;
1036 elsif SS (P + 1) = '`' then
1039 -- Create the inverted case (/NO ..)
1041 elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
1042 Sw := new Switch_Item;
1043 Last_Switch.Next := Sw;
1047 new String'("/NO" & SS (SS'First + 1 .. P - 1));
1048 Sw.Translation := T_Direct;
1049 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
1050 Validate_Unix_Switch (Sw.Unix_String);
1053 -- Directories translation case
1055 elsif SS (P + 1) = '*' then
1056 pragma Assert (SS (SS'Last) = '*');
1057 Sw.Translation := T_Directories;
1058 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1059 Validate_Unix_Switch (Sw.Unix_String);
1061 -- Directory translation case
1063 elsif SS (P + 1) = '%' then
1064 pragma Assert (SS (SS'Last) = '%');
1065 Sw.Translation := T_Directory;
1066 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1067 Validate_Unix_Switch (Sw.Unix_String);
1069 -- File translation case
1071 elsif SS (P + 1) = '@' then
1072 pragma Assert (SS (SS'Last) = '@');
1073 Sw.Translation := T_File;
1074 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1075 Validate_Unix_Switch (Sw.Unix_String);
1077 -- No space file translation case
1079 elsif SS (P + 1) = '<' then
1080 pragma Assert (SS (SS'Last) = '>');
1081 Sw.Translation := T_No_Space_File;
1082 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1083 Validate_Unix_Switch (Sw.Unix_String);
1085 -- Numeric translation case
1087 elsif SS (P + 1) = '#' then
1088 pragma Assert (SS (SS'Last) = '#');
1089 Sw.Translation := T_Numeric;
1090 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1091 Validate_Unix_Switch (Sw.Unix_String);
1093 -- Alphanumerplus translation case
1095 elsif SS (P + 1) = '|' then
1096 pragma Assert (SS (SS'Last) = '|');
1097 Sw.Translation := T_Alphanumplus;
1098 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1099 Validate_Unix_Switch (Sw.Unix_String);
1101 -- String translation case
1103 elsif SS (P + 1) = '"' then
1104 pragma Assert (SS (SS'Last) = '"');
1105 Sw.Translation := T_String;
1106 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1107 Validate_Unix_Switch (Sw.Unix_String);
1109 -- Commands translation case
1111 elsif SS (P + 1) = '?' then
1112 Sw.Translation := T_Commands;
1113 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));
1115 -- Options translation case
1118 Sw.Translation := T_Options;
1119 Sw.Unix_String := new String'("");
1121 P := P + 1; -- bump past =
1122 while P <= SS'Last loop
1124 Opt : constant Item_Ptr := new Option_Item;
1128 -- Link new option item into options list
1130 if Last_Opt = null then
1133 Last_Opt.Next := Opt;
1138 -- Fill in fields of new option item
1141 while SS (Q) /= ' ' loop
1145 Opt.Name := new String'(SS (P .. Q - 1));
1146 Validate_Command_Or_Option (Opt.Name);
1151 while Q <= SS'Last and then SS (Q) /= ' ' loop
1155 Opt.Unix_String := new String'(SS (P .. Q - 1));
1156 Validate_Unix_Switch (Opt.Unix_String);
1165 end Preprocess_Command_Data;
1167 ----------------------
1168 -- Process_Argument --
1169 ----------------------
1171 procedure Process_Argument (The_Command : in out Command_Type) is
1172 Argv : String_Access;
1175 function Get_Arg_End
1177 Arg_Idx : Integer) return Integer;
1178 -- Begins looking at Arg_Idx + 1 and returns the index of the
1179 -- last character before a slash or else the index of the last
1180 -- character in the string Argv.
1186 function Get_Arg_End
1188 Arg_Idx : Integer) return Integer
1191 for J in Arg_Idx + 1 .. Argv'Last loop
1192 if Argv (J) = '/' then
1200 -- Start of processing for Process_Argument
1205 -- If an argument file is open, read the next non empty line
1207 if Is_Open (Arg_File) then
1209 Line : String (1 .. 256);
1213 Get_Line (Arg_File, Line, Last);
1214 exit when Last /= 0 or else End_Of_File (Arg_File);
1217 -- If the end of the argument file has been reached, close it
1219 if End_Of_File (Arg_File) then
1222 -- If the last line was empty, return after increasing Arg_Num
1223 -- to go to the next argument on the comment line.
1226 Arg_Num := Arg_Num + 1;
1231 Argv := new String'(Line (1 .. Last));
1234 if Argv (1) = '@' then
1235 Put_Line (Standard_Error, "argument file cannot contain @cmd");
1241 -- No argument file is open, get the argument on the command line
1243 Argv := new String'(Argument (Arg_Num));
1244 Arg_Idx := Argv'First;
1246 -- Check if this is the specification of an argument file
1248 if Argv (Arg_Idx) = '@' then
1249 -- The first argument on the command line cannot be an argument
1255 "Cannot specify argument line before command");
1259 -- Open the file, after conversion of the name to canonical form.
1260 -- Fail if file is not found.
1263 Canonical_File_Name : String_Access :=
1264 To_Canonical_File_Spec (Argv (Arg_Idx + 1 .. Argv'Last));
1266 Open (Arg_File, In_File, Canonical_File_Name.all);
1267 Free (Canonical_File_Name);
1272 Put (Standard_Error, "Cannot open argument file """);
1273 Put (Standard_Error, Argv (Arg_Idx + 1 .. Argv'Last));
1274 Put_Line (Standard_Error, """");
1280 <<Tryagain_After_Coalesce>>
1283 Next_Arg_Idx : Integer;
1284 Arg : String_Access;
1287 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1288 Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1290 -- The first one must be a command name
1292 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1293 Command := Matching_Name (Arg.all, Commands);
1295 if Command = null then
1299 The_Command := Command.Command;
1300 Output_File_Expected := False;
1302 -- Give usage information if only command given
1304 if Argument_Count = 1
1305 and then Next_Arg_Idx = Argv'Last
1310 ("List of available qualifiers and options");
1313 Put (Command.Usage.all);
1315 Put_Line (Command.Unix_String.all);
1318 Sw : Item_Ptr := Command.Switches;
1321 while Sw /= null loop
1325 case Sw.Translation is
1329 Put_Line (Sw.Unix_String.all &
1334 Put_Line (Sw.Unix_String.all);
1336 when T_Directories =>
1337 Put ("=(direc,direc,..direc)");
1339 Put (Sw.Unix_String.all);
1341 Put (Sw.Unix_String.all);
1342 Put_Line (" direc ...");
1347 Put (Sw.Unix_String.all);
1349 if Sw.Unix_String (Sw.Unix_String'Last)
1355 Put_Line ("directory ");
1357 when T_File | T_No_Space_File =>
1360 Put (Sw.Unix_String.all);
1362 if Sw.Translation = T_File
1363 and then Sw.Unix_String
1364 (Sw.Unix_String'Last) /= '='
1376 (Sw.Unix_String'First) = '`'
1379 (Sw.Unix_String'First + 1
1380 .. Sw.Unix_String'Last));
1382 Put (Sw.Unix_String.all);
1387 when T_Alphanumplus =>
1392 (Sw.Unix_String'First) = '`'
1395 (Sw.Unix_String'First + 1
1396 .. Sw.Unix_String'Last));
1398 Put (Sw.Unix_String.all);
1410 Put (Sw.Unix_String.all);
1413 (Sw.Unix_String'Last) /= '='
1422 Put (" (switches for ");
1424 (Sw.Unix_String'First + 7
1425 .. Sw.Unix_String'Last));
1429 (Sw.Unix_String'First
1430 .. Sw.Unix_String'First + 5));
1431 Put_Line (" switches");
1435 Opt : Item_Ptr := Sw.Options;
1438 Put_Line ("=(option,option..)");
1440 while Opt /= null loop
1444 if Opt = Sw.Options then
1449 Put_Line (Opt.Unix_String.all);
1463 -- Special handling for internal debugging switch /?
1465 elsif Arg.all = "/?" then
1466 Display_Command := True;
1467 Output_File_Expected := False;
1469 -- Special handling of internal option /KEEP_TEMPORARY_FILES
1471 elsif Arg'Length >= 7
1472 and then Matching_Name
1473 (Arg.all, Keep_Temps_Option, True) /= null
1475 Opt.Keep_Temporary_Files := True;
1477 -- Copy -switch unchanged, as well as +rule
1479 elsif Arg (Arg'First) = '-' or else Arg (Arg'First) = '+' then
1483 -- Set Output_File_Expected for the next argument
1485 Output_File_Expected :=
1486 Arg.all = "-o" and then The_Command = Link;
1488 -- Copy quoted switch with quotes stripped
1490 elsif Arg (Arg'First) = '"' then
1491 if Arg (Arg'Last) /= '"' then
1492 Put (Standard_Error, "misquoted argument: ");
1493 Put_Line (Standard_Error, Arg.all);
1494 Errors := Errors + 1;
1498 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1501 Output_File_Expected := False;
1503 -- Parameter Argument
1505 elsif Arg (Arg'First) /= '/'
1506 and then Make_Commands_Active = null
1508 Param_Count := Param_Count + 1;
1510 if Param_Count <= Command.Params'Length then
1512 case Command.Params (Param_Count) is
1514 when File | Optional_File =>
1516 Normal_File : constant String_Access :=
1517 To_Canonical_File_Spec
1522 Place_Lower (Normal_File.all);
1524 if Is_Extensionless (Normal_File.all)
1525 and then Command.Defext /= " "
1528 Place (Command.Defext);
1532 when Unlimited_Files =>
1534 Normal_File : constant String_Access :=
1535 To_Canonical_File_Spec
1538 File_Is_Wild : Boolean := False;
1539 File_List : String_Access_List_Access;
1542 for J in Arg'Range loop
1544 or else Arg (J) = '%'
1546 File_Is_Wild := True;
1550 if File_Is_Wild then
1551 File_List := To_Canonical_File_List
1554 for J in File_List.all'Range loop
1556 Place_Lower (File_List.all (J).all);
1561 Place_Lower (Normal_File.all);
1563 -- Add extension if not present, except after
1566 if Is_Extensionless (Normal_File.all)
1567 and then Command.Defext /= " "
1568 and then not Output_File_Expected
1571 Place (Command.Defext);
1575 Param_Count := Param_Count - 1;
1582 when Unlimited_As_Is =>
1585 Param_Count := Param_Count - 1;
1587 when Files_Or_Wildcard =>
1589 -- Remove spaces from a comma separated list
1590 -- of file names and adjust control variables
1593 while Arg_Num < Argument_Count and then
1594 (Argv (Argv'Last) = ',' xor
1595 Argument (Arg_Num + 1)
1596 (Argument (Arg_Num + 1)'First) = ',')
1599 (Argv.all & Argument (Arg_Num + 1));
1600 Arg_Num := Arg_Num + 1;
1601 Arg_Idx := Argv'First;
1603 Get_Arg_End (Argv.all, Arg_Idx);
1605 (Argv (Arg_Idx .. Next_Arg_Idx));
1608 -- Parse the comma separated list of VMS
1609 -- filenames and place them on the command
1610 -- line as space separated Unix style
1611 -- filenames. Lower case and add default
1612 -- extension as appropriate.
1615 Arg1_Idx : Integer := Arg'First;
1617 function Get_Arg1_End
1619 Arg_Idx : Integer) return Integer;
1620 -- Begins looking at Arg_Idx + 1 and
1621 -- returns the index of the last character
1622 -- before a comma or else the index of the
1623 -- last character in the string Arg.
1629 function Get_Arg1_End
1631 Arg_Idx : Integer) return Integer
1634 for J in Arg_Idx + 1 .. Arg'Last loop
1635 if Arg (J) = ',' then
1648 Get_Arg1_End (Arg.all, Arg1_Idx);
1652 Arg (Arg1_Idx .. Next_Arg1_Idx);
1655 constant String_Access :=
1656 To_Canonical_File_Spec (Arg1);
1660 Place_Lower (Normal_File.all);
1662 if Is_Extensionless (Normal_File.all)
1663 and then Command.Defext /= " "
1666 Place (Command.Defext);
1669 Arg1_Idx := Next_Arg1_Idx + 1;
1672 exit when Arg1_Idx > Arg'Last;
1674 -- Don't allow two or more commas in
1677 if Arg (Arg1_Idx) = ',' then
1678 Arg1_Idx := Arg1_Idx + 1;
1679 if Arg1_Idx > Arg'Last or else
1680 Arg (Arg1_Idx) = ','
1684 "Malformed Parameter: " &
1686 Put (Standard_Error, "usage: ");
1687 Put_Line (Standard_Error,
1698 -- Reset Output_File_Expected, in case it was True
1700 Output_File_Expected := False;
1702 -- Qualifier argument
1705 Output_File_Expected := False;
1707 Cargs := Command.Name.all = "COMPILE";
1709 -- This code is too heavily nested, should be
1710 -- separated out as separate subprogram ???
1716 Endp : Natural := 0; -- avoid warning!
1721 while SwP < Arg'Last
1722 and then Arg (SwP + 1) /= '='
1727 -- At this point, the switch name is in
1728 -- Arg (Arg'First..SwP) and if that is not the
1729 -- whole switch, then there is an equal sign at
1730 -- Arg (SwP + 1) and the rest of Arg is what comes
1731 -- after the equal sign.
1733 -- If make commands are active, see if we have
1734 -- another COMMANDS_TRANSLATION switch belonging
1737 if Make_Commands_Active /= null then
1740 (Arg (Arg'First .. SwP),
1745 and then Sw.Translation = T_Commands
1752 (Arg (Arg'First .. SwP),
1753 Make_Commands_Active.Switches,
1757 -- For case of GNAT MAKE or CHOP, if we cannot
1758 -- find the switch, then see if it is a
1759 -- recognized compiler switch instead, and if
1760 -- so process the compiler switch.
1762 elsif Command.Name.all = "MAKE"
1763 or else Command.Name.all = "CHOP" then
1766 (Arg (Arg'First .. SwP),
1773 (Arg (Arg'First .. SwP),
1775 ("COMPILE", Commands).Switches,
1779 -- For all other cases, just search the relevant
1785 (Arg (Arg'First .. SwP),
1791 case Sw.Translation is
1794 Place_Unix_Switches (Sw.Unix_String);
1796 and then Arg (SwP + 1) = '='
1798 Put (Standard_Error,
1799 "qualifier options ignored: ");
1800 Put_Line (Standard_Error, Arg.all);
1803 when T_Directories =>
1804 if SwP + 1 > Arg'Last then
1805 Put (Standard_Error,
1806 "missing directories for: ");
1807 Put_Line (Standard_Error, Arg.all);
1808 Errors := Errors + 1;
1810 elsif Arg (SwP + 2) /= '(' then
1814 elsif Arg (Arg'Last) /= ')' then
1816 -- Remove spaces from a comma separated
1817 -- list of file names and adjust
1818 -- control variables accordingly.
1820 if Arg_Num < Argument_Count and then
1821 (Argv (Argv'Last) = ',' xor
1822 Argument (Arg_Num + 1)
1823 (Argument (Arg_Num + 1)'First) = ',')
1826 new String'(Argv.all
1829 Arg_Num := Arg_Num + 1;
1830 Arg_Idx := Argv'First;
1832 Get_Arg_End (Argv.all, Arg_Idx);
1834 (Argv (Arg_Idx .. Next_Arg_Idx));
1835 goto Tryagain_After_Coalesce;
1838 Put (Standard_Error,
1839 "incorrectly parenthesized " &
1840 "or malformed argument: ");
1841 Put_Line (Standard_Error, Arg.all);
1842 Errors := Errors + 1;
1846 Endp := Arg'Last - 1;
1849 while SwP <= Endp loop
1851 Dir_Is_Wild : Boolean := False;
1852 Dir_Maybe_Is_Wild : Boolean := False;
1854 Dir_List : String_Access_List_Access;
1860 and then Arg (P2 + 1) /= ','
1862 -- A wildcard directory spec on
1863 -- VMS will contain either * or
1866 if Arg (P2) = '*' then
1867 Dir_Is_Wild := True;
1869 elsif Arg (P2) = '%' then
1870 Dir_Is_Wild := True;
1872 elsif Dir_Maybe_Is_Wild
1873 and then Arg (P2) = '.'
1874 and then Arg (P2 + 1) = '.'
1876 Dir_Is_Wild := True;
1877 Dir_Maybe_Is_Wild := False;
1879 elsif Dir_Maybe_Is_Wild then
1880 Dir_Maybe_Is_Wild := False;
1882 elsif Arg (P2) = '.'
1883 and then Arg (P2 + 1) = '.'
1885 Dir_Maybe_Is_Wild := True;
1894 To_Canonical_File_List
1895 (Arg (SwP .. P2), True);
1897 for J in Dir_List.all'Range loop
1901 (Dir_List.all (J).all);
1908 (To_Canonical_Dir_Spec
1909 (Arg (SwP .. P2), False).all);
1917 if SwP + 1 > Arg'Last then
1918 Put (Standard_Error,
1919 "missing directory for: ");
1920 Put_Line (Standard_Error, Arg.all);
1921 Errors := Errors + 1;
1924 Place_Unix_Switches (Sw.Unix_String);
1926 -- Some switches end in "=". No space
1930 (Sw.Unix_String'Last) /= '='
1936 (To_Canonical_Dir_Spec
1937 (Arg (SwP + 2 .. Arg'Last),
1941 when T_File | T_No_Space_File =>
1942 if SwP + 1 > Arg'Last then
1943 Put (Standard_Error,
1944 "missing file for: ");
1945 Put_Line (Standard_Error, Arg.all);
1946 Errors := Errors + 1;
1949 Place_Unix_Switches (Sw.Unix_String);
1951 -- Some switches end in "=". No space
1954 if Sw.Translation = T_File
1955 and then Sw.Unix_String
1956 (Sw.Unix_String'Last) /= '='
1962 (To_Canonical_File_Spec
1963 (Arg (SwP + 2 .. Arg'Last)).all);
1967 if OK_Integer (Arg (SwP + 2 .. Arg'Last)) then
1968 Place_Unix_Switches (Sw.Unix_String);
1969 Place (Arg (SwP + 2 .. Arg'Last));
1972 Put (Standard_Error, "argument for ");
1973 Put (Standard_Error, Sw.Name.all);
1975 (Standard_Error, " must be numeric");
1976 Errors := Errors + 1;
1979 when T_Alphanumplus =>
1980 if OK_Alphanumerplus
1981 (Arg (SwP + 2 .. Arg'Last))
1983 Place_Unix_Switches (Sw.Unix_String);
1984 Place (Arg (SwP + 2 .. Arg'Last));
1987 Put (Standard_Error, "argument for ");
1988 Put (Standard_Error, Sw.Name.all);
1989 Put_Line (Standard_Error,
1990 " must be alphanumeric");
1991 Errors := Errors + 1;
1996 -- A String value must be extended to the
1997 -- end of the Argv, otherwise strings like
1998 -- "foo/bar" get split at the slash.
2000 -- The begining and ending of the string
2001 -- are flagged with embedded nulls which
2002 -- are removed when building the Spawn
2003 -- call. Nulls are use because they won't
2004 -- show up in a /? output. Quotes aren't
2005 -- used because that would make it
2006 -- difficult to embed them.
2008 Place_Unix_Switches (Sw.Unix_String);
2010 if Next_Arg_Idx /= Argv'Last then
2011 Next_Arg_Idx := Argv'Last;
2013 (Argv (Arg_Idx .. Next_Arg_Idx));
2016 while SwP < Arg'Last and then
2017 Arg (SwP + 1) /= '=' loop
2023 Place (Arg (SwP + 2 .. Arg'Last));
2028 -- Output -largs/-bargs/-cargs
2031 Place (Sw.Unix_String
2032 (Sw.Unix_String'First ..
2033 Sw.Unix_String'First + 5));
2036 (Sw.Unix_String'First + 7 ..
2037 Sw.Unix_String'Last) = "MAKE"
2039 Make_Commands_Active := null;
2042 -- Set source of new commands, also
2043 -- setting this non-null indicates that
2044 -- we are in the special commands mode
2045 -- for processing the -xargs case.
2047 Make_Commands_Active :=
2050 (Sw.Unix_String'First + 7 ..
2051 Sw.Unix_String'Last),
2056 if SwP + 1 > Arg'Last then
2058 (Sw.Options.Unix_String);
2061 elsif Arg (SwP + 2) /= '(' then
2065 elsif Arg (Arg'Last) /= ')' then
2066 Put (Standard_Error,
2067 "incorrectly parenthesized argument: ");
2068 Put_Line (Standard_Error, Arg.all);
2069 Errors := Errors + 1;
2074 Endp := Arg'Last - 1;
2077 while SwP <= Endp loop
2081 and then Arg (P2 + 1) /= ','
2086 -- Option name is in Arg (SwP .. P2)
2088 Opt := Matching_Name (Arg (SwP .. P2),
2101 (new String'(Sw.Unix_String.all &
2109 Arg_Idx := Next_Arg_Idx + 1;
2112 exit when Arg_Idx > Argv'Last;
2116 if not Is_Open (Arg_File) then
2117 Arg_Num := Arg_Num + 1;
2119 end Process_Argument;
2121 --------------------
2122 -- Process_Buffer --
2123 --------------------
2125 procedure Process_Buffer (S : String) is
2127 Inside_Nul : Boolean := False;
2128 Arg : String (1 .. 1024);
2133 while P1 <= S'Last and then S (P1) = ' ' loop
2138 Arg (Arg_Ctr) := S (P1);
2140 while P1 <= S'Last loop
2141 if S (P1) = ASCII.NUL then
2143 Inside_Nul := False;
2149 if S (P1) = ' ' and then not Inside_Nul then
2151 Arg_Ctr := Arg_Ctr + 1;
2152 Arg (Arg_Ctr) := S (P1);
2155 Last_Switches.Increment_Last;
2159 and then (S (P2 + 1) /= ' ' or else
2163 Arg_Ctr := Arg_Ctr + 1;
2164 Arg (Arg_Ctr) := S (P2);
2165 if S (P2) = ASCII.NUL then
2166 Arg_Ctr := Arg_Ctr - 1;
2169 Inside_Nul := False;
2176 Last_Switches.Table (Last_Switches.Last) :=
2177 new String'(String (Arg (1 .. Arg_Ctr)));
2180 exit when P1 > S'Last;
2183 Arg (Arg_Ctr) := S (P1);
2188 --------------------------------
2189 -- Validate_Command_Or_Option --
2190 --------------------------------
2192 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
2194 pragma Assert (N'Length > 0);
2196 for J in N'Range loop
2198 pragma Assert (N (J - 1) /= '_');
2201 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
2205 end Validate_Command_Or_Option;
2207 --------------------------
2208 -- Validate_Unix_Switch --
2209 --------------------------
2211 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
2213 if S (S'First) = '`' then
2217 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
2219 for J in S'First + 1 .. S'Last loop
2220 pragma Assert (S (J) /= ' ');
2223 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
2227 end Validate_Unix_Switch;
2229 --------------------
2230 -- VMS_Conversion --
2231 --------------------
2233 procedure VMS_Conversion (The_Command : out Command_Type) is
2234 Result : Command_Type := Undefined;
2235 Result_Set : Boolean := False;
2240 -- First we must preprocess the string form of the command and options
2241 -- list into the internal form that we use.
2243 Preprocess_Command_Data;
2245 -- If no parameters, give complete list of commands
2247 if Argument_Count = 0 then
2250 Put_Line ("List of available commands");
2253 while Commands /= null loop
2254 Put (Commands.Usage.all);
2256 Put_Line (Commands.Unix_String.all);
2257 Commands := Commands.Next;
2263 -- Loop through arguments
2266 while Arg_Num <= Argument_Count loop
2267 Process_Argument (Result);
2269 if not Result_Set then
2270 The_Command := Result;
2275 -- Gross error checking that the number of parameters is correct.
2276 -- Not applicable to Unlimited_Files parameters.
2278 if (Param_Count = Command.Params'Length - 1
2279 and then Command.Params (Param_Count + 1) = Unlimited_Files)
2280 or else Param_Count <= Command.Params'Length
2285 Put_Line (Standard_Error,
2286 "Parameter count of "
2287 & Integer'Image (Param_Count)
2288 & " not equal to expected "
2289 & Integer'Image (Command.Params'Length));
2290 Put (Standard_Error, "usage: ");
2291 Put_Line (Standard_Error, Command.Usage.all);
2292 Errors := Errors + 1;
2298 -- Prepare arguments for a call to spawn, filtering out
2299 -- embedded nulls place there to delineate strings.
2301 Process_Buffer (String (Buffer.Table (1 .. Buffer.Last)));
2303 if Cargs_Buffer.Last > 1 then
2304 Last_Switches.Append (new String'("-cargs"));
2306 (String (Cargs_Buffer.Table (1 .. Cargs_Buffer.Last)));