Merge -r 127928:132243 from trunk
[official-gcc.git] / gcc / ada / vms_conv.adb
blobefd3ab1bd791f7fa10554b30032dbdf07719f890
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- V M S _ C O N V --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1996-2007, Free Software Foundation, Inc. --
10 -- --
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 3, 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Gnatvsn; use Gnatvsn;
27 with Hostparm;
28 with Opt;
29 with Osint; use Osint;
30 with Targparm; use Targparm;
32 with Ada.Characters.Handling; use Ada.Characters.Handling;
33 with Ada.Command_Line; use Ada.Command_Line;
34 with Ada.Text_IO; use Ada.Text_IO;
36 package body VMS_Conv is
38 -------------------------
39 -- Internal Structures --
40 -------------------------
42 -- The switches and commands are defined by strings in the previous
43 -- section so that they are easy to modify, but internally, they are
44 -- kept in a more conveniently accessible form described in this
45 -- section.
47 -- Commands, command qualifers and options have a similar common format
48 -- so that searching for matching names can be done in a common manner.
50 type Item_Id is (Id_Command, Id_Switch, Id_Option);
52 type Translation_Type is
54 T_Direct,
55 -- A qualifier with no options.
56 -- Example: GNAT MAKE /VERBOSE
58 T_Directories,
59 -- A qualifier followed by a list of directories
60 -- Example: GNAT COMPILE /SEARCH=([], [.FOO], [.BAR])
62 T_Directory,
63 -- A qualifier followed by one directory
64 -- Example: GNAT LIBRARY /SET=[.VAXFLOATLIB]
66 T_File,
67 -- A qualifier followed by a filename
68 -- Example: GNAT LINK /EXECUTABLE=FOO.EXE
70 T_No_Space_File,
71 -- A qualifier followed by a filename
72 -- Example: GNAT MAKE /PROJECT_FILE=PRJ.GPR
74 T_Numeric,
75 -- A qualifier followed by a numeric value.
76 -- Example: GNAT CHOP /FILE_NAME_MAX_LENGTH=39
78 T_String,
79 -- A qualifier followed by a quoted string. Only used by
80 -- /IDENTIFICATION qualifier.
81 -- Example: GNAT LINK /IDENTIFICATION="3.14a1 version"
83 T_Options,
84 -- A qualifier followed by a list of options.
85 -- Example: GNAT COMPILE /REPRESENTATION_INFO=(ARRAYS,OBJECTS)
87 T_Commands,
88 -- A qualifier followed by a list. Only used for
89 -- MAKE /COMPILER_QUALIFIERS /BINDER_QUALIFIERS /LINKER_QUALIFIERS
90 -- (gnatmake -cargs -bargs -largs )
91 -- Example: GNAT MAKE ... /LINKER_QUALIFIERS /VERBOSE FOOBAR.OBJ
93 T_Other,
94 -- A qualifier passed directly to the linker. Only used
95 -- for LINK and SHARED if no other match is found.
96 -- Example: GNAT LINK FOO.ALI /SYSSHR
98 T_Alphanumplus
99 -- A qualifier followed by a legal linker symbol prefix. Only used
100 -- for BIND /BUILD_LIBRARY (gnatbind -Lxyz).
101 -- Example: GNAT BIND /BUILD_LIBRARY=foobar
104 type Item (Id : Item_Id);
105 type Item_Ptr is access all Item;
107 type Item (Id : Item_Id) is record
108 Name : String_Ptr;
109 -- Name of the command, switch (with slash) or option
111 Next : Item_Ptr;
112 -- Pointer to next item on list, always has the same Id value
114 Command : Command_Type := Undefined;
116 Unix_String : String_Ptr := null;
117 -- Corresponding Unix string. For a command, this is the unix command
118 -- name and possible default switches. For a switch or option it is
119 -- the unix switch string.
121 case Id is
123 when Id_Command =>
125 Switches : Item_Ptr;
126 -- Pointer to list of switch items for the command, linked
127 -- through the Next fields with null terminating the list.
129 Usage : String_Ptr;
130 -- Usage information, used only for errors and the default
131 -- list of commands output.
133 Params : Parameter_Ref;
134 -- Array of parameters
136 Defext : String (1 .. 3);
137 -- Default extension. If non-blank, then this extension is
138 -- supplied by default as the extension for any file parameter
139 -- which does not have an extension already.
141 when Id_Switch =>
143 Translation : Translation_Type;
144 -- Type of switch translation. For all cases, except Options,
145 -- this is the only field needed, since the Unix translation
146 -- is found in Unix_String.
148 Options : Item_Ptr;
149 -- For the Options case, this field is set to point to a list
150 -- of options item (for this case Unix_String is null in the
151 -- main switch item). The end of the list is marked by null.
153 when Id_Option =>
155 null;
156 -- No special fields needed, since Name and Unix_String are
157 -- sufficient to completely described an option.
159 end case;
160 end record;
162 subtype Command_Item is Item (Id_Command);
163 subtype Switch_Item is Item (Id_Switch);
164 subtype Option_Item is Item (Id_Option);
166 Keep_Temps_Option : constant Item_Ptr :=
167 new Item'
168 (Id => Id_Option,
169 Name =>
170 new String'("/KEEP_TEMPORARY_FILES"),
171 Next => null,
172 Command => Undefined,
173 Unix_String => null);
175 Param_Count : Natural := 0;
176 -- Number of parameter arguments so far
178 Arg_Num : Natural;
179 -- Argument number
181 Arg_File : Ada.Text_IO.File_Type;
182 -- A file where arguments are read from
184 Commands : Item_Ptr;
185 -- Pointer to head of list of command items, one for each command, with
186 -- the end of the list marked by a null pointer.
188 Last_Command : Item_Ptr;
189 -- Pointer to last item in Commands list
191 Command : Item_Ptr;
192 -- Pointer to command item for current command
194 Make_Commands_Active : Item_Ptr := null;
195 -- Set to point to Command entry for COMPILE, BIND, or LINK as appropriate
196 -- if a COMMANDS_TRANSLATION switch has been encountered while processing
197 -- a MAKE Command.
199 Output_File_Expected : Boolean := False;
200 -- True for GNAT LINK after -o switch, so that the ".ali" extension is
201 -- not added to the executable file name.
203 package Buffer is new Table.Table
204 (Table_Component_Type => Character,
205 Table_Index_Type => Integer,
206 Table_Low_Bound => 1,
207 Table_Initial => 4096,
208 Table_Increment => 100,
209 Table_Name => "Buffer");
210 -- Table to store the command to be used
212 package Cargs_Buffer is new Table.Table
213 (Table_Component_Type => Character,
214 Table_Index_Type => Integer,
215 Table_Low_Bound => 1,
216 Table_Initial => 4096,
217 Table_Increment => 100,
218 Table_Name => "Cargs_Buffer");
219 -- Table to store the compiler switches for GNAT COMPILE
221 Cargs : Boolean := False;
222 -- When True, commands should go to Cargs_Buffer instead of Buffer table
224 function Init_Object_Dirs return Argument_List;
225 -- Get the list of the object directories
227 function Invert_Sense (S : String) return VMS_Data.String_Ptr;
228 -- Given a unix switch string S, computes the inverse (adding or
229 -- removing ! characters as required), and returns a pointer to
230 -- the allocated result on the heap.
232 function Is_Extensionless (F : String) return Boolean;
233 -- Returns true if the filename has no extension
235 function Match (S1, S2 : String) return Boolean;
236 -- Determines whether S1 and S2 match (this is a case insensitive match)
238 function Match_Prefix (S1, S2 : String) return Boolean;
239 -- Determines whether S1 matches a prefix of S2. This is also a case
240 -- insensitive match (for example Match ("AB","abc") is True).
242 function Matching_Name
243 (S : String;
244 Itm : Item_Ptr;
245 Quiet : Boolean := False) return Item_Ptr;
246 -- Determines if the item list headed by Itm and threaded through the
247 -- Next fields (with null marking the end of the list), contains an
248 -- entry that uniquely matches the given string. The match is case
249 -- insensitive and permits unique abbreviation. If the match succeeds,
250 -- then a pointer to the matching item is returned. Otherwise, an
251 -- appropriate error message is written. Note that the discriminant
252 -- of Itm is used to determine the appropriate form of this message.
253 -- Quiet is normally False as shown, if it is set to True, then no
254 -- error message is generated in a not found situation (null is still
255 -- returned to indicate the not-found situation).
257 function OK_Alphanumerplus (S : String) return Boolean;
258 -- Checks that S is a string of alphanumeric characters,
259 -- returning True if all alphanumeric characters,
260 -- False if empty or a non-alphanumeric character is present.
262 function OK_Integer (S : String) return Boolean;
263 -- Checks that S is a string of digits, returning True if all digits,
264 -- False if empty or a non-digit is present.
266 procedure Place (C : Character);
267 -- Place a single character in the buffer, updating Ptr
269 procedure Place (S : String);
270 -- Place a string character in the buffer, updating Ptr
272 procedure Place_Lower (S : String);
273 -- Place string in buffer, forcing letters to lower case, updating Ptr
275 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr);
276 -- Given a unix switch string, place corresponding switches in Buffer,
277 -- updating Ptr appropriatelly. Note that in the case of use of ! the
278 -- result may be to remove a previously placed switch.
280 procedure Preprocess_Command_Data;
281 -- Preprocess the string form of the command and options list into the
282 -- internal form.
284 procedure Process_Argument (The_Command : in out Command_Type);
285 -- Process one argument from the command line, or one line from
286 -- from a command line file. For the first call, set The_Command.
288 procedure Process_Buffer (S : String);
289 -- Process the characters in the Buffer table or the Cargs_Buffer table
290 -- to convert these into arguments.
292 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr);
293 -- Check that N is a valid command or option name, i.e. that it is of the
294 -- form of an Ada identifier with upper case letters and underscores.
296 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr);
297 -- Check that S is a valid switch string as described in the syntax for
298 -- the switch table item UNIX_SWITCH or else begins with a backquote.
300 ----------------------
301 -- Init_Object_Dirs --
302 ----------------------
304 function Init_Object_Dirs return Argument_List is
305 Object_Dirs : Integer;
306 Object_Dir : Argument_List (1 .. 256);
307 Object_Dir_Name : String_Access;
309 begin
310 Object_Dirs := 0;
311 Object_Dir_Name := new String'(Object_Dir_Default_Prefix);
312 Get_Next_Dir_In_Path_Init (Object_Dir_Name);
314 loop
315 declare
316 Dir : constant String_Access :=
317 String_Access (Get_Next_Dir_In_Path (Object_Dir_Name));
318 begin
319 exit when Dir = null;
320 Object_Dirs := Object_Dirs + 1;
321 Object_Dir (Object_Dirs) :=
322 new String'("-L" &
323 To_Canonical_Dir_Spec
324 (To_Host_Dir_Spec
325 (Normalize_Directory_Name (Dir.all).all,
326 True).all, True).all);
327 end;
328 end loop;
330 Object_Dirs := Object_Dirs + 1;
331 Object_Dir (Object_Dirs) := new String'("-lgnat");
333 if OpenVMS_On_Target then
334 Object_Dirs := Object_Dirs + 1;
335 Object_Dir (Object_Dirs) := new String'("-ldecgnat");
336 end if;
338 return Object_Dir (1 .. Object_Dirs);
339 end Init_Object_Dirs;
341 ----------------
342 -- Initialize --
343 ----------------
345 procedure Initialize is
346 begin
347 Command_List :=
348 (Bind =>
349 (Cname => new S'("BIND"),
350 Usage => new S'("GNAT BIND file[.ali] /qualifiers"),
351 VMS_Only => False,
352 Unixcmd => new S'("gnatbind"),
353 Unixsws => null,
354 Switches => Bind_Switches'Access,
355 Params => new Parameter_Array'(1 => Unlimited_Files),
356 Defext => "ali"),
358 Chop =>
359 (Cname => new S'("CHOP"),
360 Usage => new S'("GNAT CHOP file [directory] /qualifiers"),
361 VMS_Only => False,
362 Unixcmd => new S'("gnatchop"),
363 Unixsws => null,
364 Switches => Chop_Switches'Access,
365 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
366 Defext => " "),
368 Clean =>
369 (Cname => new S'("CLEAN"),
370 Usage => new S'("GNAT CLEAN /qualifiers files"),
371 VMS_Only => False,
372 Unixcmd => new S'("gnatclean"),
373 Unixsws => null,
374 Switches => Clean_Switches'Access,
375 Params => new Parameter_Array'(1 => File),
376 Defext => " "),
378 Compile =>
379 (Cname => new S'("COMPILE"),
380 Usage => new S'("GNAT COMPILE filespec[,...] /qualifiers"),
381 VMS_Only => False,
382 Unixcmd => new S'("gnatmake"),
383 Unixsws => new Argument_List'(1 => new String'("-f"),
384 2 => new String'("-u"),
385 3 => new String'("-c")),
386 Switches => GCC_Switches'Access,
387 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
388 Defext => " "),
390 Check =>
391 (Cname => new S'("CHECK"),
392 Usage => new S'("GNAT CHECK name /qualifiers"),
393 VMS_Only => False,
394 Unixcmd => new S'("gnatcheck"),
395 Unixsws => null,
396 Switches => Check_Switches'Access,
397 Params => new Parameter_Array'(1 => Unlimited_Files),
398 Defext => " "),
400 Elim =>
401 (Cname => new S'("ELIM"),
402 Usage => new S'("GNAT ELIM name /qualifiers"),
403 VMS_Only => False,
404 Unixcmd => new S'("gnatelim"),
405 Unixsws => null,
406 Switches => Elim_Switches'Access,
407 Params => new Parameter_Array'(1 => Other_As_Is),
408 Defext => "ali"),
410 Find =>
411 (Cname => new S'("FIND"),
412 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
413 & "[:column]]] filespec[,...] /qualifiers"),
414 VMS_Only => False,
415 Unixcmd => new S'("gnatfind"),
416 Unixsws => null,
417 Switches => Find_Switches'Access,
418 Params => new Parameter_Array'(1 => Other_As_Is,
419 2 => Files_Or_Wildcard),
420 Defext => "ali"),
422 Krunch =>
423 (Cname => new S'("KRUNCH"),
424 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
425 VMS_Only => False,
426 Unixcmd => new S'("gnatkr"),
427 Unixsws => null,
428 Switches => Krunch_Switches'Access,
429 Params => new Parameter_Array'(1 => File),
430 Defext => " "),
432 Link =>
433 (Cname => new S'("LINK"),
434 Usage => new S'("GNAT LINK file[.ali]"
435 & " [extra obj_&_lib_&_exe_&_opt files]"
436 & " /qualifiers"),
437 VMS_Only => False,
438 Unixcmd => new S'("gnatlink"),
439 Unixsws => null,
440 Switches => Link_Switches'Access,
441 Params => new Parameter_Array'(1 => Unlimited_Files),
442 Defext => "ali"),
444 List =>
445 (Cname => new S'("LIST"),
446 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
447 VMS_Only => False,
448 Unixcmd => new S'("gnatls"),
449 Unixsws => null,
450 Switches => List_Switches'Access,
451 Params => new Parameter_Array'(1 => Unlimited_Files),
452 Defext => "ali"),
454 Make =>
455 (Cname => new S'("MAKE"),
456 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
457 & "COMPILE /qualifiers)"),
458 VMS_Only => False,
459 Unixcmd => new S'("gnatmake"),
460 Unixsws => null,
461 Switches => Make_Switches'Access,
462 Params => new Parameter_Array'(1 => Unlimited_Files),
463 Defext => " "),
465 Metric =>
466 (Cname => new S'("METRIC"),
467 Usage => new S'("GNAT METRIC /qualifiers source_file"),
468 VMS_Only => False,
469 Unixcmd => new S'("gnatmetric"),
470 Unixsws => null,
471 Switches => Metric_Switches'Access,
472 Params => new Parameter_Array'(1 => Unlimited_Files),
473 Defext => " "),
475 Name =>
476 (Cname => new S'("NAME"),
477 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
478 & "[naming-patterns]"),
479 VMS_Only => False,
480 Unixcmd => new S'("gnatname"),
481 Unixsws => null,
482 Switches => Name_Switches'Access,
483 Params => new Parameter_Array'(1 => Unlimited_As_Is),
484 Defext => " "),
486 Preprocess =>
487 (Cname => new S'("PREPROCESS"),
488 Usage =>
489 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
490 VMS_Only => False,
491 Unixcmd => new S'("gnatprep"),
492 Unixsws => null,
493 Switches => Prep_Switches'Access,
494 Params => new Parameter_Array'(1 .. 3 => File),
495 Defext => " "),
497 Pretty =>
498 (Cname => new S'("PRETTY"),
499 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
500 VMS_Only => False,
501 Unixcmd => new S'("gnatpp"),
502 Unixsws => null,
503 Switches => Pretty_Switches'Access,
504 Params => new Parameter_Array'(1 => Unlimited_Files),
505 Defext => " "),
507 Shared =>
508 (Cname => new S'("SHARED"),
509 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
510 & "files] /qualifiers"),
511 VMS_Only => True,
512 Unixcmd => new S'("gcc"),
513 Unixsws =>
514 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
515 Switches => Shared_Switches'Access,
516 Params => new Parameter_Array'(1 => Unlimited_Files),
517 Defext => " "),
519 Stack =>
520 (Cname => new S'("STACK"),
521 Usage => new S'("GNAT STACK /qualifiers ci_files"),
522 VMS_Only => False,
523 Unixcmd => new S'("gnatstack"),
524 Unixsws => null,
525 Switches => Stack_Switches'Access,
526 Params => new Parameter_Array'(1 => Unlimited_Files),
527 Defext => "ci" & ASCII.NUL),
529 Stub =>
530 (Cname => new S'("STUB"),
531 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
532 VMS_Only => False,
533 Unixcmd => new S'("gnatstub"),
534 Unixsws => null,
535 Switches => Stub_Switches'Access,
536 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
537 Defext => " "),
539 Xref =>
540 (Cname => new S'("XREF"),
541 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
542 VMS_Only => False,
543 Unixcmd => new S'("gnatxref"),
544 Unixsws => null,
545 Switches => Xref_Switches'Access,
546 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
547 Defext => "ali")
549 end Initialize;
551 ------------------
552 -- Invert_Sense --
553 ------------------
555 function Invert_Sense (S : String) return VMS_Data.String_Ptr is
556 Sinv : String (1 .. S'Length * 2);
557 -- Result (for sure long enough)
559 Sinvp : Natural := 0;
560 -- Pointer to output string
562 begin
563 for Sp in S'Range loop
564 if Sp = S'First or else S (Sp - 1) = ',' then
565 if S (Sp) = '!' then
566 null;
567 else
568 Sinv (Sinvp + 1) := '!';
569 Sinv (Sinvp + 2) := S (Sp);
570 Sinvp := Sinvp + 2;
571 end if;
573 else
574 Sinv (Sinvp + 1) := S (Sp);
575 Sinvp := Sinvp + 1;
576 end if;
577 end loop;
579 return new String'(Sinv (1 .. Sinvp));
580 end Invert_Sense;
582 ----------------------
583 -- Is_Extensionless --
584 ----------------------
586 function Is_Extensionless (F : String) return Boolean is
587 begin
588 for J in reverse F'Range loop
589 if F (J) = '.' then
590 return False;
591 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
592 return True;
593 end if;
594 end loop;
596 return True;
597 end Is_Extensionless;
599 -----------
600 -- Match --
601 -----------
603 function Match (S1, S2 : String) return Boolean is
604 Dif : constant Integer := S2'First - S1'First;
606 begin
608 if S1'Length /= S2'Length then
609 return False;
611 else
612 for J in S1'Range loop
613 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
614 return False;
615 end if;
616 end loop;
618 return True;
619 end if;
620 end Match;
622 ------------------
623 -- Match_Prefix --
624 ------------------
626 function Match_Prefix (S1, S2 : String) return Boolean is
627 begin
628 if S1'Length > S2'Length then
629 return False;
630 else
631 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
632 end if;
633 end Match_Prefix;
635 -------------------
636 -- Matching_Name --
637 -------------------
639 function Matching_Name
640 (S : String;
641 Itm : Item_Ptr;
642 Quiet : Boolean := False) return Item_Ptr
644 P1, P2 : Item_Ptr;
646 procedure Err;
647 -- Little procedure to output command/qualifier/option as appropriate
648 -- and bump error count.
650 ---------
651 -- Err --
652 ---------
654 procedure Err is
655 begin
656 if Quiet then
657 return;
658 end if;
660 Errors := Errors + 1;
662 if Itm /= null then
663 case Itm.Id is
664 when Id_Command =>
665 Put (Standard_Error, "command");
667 when Id_Switch =>
668 if Hostparm.OpenVMS then
669 Put (Standard_Error, "qualifier");
670 else
671 Put (Standard_Error, "switch");
672 end if;
674 when Id_Option =>
675 Put (Standard_Error, "option");
677 end case;
678 else
679 Put (Standard_Error, "input");
681 end if;
683 Put (Standard_Error, ": ");
684 Put (Standard_Error, S);
685 end Err;
687 -- Start of processing for Matching_Name
689 begin
690 -- If exact match, that's the one we want
692 P1 := Itm;
693 while P1 /= null loop
694 if Match (S, P1.Name.all) then
695 return P1;
696 else
697 P1 := P1.Next;
698 end if;
699 end loop;
701 -- Now check for prefix matches
703 P1 := Itm;
704 while P1 /= null loop
705 if P1.Name.all = "/<other>" then
706 return P1;
708 elsif not Match_Prefix (S, P1.Name.all) then
709 P1 := P1.Next;
711 else
712 -- Here we have found one matching prefix, so see if there is
713 -- another one (which is an ambiguity)
715 P2 := P1.Next;
716 while P2 /= null loop
717 if Match_Prefix (S, P2.Name.all) then
718 if not Quiet then
719 Put (Standard_Error, "ambiguous ");
720 Err;
721 Put (Standard_Error, " (matches ");
722 Put (Standard_Error, P1.Name.all);
724 while P2 /= null loop
725 if Match_Prefix (S, P2.Name.all) then
726 Put (Standard_Error, ',');
727 Put (Standard_Error, P2.Name.all);
728 end if;
730 P2 := P2.Next;
731 end loop;
733 Put_Line (Standard_Error, ")");
734 end if;
736 return null;
737 end if;
739 P2 := P2.Next;
740 end loop;
742 -- If we fall through that loop, then there was only one match
744 return P1;
745 end if;
746 end loop;
748 -- If we fall through outer loop, there was no match
750 if not Quiet then
751 Put (Standard_Error, "unrecognized ");
752 Err;
753 New_Line (Standard_Error);
754 end if;
756 return null;
757 end Matching_Name;
759 -----------------------
760 -- OK_Alphanumerplus --
761 -----------------------
763 function OK_Alphanumerplus (S : String) return Boolean is
764 begin
765 if S'Length = 0 then
766 return False;
768 else
769 for J in S'Range loop
770 if not (Is_Alphanumeric (S (J)) or else
771 S (J) = '_' or else S (J) = '$')
772 then
773 return False;
774 end if;
775 end loop;
777 return True;
778 end if;
779 end OK_Alphanumerplus;
781 ----------------
782 -- OK_Integer --
783 ----------------
785 function OK_Integer (S : String) return Boolean is
786 begin
787 if S'Length = 0 then
788 return False;
790 else
791 for J in S'Range loop
792 if not Is_Digit (S (J)) then
793 return False;
794 end if;
795 end loop;
797 return True;
798 end if;
799 end OK_Integer;
801 --------------------
802 -- Output_Version --
803 --------------------
805 procedure Output_Version is
806 begin
807 Put ("GNAT ");
808 Put_Line (Gnatvsn.Gnat_Version_String);
809 Put_Line ("Copyright 1996-" &
810 Current_Year &
811 ", Free Software Foundation, Inc.");
812 end Output_Version;
814 -----------
815 -- Place --
816 -----------
818 procedure Place (C : Character) is
819 begin
820 if Cargs then
821 Cargs_Buffer.Append (C);
822 else
823 Buffer.Append (C);
824 end if;
825 end Place;
827 procedure Place (S : String) is
828 begin
829 for J in S'Range loop
830 Place (S (J));
831 end loop;
832 end Place;
834 -----------------
835 -- Place_Lower --
836 -----------------
838 procedure Place_Lower (S : String) is
839 begin
840 for J in S'Range loop
841 Place (To_Lower (S (J)));
842 end loop;
843 end Place_Lower;
845 -------------------------
846 -- Place_Unix_Switches --
847 -------------------------
849 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
850 P1, P2, P3 : Natural;
851 Remove : Boolean;
852 Slen, Sln2 : Natural;
853 Wild_Card : Boolean := False;
855 begin
856 P1 := S'First;
857 while P1 <= S'Last loop
858 if S (P1) = '!' then
859 P1 := P1 + 1;
860 Remove := True;
861 else
862 Remove := False;
863 end if;
865 P2 := P1;
866 pragma Assert (S (P1) = '-' or else S (P1) = '`');
868 while P2 < S'Last and then S (P2 + 1) /= ',' loop
869 P2 := P2 + 1;
870 end loop;
872 -- Switch is now in S (P1 .. P2)
874 Slen := P2 - P1 + 1;
876 if Remove then
877 Wild_Card := S (P2) = '*';
879 if Wild_Card then
880 Slen := Slen - 1;
881 P2 := P2 - 1;
882 end if;
884 P3 := 1;
885 while P3 <= Buffer.Last - Slen loop
886 if Buffer.Table (P3) = ' '
887 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
888 S (P1 .. P2)
889 and then (Wild_Card
890 or else
891 P3 + Slen = Buffer.Last
892 or else
893 Buffer.Table (P3 + Slen + 1) = ' ')
894 then
895 Sln2 := Slen;
897 if Wild_Card then
898 while P3 + Sln2 /= Buffer.Last
899 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
900 loop
901 Sln2 := Sln2 + 1;
902 end loop;
903 end if;
905 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
906 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
907 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
909 else
910 P3 := P3 + 1;
911 end if;
912 end loop;
914 if Wild_Card then
915 P2 := P2 + 1;
916 end if;
918 else
919 pragma Assert (S (P2) /= '*');
920 Place (' ');
922 if S (P1) = '`' then
923 P1 := P1 + 1;
924 end if;
926 Place (S (P1 .. P2));
927 end if;
929 P1 := P2 + 2;
930 end loop;
931 end Place_Unix_Switches;
933 -----------------------------
934 -- Preprocess_Command_Data --
935 -----------------------------
937 procedure Preprocess_Command_Data is
938 begin
939 for C in Real_Command_Type loop
940 declare
941 Command : constant Item_Ptr := new Command_Item;
943 Last_Switch : Item_Ptr;
944 -- Last switch in list
946 begin
947 -- Link new command item into list of commands
949 if Last_Command = null then
950 Commands := Command;
951 else
952 Last_Command.Next := Command;
953 end if;
955 Last_Command := Command;
957 -- Fill in fields of new command item
959 Command.Name := Command_List (C).Cname;
960 Command.Usage := Command_List (C).Usage;
961 Command.Command := C;
963 if Command_List (C).Unixsws = null then
964 Command.Unix_String := Command_List (C).Unixcmd;
965 else
966 declare
967 Cmd : String (1 .. 5_000);
968 Last : Natural := 0;
969 Sws : constant Argument_List_Access :=
970 Command_List (C).Unixsws;
972 begin
973 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
974 Command_List (C).Unixcmd.all;
975 Last := Command_List (C).Unixcmd'Length;
977 for J in Sws'Range loop
978 Last := Last + 1;
979 Cmd (Last) := ' ';
980 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
981 Sws (J).all;
982 Last := Last + Sws (J)'Length;
983 end loop;
985 Command.Unix_String := new String'(Cmd (1 .. Last));
986 end;
987 end if;
989 Command.Params := Command_List (C).Params;
990 Command.Defext := Command_List (C).Defext;
992 Validate_Command_Or_Option (Command.Name);
994 -- Process the switch list
996 for S in Command_List (C).Switches'Range loop
997 declare
998 SS : constant VMS_Data.String_Ptr :=
999 Command_List (C).Switches (S);
1000 P : Natural := SS'First;
1001 Sw : Item_Ptr := new Switch_Item;
1003 Last_Opt : Item_Ptr;
1004 -- Pointer to last option
1006 begin
1007 -- Link new switch item into list of switches
1009 if Last_Switch = null then
1010 Command.Switches := Sw;
1011 else
1012 Last_Switch.Next := Sw;
1013 end if;
1015 Last_Switch := Sw;
1017 -- Process switch string, first get name
1019 while SS (P) /= ' ' and SS (P) /= '=' loop
1020 P := P + 1;
1021 end loop;
1023 Sw.Name := new String'(SS (SS'First .. P - 1));
1025 -- Direct translation case
1027 if SS (P) = ' ' then
1028 Sw.Translation := T_Direct;
1029 Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
1030 Validate_Unix_Switch (Sw.Unix_String);
1032 if SS (P - 1) = '>' then
1033 Sw.Translation := T_Other;
1035 elsif SS (P + 1) = '`' then
1036 null;
1038 -- Create the inverted case (/NO ..)
1040 elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
1041 Sw := new Switch_Item;
1042 Last_Switch.Next := Sw;
1043 Last_Switch := Sw;
1045 Sw.Name :=
1046 new String'("/NO" & SS (SS'First + 1 .. P - 1));
1047 Sw.Translation := T_Direct;
1048 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
1049 Validate_Unix_Switch (Sw.Unix_String);
1050 end if;
1052 -- Directories translation case
1054 elsif SS (P + 1) = '*' then
1055 pragma Assert (SS (SS'Last) = '*');
1056 Sw.Translation := T_Directories;
1057 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1058 Validate_Unix_Switch (Sw.Unix_String);
1060 -- Directory translation case
1062 elsif SS (P + 1) = '%' then
1063 pragma Assert (SS (SS'Last) = '%');
1064 Sw.Translation := T_Directory;
1065 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1066 Validate_Unix_Switch (Sw.Unix_String);
1068 -- File translation case
1070 elsif SS (P + 1) = '@' then
1071 pragma Assert (SS (SS'Last) = '@');
1072 Sw.Translation := T_File;
1073 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1074 Validate_Unix_Switch (Sw.Unix_String);
1076 -- No space file translation case
1078 elsif SS (P + 1) = '<' then
1079 pragma Assert (SS (SS'Last) = '>');
1080 Sw.Translation := T_No_Space_File;
1081 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1082 Validate_Unix_Switch (Sw.Unix_String);
1084 -- Numeric translation case
1086 elsif SS (P + 1) = '#' then
1087 pragma Assert (SS (SS'Last) = '#');
1088 Sw.Translation := T_Numeric;
1089 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1090 Validate_Unix_Switch (Sw.Unix_String);
1092 -- Alphanumerplus translation case
1094 elsif SS (P + 1) = '|' then
1095 pragma Assert (SS (SS'Last) = '|');
1096 Sw.Translation := T_Alphanumplus;
1097 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1098 Validate_Unix_Switch (Sw.Unix_String);
1100 -- String translation case
1102 elsif SS (P + 1) = '"' then
1103 pragma Assert (SS (SS'Last) = '"');
1104 Sw.Translation := T_String;
1105 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1106 Validate_Unix_Switch (Sw.Unix_String);
1108 -- Commands translation case
1110 elsif SS (P + 1) = '?' then
1111 Sw.Translation := T_Commands;
1112 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));
1114 -- Options translation case
1116 else
1117 Sw.Translation := T_Options;
1118 Sw.Unix_String := new String'("");
1120 P := P + 1; -- bump past =
1121 while P <= SS'Last loop
1122 declare
1123 Opt : constant Item_Ptr := new Option_Item;
1124 Q : Natural;
1126 begin
1127 -- Link new option item into options list
1129 if Last_Opt = null then
1130 Sw.Options := Opt;
1131 else
1132 Last_Opt.Next := Opt;
1133 end if;
1135 Last_Opt := Opt;
1137 -- Fill in fields of new option item
1139 Q := P;
1140 while SS (Q) /= ' ' loop
1141 Q := Q + 1;
1142 end loop;
1144 Opt.Name := new String'(SS (P .. Q - 1));
1145 Validate_Command_Or_Option (Opt.Name);
1147 P := Q + 1;
1148 Q := P;
1150 while Q <= SS'Last and then SS (Q) /= ' ' loop
1151 Q := Q + 1;
1152 end loop;
1154 Opt.Unix_String := new String'(SS (P .. Q - 1));
1155 Validate_Unix_Switch (Opt.Unix_String);
1156 P := Q + 1;
1157 end;
1158 end loop;
1159 end if;
1160 end;
1161 end loop;
1162 end;
1163 end loop;
1164 end Preprocess_Command_Data;
1166 ----------------------
1167 -- Process_Argument --
1168 ----------------------
1170 procedure Process_Argument (The_Command : in out Command_Type) is
1171 Argv : String_Access;
1172 Arg_Idx : Integer;
1174 function Get_Arg_End
1175 (Argv : String;
1176 Arg_Idx : Integer) return Integer;
1177 -- Begins looking at Arg_Idx + 1 and returns the index of the
1178 -- last character before a slash or else the index of the last
1179 -- character in the string Argv.
1181 -----------------
1182 -- Get_Arg_End --
1183 -----------------
1185 function Get_Arg_End
1186 (Argv : String;
1187 Arg_Idx : Integer) return Integer
1189 begin
1190 for J in Arg_Idx + 1 .. Argv'Last loop
1191 if Argv (J) = '/' then
1192 return J - 1;
1193 end if;
1194 end loop;
1196 return Argv'Last;
1197 end Get_Arg_End;
1199 -- Start of processing for Process_Argument
1201 begin
1202 Cargs := False;
1204 -- If an argument file is open, read the next non empty line
1206 if Is_Open (Arg_File) then
1207 declare
1208 Line : String (1 .. 256);
1209 Last : Natural;
1210 begin
1211 loop
1212 Get_Line (Arg_File, Line, Last);
1213 exit when Last /= 0 or else End_Of_File (Arg_File);
1214 end loop;
1216 -- If the end of the argument file has been reached, close it
1218 if End_Of_File (Arg_File) then
1219 Close (Arg_File);
1221 -- If the last line was empty, return after increasing Arg_Num
1222 -- to go to the next argument on the comment line.
1224 if Last = 0 then
1225 Arg_Num := Arg_Num + 1;
1226 return;
1227 end if;
1228 end if;
1230 Argv := new String'(Line (1 .. Last));
1231 Arg_Idx := 1;
1233 if Argv (1) = '@' then
1234 Put_Line (Standard_Error, "argument file cannot contain @cmd");
1235 raise Error_Exit;
1236 end if;
1237 end;
1239 else
1240 -- No argument file is open, get the argument on the command line
1242 Argv := new String'(Argument (Arg_Num));
1243 Arg_Idx := Argv'First;
1245 -- Check if this is the specification of an argument file
1247 if Argv (Arg_Idx) = '@' then
1248 -- The first argument on the command line cannot be an argument
1249 -- file.
1251 if Arg_Num = 1 then
1252 Put_Line
1253 (Standard_Error,
1254 "Cannot specify argument line before command");
1255 raise Error_Exit;
1256 end if;
1258 -- Open the file, after conversion of the name to canonical form.
1259 -- Fail if file is not found.
1261 declare
1262 Canonical_File_Name : String_Access :=
1263 To_Canonical_File_Spec (Argv (Arg_Idx + 1 .. Argv'Last));
1264 begin
1265 Open (Arg_File, In_File, Canonical_File_Name.all);
1266 Free (Canonical_File_Name);
1267 return;
1269 exception
1270 when others =>
1271 Put (Standard_Error, "Cannot open argument file """);
1272 Put (Standard_Error, Argv (Arg_Idx + 1 .. Argv'Last));
1273 Put_Line (Standard_Error, """");
1274 raise Error_Exit;
1275 end;
1276 end if;
1277 end if;
1279 <<Tryagain_After_Coalesce>>
1280 loop
1281 declare
1282 Next_Arg_Idx : Integer;
1283 Arg : String_Access;
1285 begin
1286 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1287 Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1289 -- The first one must be a command name
1291 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1292 Command := Matching_Name (Arg.all, Commands);
1294 if Command = null then
1295 raise Error_Exit;
1296 end if;
1298 The_Command := Command.Command;
1299 Output_File_Expected := False;
1301 -- Give usage information if only command given
1303 if Argument_Count = 1
1304 and then Next_Arg_Idx = Argv'Last
1305 then
1306 Output_Version;
1307 New_Line;
1308 Put_Line
1309 ("List of available qualifiers and options");
1310 New_Line;
1312 Put (Command.Usage.all);
1313 Set_Col (53);
1314 Put_Line (Command.Unix_String.all);
1316 declare
1317 Sw : Item_Ptr := Command.Switches;
1319 begin
1320 while Sw /= null loop
1321 Put (" ");
1322 Put (Sw.Name.all);
1324 case Sw.Translation is
1326 when T_Other =>
1327 Set_Col (53);
1328 Put_Line (Sw.Unix_String.all &
1329 "/<other>");
1331 when T_Direct =>
1332 Set_Col (53);
1333 Put_Line (Sw.Unix_String.all);
1335 when T_Directories =>
1336 Put ("=(direc,direc,..direc)");
1337 Set_Col (53);
1338 Put (Sw.Unix_String.all);
1339 Put (" direc ");
1340 Put (Sw.Unix_String.all);
1341 Put_Line (" direc ...");
1343 when T_Directory =>
1344 Put ("=directory");
1345 Set_Col (53);
1346 Put (Sw.Unix_String.all);
1348 if Sw.Unix_String (Sw.Unix_String'Last)
1349 /= '='
1350 then
1351 Put (' ');
1352 end if;
1354 Put_Line ("directory ");
1356 when T_File | T_No_Space_File =>
1357 Put ("=file");
1358 Set_Col (53);
1359 Put (Sw.Unix_String.all);
1361 if Sw.Translation = T_File
1362 and then Sw.Unix_String
1363 (Sw.Unix_String'Last) /= '='
1364 then
1365 Put (' ');
1366 end if;
1368 Put_Line ("file ");
1370 when T_Numeric =>
1371 Put ("=nnn");
1372 Set_Col (53);
1374 if Sw.Unix_String
1375 (Sw.Unix_String'First) = '`'
1376 then
1377 Put (Sw.Unix_String
1378 (Sw.Unix_String'First + 1
1379 .. Sw.Unix_String'Last));
1380 else
1381 Put (Sw.Unix_String.all);
1382 end if;
1384 Put_Line ("nnn");
1386 when T_Alphanumplus =>
1387 Put ("=xyz");
1388 Set_Col (53);
1390 if Sw.Unix_String
1391 (Sw.Unix_String'First) = '`'
1392 then
1393 Put (Sw.Unix_String
1394 (Sw.Unix_String'First + 1
1395 .. Sw.Unix_String'Last));
1396 else
1397 Put (Sw.Unix_String.all);
1398 end if;
1400 Put_Line ("xyz");
1402 when T_String =>
1403 Put ("=");
1404 Put ('"');
1405 Put ("<string>");
1406 Put ('"');
1407 Set_Col (53);
1409 Put (Sw.Unix_String.all);
1411 if Sw.Unix_String
1412 (Sw.Unix_String'Last) /= '='
1413 then
1414 Put (' ');
1415 end if;
1417 Put ("<string>");
1418 New_Line;
1420 when T_Commands =>
1421 Put (" (switches for ");
1422 Put (Sw.Unix_String
1423 (Sw.Unix_String'First + 7
1424 .. Sw.Unix_String'Last));
1425 Put (')');
1426 Set_Col (53);
1427 Put (Sw.Unix_String
1428 (Sw.Unix_String'First
1429 .. Sw.Unix_String'First + 5));
1430 Put_Line (" switches");
1432 when T_Options =>
1433 declare
1434 Opt : Item_Ptr := Sw.Options;
1436 begin
1437 Put_Line ("=(option,option..)");
1439 while Opt /= null loop
1440 Put (" ");
1441 Put (Opt.Name.all);
1443 if Opt = Sw.Options then
1444 Put (" (D)");
1445 end if;
1447 Set_Col (53);
1448 Put_Line (Opt.Unix_String.all);
1449 Opt := Opt.Next;
1450 end loop;
1451 end;
1453 end case;
1455 Sw := Sw.Next;
1456 end loop;
1457 end;
1459 raise Normal_Exit;
1460 end if;
1462 -- Special handling for internal debugging switch /?
1464 elsif Arg.all = "/?" then
1465 Display_Command := True;
1466 Output_File_Expected := False;
1468 -- Special handling of internal option /KEEP_TEMPORARY_FILES
1470 elsif Arg'Length >= 7
1471 and then Matching_Name
1472 (Arg.all, Keep_Temps_Option, True) /= null
1473 then
1474 Opt.Keep_Temporary_Files := True;
1476 -- Copy -switch unchanged, as well as +rule
1478 elsif Arg (Arg'First) = '-' or else Arg (Arg'First) = '+' then
1479 Place (' ');
1480 Place (Arg.all);
1482 -- Set Output_File_Expected for the next argument
1484 Output_File_Expected :=
1485 Arg.all = "-o" and then The_Command = Link;
1487 -- Copy quoted switch with quotes stripped
1489 elsif Arg (Arg'First) = '"' then
1490 if Arg (Arg'Last) /= '"' then
1491 Put (Standard_Error, "misquoted argument: ");
1492 Put_Line (Standard_Error, Arg.all);
1493 Errors := Errors + 1;
1495 else
1496 Place (' ');
1497 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1498 end if;
1500 Output_File_Expected := False;
1502 -- Parameter Argument
1504 elsif Arg (Arg'First) /= '/'
1505 and then Make_Commands_Active = null
1506 then
1507 Param_Count := Param_Count + 1;
1509 if Param_Count <= Command.Params'Length then
1511 case Command.Params (Param_Count) is
1513 when File | Optional_File =>
1514 declare
1515 Normal_File : constant String_Access :=
1516 To_Canonical_File_Spec
1517 (Arg.all);
1519 begin
1520 Place (' ');
1521 Place_Lower (Normal_File.all);
1523 if Is_Extensionless (Normal_File.all)
1524 and then Command.Defext /= " "
1525 then
1526 Place ('.');
1527 Place (Command.Defext);
1528 end if;
1529 end;
1531 when Unlimited_Files =>
1532 declare
1533 Normal_File : constant String_Access :=
1534 To_Canonical_File_Spec
1535 (Arg.all);
1537 File_Is_Wild : Boolean := False;
1538 File_List : String_Access_List_Access;
1540 begin
1541 for J in Arg'Range loop
1542 if Arg (J) = '*'
1543 or else Arg (J) = '%'
1544 then
1545 File_Is_Wild := True;
1546 end if;
1547 end loop;
1549 if File_Is_Wild then
1550 File_List := To_Canonical_File_List
1551 (Arg.all, False);
1553 for J in File_List.all'Range loop
1554 Place (' ');
1555 Place_Lower (File_List.all (J).all);
1556 end loop;
1558 else
1559 Place (' ');
1560 Place_Lower (Normal_File.all);
1562 -- Add extension if not present, except after
1563 -- switch -o.
1565 if Is_Extensionless (Normal_File.all)
1566 and then Command.Defext /= " "
1567 and then not Output_File_Expected
1568 then
1569 Place ('.');
1570 Place (Command.Defext);
1571 end if;
1572 end if;
1574 Param_Count := Param_Count - 1;
1575 end;
1577 when Other_As_Is =>
1578 Place (' ');
1579 Place (Arg.all);
1581 when Unlimited_As_Is =>
1582 Place (' ');
1583 Place (Arg.all);
1584 Param_Count := Param_Count - 1;
1586 when Files_Or_Wildcard =>
1588 -- Remove spaces from a comma separated list
1589 -- of file names and adjust control variables
1590 -- accordingly.
1592 while Arg_Num < Argument_Count and then
1593 (Argv (Argv'Last) = ',' xor
1594 Argument (Arg_Num + 1)
1595 (Argument (Arg_Num + 1)'First) = ',')
1596 loop
1597 Argv := new String'
1598 (Argv.all & Argument (Arg_Num + 1));
1599 Arg_Num := Arg_Num + 1;
1600 Arg_Idx := Argv'First;
1601 Next_Arg_Idx :=
1602 Get_Arg_End (Argv.all, Arg_Idx);
1603 Arg := new String'
1604 (Argv (Arg_Idx .. Next_Arg_Idx));
1605 end loop;
1607 -- Parse the comma separated list of VMS
1608 -- filenames and place them on the command
1609 -- line as space separated Unix style
1610 -- filenames. Lower case and add default
1611 -- extension as appropriate.
1613 declare
1614 Arg1_Idx : Integer := Arg'First;
1616 function Get_Arg1_End
1617 (Arg : String;
1618 Arg_Idx : Integer) return Integer;
1619 -- Begins looking at Arg_Idx + 1 and
1620 -- returns the index of the last character
1621 -- before a comma or else the index of the
1622 -- last character in the string Arg.
1624 ------------------
1625 -- Get_Arg1_End --
1626 ------------------
1628 function Get_Arg1_End
1629 (Arg : String;
1630 Arg_Idx : Integer) return Integer
1632 begin
1633 for J in Arg_Idx + 1 .. Arg'Last loop
1634 if Arg (J) = ',' then
1635 return J - 1;
1636 end if;
1637 end loop;
1639 return Arg'Last;
1640 end Get_Arg1_End;
1642 begin
1643 loop
1644 declare
1645 Next_Arg1_Idx :
1646 constant Integer :=
1647 Get_Arg1_End (Arg.all, Arg1_Idx);
1649 Arg1 :
1650 constant String :=
1651 Arg (Arg1_Idx .. Next_Arg1_Idx);
1653 Normal_File :
1654 constant String_Access :=
1655 To_Canonical_File_Spec (Arg1);
1657 begin
1658 Place (' ');
1659 Place_Lower (Normal_File.all);
1661 if Is_Extensionless (Normal_File.all)
1662 and then Command.Defext /= " "
1663 then
1664 Place ('.');
1665 Place (Command.Defext);
1666 end if;
1668 Arg1_Idx := Next_Arg1_Idx + 1;
1669 end;
1671 exit when Arg1_Idx > Arg'Last;
1673 -- Don't allow two or more commas in
1674 -- a row
1676 if Arg (Arg1_Idx) = ',' then
1677 Arg1_Idx := Arg1_Idx + 1;
1678 if Arg1_Idx > Arg'Last or else
1679 Arg (Arg1_Idx) = ','
1680 then
1681 Put_Line
1682 (Standard_Error,
1683 "Malformed Parameter: " &
1684 Arg.all);
1685 Put (Standard_Error, "usage: ");
1686 Put_Line (Standard_Error,
1687 Command.Usage.all);
1688 raise Error_Exit;
1689 end if;
1690 end if;
1692 end loop;
1693 end;
1694 end case;
1695 end if;
1697 -- Reset Output_File_Expected, in case it was True
1699 Output_File_Expected := False;
1701 -- Qualifier argument
1703 else
1704 Output_File_Expected := False;
1706 Cargs := Command.Name.all = "COMPILE";
1708 -- This code is too heavily nested, should be
1709 -- separated out as separate subprogram ???
1711 declare
1712 Sw : Item_Ptr;
1713 SwP : Natural;
1714 P2 : Natural;
1715 Endp : Natural := 0; -- avoid warning!
1716 Opt : Item_Ptr;
1718 begin
1719 SwP := Arg'First;
1720 while SwP < Arg'Last
1721 and then Arg (SwP + 1) /= '='
1722 loop
1723 SwP := SwP + 1;
1724 end loop;
1726 -- At this point, the switch name is in
1727 -- Arg (Arg'First..SwP) and if that is not the
1728 -- whole switch, then there is an equal sign at
1729 -- Arg (SwP + 1) and the rest of Arg is what comes
1730 -- after the equal sign.
1732 -- If make commands are active, see if we have
1733 -- another COMMANDS_TRANSLATION switch belonging
1734 -- to gnatmake.
1736 if Make_Commands_Active /= null then
1737 Sw :=
1738 Matching_Name
1739 (Arg (Arg'First .. SwP),
1740 Command.Switches,
1741 Quiet => True);
1743 if Sw /= null
1744 and then Sw.Translation = T_Commands
1745 then
1746 null;
1748 else
1749 Sw :=
1750 Matching_Name
1751 (Arg (Arg'First .. SwP),
1752 Make_Commands_Active.Switches,
1753 Quiet => False);
1754 end if;
1756 -- For case of GNAT MAKE or CHOP, if we cannot
1757 -- find the switch, then see if it is a
1758 -- recognized compiler switch instead, and if
1759 -- so process the compiler switch.
1761 elsif Command.Name.all = "MAKE"
1762 or else Command.Name.all = "CHOP" then
1763 Sw :=
1764 Matching_Name
1765 (Arg (Arg'First .. SwP),
1766 Command.Switches,
1767 Quiet => True);
1769 if Sw = null then
1770 Sw :=
1771 Matching_Name
1772 (Arg (Arg'First .. SwP),
1773 Matching_Name
1774 ("COMPILE", Commands).Switches,
1775 Quiet => False);
1776 end if;
1778 -- For all other cases, just search the relevant
1779 -- command.
1781 else
1782 Sw :=
1783 Matching_Name
1784 (Arg (Arg'First .. SwP),
1785 Command.Switches,
1786 Quiet => False);
1787 end if;
1789 if Sw /= null then
1790 if Cargs
1791 and then Sw.Name /= null
1792 and then
1793 (Sw.Name.all = "/PROJECT_FILE" or else
1794 Sw.Name.all = "/MESSAGES_PROJECT_FILE" or else
1795 Sw.Name.all = "/EXTERNAL_REFERENCE")
1796 then
1797 Cargs := False;
1798 end if;
1800 case Sw.Translation is
1801 when T_Direct =>
1802 Place_Unix_Switches (Sw.Unix_String);
1803 if SwP < Arg'Last
1804 and then Arg (SwP + 1) = '='
1805 then
1806 Put (Standard_Error,
1807 "qualifier options ignored: ");
1808 Put_Line (Standard_Error, Arg.all);
1809 end if;
1811 when T_Directories =>
1812 if SwP + 1 > Arg'Last then
1813 Put (Standard_Error,
1814 "missing directories for: ");
1815 Put_Line (Standard_Error, Arg.all);
1816 Errors := Errors + 1;
1818 elsif Arg (SwP + 2) /= '(' then
1819 SwP := SwP + 2;
1820 Endp := Arg'Last;
1822 elsif Arg (Arg'Last) /= ')' then
1824 -- Remove spaces from a comma separated
1825 -- list of file names and adjust
1826 -- control variables accordingly.
1828 if Arg_Num < Argument_Count and then
1829 (Argv (Argv'Last) = ',' xor
1830 Argument (Arg_Num + 1)
1831 (Argument (Arg_Num + 1)'First) = ',')
1832 then
1833 Argv :=
1834 new String'(Argv.all
1835 & Argument
1836 (Arg_Num + 1));
1837 Arg_Num := Arg_Num + 1;
1838 Arg_Idx := Argv'First;
1839 Next_Arg_Idx :=
1840 Get_Arg_End (Argv.all, Arg_Idx);
1841 Arg := new String'
1842 (Argv (Arg_Idx .. Next_Arg_Idx));
1843 goto Tryagain_After_Coalesce;
1844 end if;
1846 Put (Standard_Error,
1847 "incorrectly parenthesized " &
1848 "or malformed argument: ");
1849 Put_Line (Standard_Error, Arg.all);
1850 Errors := Errors + 1;
1852 else
1853 SwP := SwP + 3;
1854 Endp := Arg'Last - 1;
1855 end if;
1857 while SwP <= Endp loop
1858 declare
1859 Dir_Is_Wild : Boolean := False;
1860 Dir_Maybe_Is_Wild : Boolean := False;
1862 Dir_List : String_Access_List_Access;
1864 begin
1865 P2 := SwP;
1867 while P2 < Endp
1868 and then Arg (P2 + 1) /= ','
1869 loop
1870 -- A wildcard directory spec on
1871 -- VMS will contain either * or
1872 -- % or ...
1874 if Arg (P2) = '*' then
1875 Dir_Is_Wild := True;
1877 elsif Arg (P2) = '%' then
1878 Dir_Is_Wild := True;
1880 elsif Dir_Maybe_Is_Wild
1881 and then Arg (P2) = '.'
1882 and then Arg (P2 + 1) = '.'
1883 then
1884 Dir_Is_Wild := True;
1885 Dir_Maybe_Is_Wild := False;
1887 elsif Dir_Maybe_Is_Wild then
1888 Dir_Maybe_Is_Wild := False;
1890 elsif Arg (P2) = '.'
1891 and then Arg (P2 + 1) = '.'
1892 then
1893 Dir_Maybe_Is_Wild := True;
1895 end if;
1897 P2 := P2 + 1;
1898 end loop;
1900 if Dir_Is_Wild then
1901 Dir_List :=
1902 To_Canonical_File_List
1903 (Arg (SwP .. P2), True);
1905 for J in Dir_List.all'Range loop
1906 Place_Unix_Switches
1907 (Sw.Unix_String);
1908 Place_Lower
1909 (Dir_List.all (J).all);
1910 end loop;
1912 else
1913 Place_Unix_Switches
1914 (Sw.Unix_String);
1915 Place_Lower
1916 (To_Canonical_Dir_Spec
1917 (Arg (SwP .. P2), False).all);
1918 end if;
1920 SwP := P2 + 2;
1921 end;
1922 end loop;
1924 when T_Directory =>
1925 if SwP + 1 > Arg'Last then
1926 Put (Standard_Error,
1927 "missing directory for: ");
1928 Put_Line (Standard_Error, Arg.all);
1929 Errors := Errors + 1;
1931 else
1932 Place_Unix_Switches (Sw.Unix_String);
1934 -- Some switches end in "=". No space
1935 -- here
1937 if Sw.Unix_String
1938 (Sw.Unix_String'Last) /= '='
1939 then
1940 Place (' ');
1941 end if;
1943 Place_Lower
1944 (To_Canonical_Dir_Spec
1945 (Arg (SwP + 2 .. Arg'Last),
1946 False).all);
1947 end if;
1949 when T_File | T_No_Space_File =>
1950 if SwP + 1 > Arg'Last then
1951 Put (Standard_Error,
1952 "missing file for: ");
1953 Put_Line (Standard_Error, Arg.all);
1954 Errors := Errors + 1;
1956 else
1957 Place_Unix_Switches (Sw.Unix_String);
1959 -- Some switches end in "=". No space
1960 -- here.
1962 if Sw.Translation = T_File
1963 and then Sw.Unix_String
1964 (Sw.Unix_String'Last) /= '='
1965 then
1966 Place (' ');
1967 end if;
1969 Place_Lower
1970 (To_Canonical_File_Spec
1971 (Arg (SwP + 2 .. Arg'Last)).all);
1972 end if;
1974 when T_Numeric =>
1975 if OK_Integer (Arg (SwP + 2 .. Arg'Last)) then
1976 Place_Unix_Switches (Sw.Unix_String);
1977 Place (Arg (SwP + 2 .. Arg'Last));
1979 else
1980 Put (Standard_Error, "argument for ");
1981 Put (Standard_Error, Sw.Name.all);
1982 Put_Line
1983 (Standard_Error, " must be numeric");
1984 Errors := Errors + 1;
1985 end if;
1987 when T_Alphanumplus =>
1988 if OK_Alphanumerplus
1989 (Arg (SwP + 2 .. Arg'Last))
1990 then
1991 Place_Unix_Switches (Sw.Unix_String);
1992 Place (Arg (SwP + 2 .. Arg'Last));
1994 else
1995 Put (Standard_Error, "argument for ");
1996 Put (Standard_Error, Sw.Name.all);
1997 Put_Line (Standard_Error,
1998 " must be alphanumeric");
1999 Errors := Errors + 1;
2000 end if;
2002 when T_String =>
2004 -- A String value must be extended to the
2005 -- end of the Argv, otherwise strings like
2006 -- "foo/bar" get split at the slash.
2008 -- The begining and ending of the string
2009 -- are flagged with embedded nulls which
2010 -- are removed when building the Spawn
2011 -- call. Nulls are use because they won't
2012 -- show up in a /? output. Quotes aren't
2013 -- used because that would make it
2014 -- difficult to embed them.
2016 Place_Unix_Switches (Sw.Unix_String);
2018 if Next_Arg_Idx /= Argv'Last then
2019 Next_Arg_Idx := Argv'Last;
2020 Arg := new String'
2021 (Argv (Arg_Idx .. Next_Arg_Idx));
2023 SwP := Arg'First;
2024 while SwP < Arg'Last and then
2025 Arg (SwP + 1) /= '=' loop
2026 SwP := SwP + 1;
2027 end loop;
2028 end if;
2030 Place (ASCII.NUL);
2031 Place (Arg (SwP + 2 .. Arg'Last));
2032 Place (ASCII.NUL);
2034 when T_Commands =>
2036 -- Output -largs/-bargs/-cargs
2038 Place (' ');
2039 Place (Sw.Unix_String
2040 (Sw.Unix_String'First ..
2041 Sw.Unix_String'First + 5));
2043 if Sw.Unix_String
2044 (Sw.Unix_String'First + 7 ..
2045 Sw.Unix_String'Last) = "MAKE"
2046 then
2047 Make_Commands_Active := null;
2049 else
2050 -- Set source of new commands, also
2051 -- setting this non-null indicates that
2052 -- we are in the special commands mode
2053 -- for processing the -xargs case.
2055 Make_Commands_Active :=
2056 Matching_Name
2057 (Sw.Unix_String
2058 (Sw.Unix_String'First + 7 ..
2059 Sw.Unix_String'Last),
2060 Commands);
2061 end if;
2063 when T_Options =>
2064 if SwP + 1 > Arg'Last then
2065 Place_Unix_Switches
2066 (Sw.Options.Unix_String);
2067 SwP := Endp + 1;
2069 elsif Arg (SwP + 2) /= '(' then
2070 SwP := SwP + 2;
2071 Endp := Arg'Last;
2073 elsif Arg (Arg'Last) /= ')' then
2074 Put (Standard_Error,
2075 "incorrectly parenthesized argument: ");
2076 Put_Line (Standard_Error, Arg.all);
2077 Errors := Errors + 1;
2078 SwP := Endp + 1;
2080 else
2081 SwP := SwP + 3;
2082 Endp := Arg'Last - 1;
2083 end if;
2085 while SwP <= Endp loop
2086 P2 := SwP;
2088 while P2 < Endp
2089 and then Arg (P2 + 1) /= ','
2090 loop
2091 P2 := P2 + 1;
2092 end loop;
2094 -- Option name is in Arg (SwP .. P2)
2096 Opt := Matching_Name (Arg (SwP .. P2),
2097 Sw.Options);
2099 if Opt /= null then
2100 Place_Unix_Switches
2101 (Opt.Unix_String);
2102 end if;
2104 SwP := P2 + 2;
2105 end loop;
2107 when T_Other =>
2108 Place_Unix_Switches
2109 (new String'(Sw.Unix_String.all &
2110 Arg.all));
2112 end case;
2113 end if;
2114 end;
2115 end if;
2117 Arg_Idx := Next_Arg_Idx + 1;
2118 end;
2120 exit when Arg_Idx > Argv'Last;
2122 end loop;
2124 if not Is_Open (Arg_File) then
2125 Arg_Num := Arg_Num + 1;
2126 end if;
2127 end Process_Argument;
2129 --------------------
2130 -- Process_Buffer --
2131 --------------------
2133 procedure Process_Buffer (S : String) is
2134 P1, P2 : Natural;
2135 Inside_Nul : Boolean := False;
2136 Arg : String (1 .. 1024);
2137 Arg_Ctr : Natural;
2139 begin
2140 P1 := 1;
2141 while P1 <= S'Last and then S (P1) = ' ' loop
2142 P1 := P1 + 1;
2143 end loop;
2145 Arg_Ctr := 1;
2146 Arg (Arg_Ctr) := S (P1);
2148 while P1 <= S'Last loop
2149 if S (P1) = ASCII.NUL then
2150 if Inside_Nul then
2151 Inside_Nul := False;
2152 else
2153 Inside_Nul := True;
2154 end if;
2155 end if;
2157 if S (P1) = ' ' and then not Inside_Nul then
2158 P1 := P1 + 1;
2159 Arg_Ctr := Arg_Ctr + 1;
2160 Arg (Arg_Ctr) := S (P1);
2162 else
2163 Last_Switches.Increment_Last;
2164 P2 := P1;
2166 while P2 < S'Last
2167 and then (S (P2 + 1) /= ' ' or else
2168 Inside_Nul)
2169 loop
2170 P2 := P2 + 1;
2171 Arg_Ctr := Arg_Ctr + 1;
2172 Arg (Arg_Ctr) := S (P2);
2173 if S (P2) = ASCII.NUL then
2174 Arg_Ctr := Arg_Ctr - 1;
2176 if Inside_Nul then
2177 Inside_Nul := False;
2178 else
2179 Inside_Nul := True;
2180 end if;
2181 end if;
2182 end loop;
2184 Last_Switches.Table (Last_Switches.Last) :=
2185 new String'(String (Arg (1 .. Arg_Ctr)));
2186 P1 := P2 + 2;
2188 exit when P1 > S'Last;
2190 Arg_Ctr := 1;
2191 Arg (Arg_Ctr) := S (P1);
2192 end if;
2193 end loop;
2194 end Process_Buffer;
2196 --------------------------------
2197 -- Validate_Command_Or_Option --
2198 --------------------------------
2200 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
2201 begin
2202 pragma Assert (N'Length > 0);
2204 for J in N'Range loop
2205 if N (J) = '_' then
2206 pragma Assert (N (J - 1) /= '_');
2207 null;
2208 else
2209 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
2210 null;
2211 end if;
2212 end loop;
2213 end Validate_Command_Or_Option;
2215 --------------------------
2216 -- Validate_Unix_Switch --
2217 --------------------------
2219 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
2220 begin
2221 if S (S'First) = '`' then
2222 return;
2223 end if;
2225 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
2227 for J in S'First + 1 .. S'Last loop
2228 pragma Assert (S (J) /= ' ');
2230 if S (J) = '!' then
2231 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
2232 null;
2233 end if;
2234 end loop;
2235 end Validate_Unix_Switch;
2237 --------------------
2238 -- VMS_Conversion --
2239 --------------------
2241 procedure VMS_Conversion (The_Command : out Command_Type) is
2242 Result : Command_Type := Undefined;
2243 Result_Set : Boolean := False;
2245 begin
2246 Buffer.Init;
2248 -- First we must preprocess the string form of the command and options
2249 -- list into the internal form that we use.
2251 Preprocess_Command_Data;
2253 -- If no parameters, give complete list of commands
2255 if Argument_Count = 0 then
2256 Output_Version;
2257 New_Line;
2258 Put_Line ("List of available commands");
2259 New_Line;
2261 while Commands /= null loop
2262 Put (Commands.Usage.all);
2263 Set_Col (53);
2264 Put_Line (Commands.Unix_String.all);
2265 Commands := Commands.Next;
2266 end loop;
2268 raise Normal_Exit;
2269 end if;
2271 -- Loop through arguments
2273 Arg_Num := 1;
2274 while Arg_Num <= Argument_Count loop
2275 Process_Argument (Result);
2277 if not Result_Set then
2278 The_Command := Result;
2279 Result_Set := True;
2280 end if;
2281 end loop;
2283 -- Gross error checking that the number of parameters is correct.
2284 -- Not applicable to Unlimited_Files parameters.
2286 if (Param_Count = Command.Params'Length - 1
2287 and then Command.Params (Param_Count + 1) = Unlimited_Files)
2288 or else Param_Count <= Command.Params'Length
2289 then
2290 null;
2292 else
2293 Put_Line (Standard_Error,
2294 "Parameter count of "
2295 & Integer'Image (Param_Count)
2296 & " not equal to expected "
2297 & Integer'Image (Command.Params'Length));
2298 Put (Standard_Error, "usage: ");
2299 Put_Line (Standard_Error, Command.Usage.all);
2300 Errors := Errors + 1;
2301 end if;
2303 if Errors > 0 then
2304 raise Error_Exit;
2305 else
2306 -- Prepare arguments for a call to spawn, filtering out
2307 -- embedded nulls place there to delineate strings.
2309 Process_Buffer (String (Buffer.Table (1 .. Buffer.Last)));
2311 if Cargs_Buffer.Last > 1 then
2312 Last_Switches.Append (new String'("-cargs"));
2313 Process_Buffer
2314 (String (Cargs_Buffer.Table (1 .. Cargs_Buffer.Last)));
2315 end if;
2316 end if;
2317 end VMS_Conversion;
2319 end VMS_Conv;