Merge with main truk.
[official-gcc.git] / gcc / ada / vms_conv.adb
blobfbb19e58b01a1e09c16adb7a1d5c5541be68be29
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-2013, 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 qualifiers 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 appropriately. 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 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 Sync =>
401 (Cname => new S'("SYNC"),
402 Usage => new S'("GNAT SYNC name /qualifiers"),
403 VMS_Only => False,
404 Unixcmd => new S'("gnatsync"),
405 Unixsws => null,
406 Switches => Sync_Switches'Access,
407 Params => new Parameter_Array'(1 => Unlimited_Files),
408 Defext => " "),
410 Elim =>
411 (Cname => new S'("ELIM"),
412 Usage => new S'("GNAT ELIM name /qualifiers"),
413 VMS_Only => False,
414 Unixcmd => new S'("gnatelim"),
415 Unixsws => null,
416 Switches => Elim_Switches'Access,
417 Params => new Parameter_Array'(1 => Other_As_Is),
418 Defext => "ali"),
420 Find =>
421 (Cname => new S'("FIND"),
422 Usage => new S'("GNAT FIND pattern[:sourcefile[:line"
423 & "[:column]]] filespec[,...] /qualifiers"),
424 VMS_Only => False,
425 Unixcmd => new S'("gnatfind"),
426 Unixsws => null,
427 Switches => Find_Switches'Access,
428 Params => new Parameter_Array'(1 => Other_As_Is,
429 2 => Files_Or_Wildcard),
430 Defext => "ali"),
432 Krunch =>
433 (Cname => new S'("KRUNCH"),
434 Usage => new S'("GNAT KRUNCH file [/COUNT=nnn]"),
435 VMS_Only => False,
436 Unixcmd => new S'("gnatkr"),
437 Unixsws => null,
438 Switches => Krunch_Switches'Access,
439 Params => new Parameter_Array'(1 => File),
440 Defext => " "),
442 Link =>
443 (Cname => new S'("LINK"),
444 Usage => new S'("GNAT LINK file[.ali]"
445 & " [extra obj_&_lib_&_exe_&_opt files]"
446 & " /qualifiers"),
447 VMS_Only => False,
448 Unixcmd => new S'("gnatlink"),
449 Unixsws => null,
450 Switches => Link_Switches'Access,
451 Params => new Parameter_Array'(1 => Unlimited_Files),
452 Defext => "ali"),
454 List =>
455 (Cname => new S'("LIST"),
456 Usage => new S'("GNAT LIST /qualifiers object_or_ali_file"),
457 VMS_Only => False,
458 Unixcmd => new S'("gnatls"),
459 Unixsws => null,
460 Switches => List_Switches'Access,
461 Params => new Parameter_Array'(1 => Unlimited_Files),
462 Defext => "ali"),
464 Make =>
465 (Cname => new S'("MAKE"),
466 Usage => new S'("GNAT MAKE file(s) /qualifiers (includes "
467 & "COMPILE /qualifiers)"),
468 VMS_Only => False,
469 Unixcmd => new S'("gnatmake"),
470 Unixsws => null,
471 Switches => Make_Switches'Access,
472 Params => new Parameter_Array'(1 => Unlimited_Files),
473 Defext => " "),
475 Metric =>
476 (Cname => new S'("METRIC"),
477 Usage => new S'("GNAT METRIC /qualifiers source_file"),
478 VMS_Only => False,
479 Unixcmd => new S'("gnatmetric"),
480 Unixsws => null,
481 Switches => Metric_Switches'Access,
482 Params => new Parameter_Array'(1 => Unlimited_Files),
483 Defext => " "),
485 Name =>
486 (Cname => new S'("NAME"),
487 Usage => new S'("GNAT NAME /qualifiers naming-pattern "
488 & "[naming-patterns]"),
489 VMS_Only => False,
490 Unixcmd => new S'("gnatname"),
491 Unixsws => null,
492 Switches => Name_Switches'Access,
493 Params => new Parameter_Array'(1 => Unlimited_As_Is),
494 Defext => " "),
496 Preprocess =>
497 (Cname => new S'("PREPROCESS"),
498 Usage =>
499 new S'("GNAT PREPROCESS ifile ofile dfile /qualifiers"),
500 VMS_Only => False,
501 Unixcmd => new S'("gnatprep"),
502 Unixsws => null,
503 Switches => Prep_Switches'Access,
504 Params => new Parameter_Array'(1 .. 3 => File),
505 Defext => " "),
507 Pretty =>
508 (Cname => new S'("PRETTY"),
509 Usage => new S'("GNAT PRETTY /qualifiers source_file"),
510 VMS_Only => False,
511 Unixcmd => new S'("gnatpp"),
512 Unixsws => null,
513 Switches => Pretty_Switches'Access,
514 Params => new Parameter_Array'(1 => Unlimited_Files),
515 Defext => " "),
517 Shared =>
518 (Cname => new S'("SHARED"),
519 Usage => new S'("GNAT SHARED [obj_&_lib_&_exe_&_opt"
520 & "files] /qualifiers"),
521 VMS_Only => True,
522 Unixcmd => new S'("gcc"),
523 Unixsws =>
524 new Argument_List'(new String'("-shared") & Init_Object_Dirs),
525 Switches => Shared_Switches'Access,
526 Params => new Parameter_Array'(1 => Unlimited_Files),
527 Defext => " "),
529 Stack =>
530 (Cname => new S'("STACK"),
531 Usage => new S'("GNAT STACK /qualifiers ci_files"),
532 VMS_Only => False,
533 Unixcmd => new S'("gnatstack"),
534 Unixsws => null,
535 Switches => Stack_Switches'Access,
536 Params => new Parameter_Array'(1 => Unlimited_Files),
537 Defext => "ci" & ASCII.NUL),
539 Stub =>
540 (Cname => new S'("STUB"),
541 Usage => new S'("GNAT STUB file [directory]/qualifiers"),
542 VMS_Only => False,
543 Unixcmd => new S'("gnatstub"),
544 Unixsws => null,
545 Switches => Stub_Switches'Access,
546 Params => new Parameter_Array'(1 => File, 2 => Optional_File),
547 Defext => " "),
549 Test =>
550 (Cname => new S'("TEST"),
551 Usage => new S'("GNAT TEST file(s) /qualifiers"),
552 VMS_Only => False,
553 Unixcmd => new S'("gnattest"),
554 Unixsws => null,
555 Switches => Make_Switches'Access,
556 Params => new Parameter_Array'(1 => Unlimited_Files),
557 Defext => " "),
559 Xref =>
560 (Cname => new S'("XREF"),
561 Usage => new S'("GNAT XREF filespec[,...] /qualifiers"),
562 VMS_Only => False,
563 Unixcmd => new S'("gnatxref"),
564 Unixsws => null,
565 Switches => Xref_Switches'Access,
566 Params => new Parameter_Array'(1 => Files_Or_Wildcard),
567 Defext => "ali")
569 end Initialize;
571 ------------------
572 -- Invert_Sense --
573 ------------------
575 function Invert_Sense (S : String) return VMS_Data.String_Ptr is
576 Sinv : String (1 .. S'Length * 2);
577 -- Result (for sure long enough)
579 Sinvp : Natural := 0;
580 -- Pointer to output string
582 begin
583 for Sp in S'Range loop
584 if Sp = S'First or else S (Sp - 1) = ',' then
585 if S (Sp) = '!' then
586 null;
587 else
588 Sinv (Sinvp + 1) := '!';
589 Sinv (Sinvp + 2) := S (Sp);
590 Sinvp := Sinvp + 2;
591 end if;
593 else
594 Sinv (Sinvp + 1) := S (Sp);
595 Sinvp := Sinvp + 1;
596 end if;
597 end loop;
599 return new String'(Sinv (1 .. Sinvp));
600 end Invert_Sense;
602 ----------------------
603 -- Is_Extensionless --
604 ----------------------
606 function Is_Extensionless (F : String) return Boolean is
607 begin
608 for J in reverse F'Range loop
609 if F (J) = '.' then
610 return False;
611 elsif F (J) = '/' or else F (J) = ']' or else F (J) = ':' then
612 return True;
613 end if;
614 end loop;
616 return True;
617 end Is_Extensionless;
619 -----------
620 -- Match --
621 -----------
623 function Match (S1, S2 : String) return Boolean is
624 Dif : constant Integer := S2'First - S1'First;
626 begin
628 if S1'Length /= S2'Length then
629 return False;
631 else
632 for J in S1'Range loop
633 if To_Lower (S1 (J)) /= To_Lower (S2 (J + Dif)) then
634 return False;
635 end if;
636 end loop;
638 return True;
639 end if;
640 end Match;
642 ------------------
643 -- Match_Prefix --
644 ------------------
646 function Match_Prefix (S1, S2 : String) return Boolean is
647 begin
648 if S1'Length > S2'Length then
649 return False;
650 else
651 return Match (S1, S2 (S2'First .. S2'First + S1'Length - 1));
652 end if;
653 end Match_Prefix;
655 -------------------
656 -- Matching_Name --
657 -------------------
659 function Matching_Name
660 (S : String;
661 Itm : Item_Ptr;
662 Quiet : Boolean := False) return Item_Ptr
664 P1, P2 : Item_Ptr;
666 procedure Err;
667 -- Little procedure to output command/qualifier/option as appropriate
668 -- and bump error count.
670 ---------
671 -- Err --
672 ---------
674 procedure Err is
675 begin
676 if Quiet then
677 return;
678 end if;
680 Errors := Errors + 1;
682 if Itm /= null then
683 case Itm.Id is
684 when Id_Command =>
685 Put (Standard_Error, "command");
687 when Id_Switch =>
688 if Hostparm.OpenVMS then
689 Put (Standard_Error, "qualifier");
690 else
691 Put (Standard_Error, "switch");
692 end if;
694 when Id_Option =>
695 Put (Standard_Error, "option");
697 end case;
698 else
699 Put (Standard_Error, "input");
701 end if;
703 Put (Standard_Error, ": ");
704 Put (Standard_Error, S);
705 end Err;
707 -- Start of processing for Matching_Name
709 begin
710 -- If exact match, that's the one we want
712 P1 := Itm;
713 while P1 /= null loop
714 if Match (S, P1.Name.all) then
715 return P1;
716 else
717 P1 := P1.Next;
718 end if;
719 end loop;
721 -- Now check for prefix matches
723 P1 := Itm;
724 while P1 /= null loop
725 if P1.Name.all = "/<other>" then
726 return P1;
728 elsif not Match_Prefix (S, P1.Name.all) then
729 P1 := P1.Next;
731 else
732 -- Here we have found one matching prefix, so see if there is
733 -- another one (which is an ambiguity)
735 P2 := P1.Next;
736 while P2 /= null loop
737 if Match_Prefix (S, P2.Name.all) then
738 if not Quiet then
739 Put (Standard_Error, "ambiguous ");
740 Err;
741 Put (Standard_Error, " (matches ");
742 Put (Standard_Error, P1.Name.all);
744 while P2 /= null loop
745 if Match_Prefix (S, P2.Name.all) then
746 Put (Standard_Error, ',');
747 Put (Standard_Error, P2.Name.all);
748 end if;
750 P2 := P2.Next;
751 end loop;
753 Put_Line (Standard_Error, ")");
754 end if;
756 return null;
757 end if;
759 P2 := P2.Next;
760 end loop;
762 -- If we fall through that loop, then there was only one match
764 return P1;
765 end if;
766 end loop;
768 -- If we fall through outer loop, there was no match
770 if not Quiet then
771 Put (Standard_Error, "unrecognized ");
772 Err;
773 New_Line (Standard_Error);
774 end if;
776 return null;
777 end Matching_Name;
779 -----------------------
780 -- OK_Alphanumerplus --
781 -----------------------
783 function OK_Alphanumerplus (S : String) return Boolean is
784 begin
785 if S'Length = 0 then
786 return False;
788 else
789 for J in S'Range loop
790 if not (Is_Alphanumeric (S (J)) or else
791 S (J) = '_' or else S (J) = '$')
792 then
793 return False;
794 end if;
795 end loop;
797 return True;
798 end if;
799 end OK_Alphanumerplus;
801 ----------------
802 -- OK_Integer --
803 ----------------
805 function OK_Integer (S : String) return Boolean is
806 begin
807 if S'Length = 0 then
808 return False;
810 else
811 for J in S'Range loop
812 if not Is_Digit (S (J)) then
813 return False;
814 end if;
815 end loop;
817 return True;
818 end if;
819 end OK_Integer;
821 --------------------
822 -- Output_Version --
823 --------------------
825 procedure Output_Version is
826 begin
827 if AAMP_On_Target then
828 Put ("GNAAMP ");
829 else
830 Put ("GNAT ");
831 end if;
833 Put_Line (Gnatvsn.Gnat_Version_String);
834 Put_Line ("Copyright 1996-" &
835 Current_Year &
836 ", Free Software Foundation, Inc.");
837 end Output_Version;
839 -----------
840 -- Place --
841 -----------
843 procedure Place (C : Character) is
844 begin
845 if Cargs then
846 Cargs_Buffer.Append (C);
847 else
848 Buffer.Append (C);
849 end if;
850 end Place;
852 procedure Place (S : String) is
853 begin
854 for J in S'Range loop
855 Place (S (J));
856 end loop;
857 end Place;
859 -----------------
860 -- Place_Lower --
861 -----------------
863 procedure Place_Lower (S : String) is
864 begin
865 for J in S'Range loop
866 Place (To_Lower (S (J)));
867 end loop;
868 end Place_Lower;
870 -------------------------
871 -- Place_Unix_Switches --
872 -------------------------
874 procedure Place_Unix_Switches (S : VMS_Data.String_Ptr) is
875 P1, P2, P3 : Natural;
876 Remove : Boolean;
877 Slen, Sln2 : Natural;
878 Wild_Card : Boolean := False;
880 begin
881 P1 := S'First;
882 while P1 <= S'Last loop
883 if S (P1) = '!' then
884 P1 := P1 + 1;
885 Remove := True;
886 else
887 Remove := False;
888 end if;
890 P2 := P1;
891 pragma Assert (S (P1) = '-' or else S (P1) = '`');
893 while P2 < S'Last and then S (P2 + 1) /= ',' loop
894 P2 := P2 + 1;
895 end loop;
897 -- Switch is now in S (P1 .. P2)
899 Slen := P2 - P1 + 1;
901 if Remove then
902 Wild_Card := S (P2) = '*';
904 if Wild_Card then
905 Slen := Slen - 1;
906 P2 := P2 - 1;
907 end if;
909 P3 := 1;
910 while P3 <= Buffer.Last - Slen loop
911 if Buffer.Table (P3) = ' '
912 and then String (Buffer.Table (P3 + 1 .. P3 + Slen)) =
913 S (P1 .. P2)
914 and then (Wild_Card
915 or else
916 P3 + Slen = Buffer.Last
917 or else
918 Buffer.Table (P3 + Slen + 1) = ' ')
919 then
920 Sln2 := Slen;
922 if Wild_Card then
923 while P3 + Sln2 /= Buffer.Last
924 and then Buffer.Table (P3 + Sln2 + 1) /= ' '
925 loop
926 Sln2 := Sln2 + 1;
927 end loop;
928 end if;
930 Buffer.Table (P3 .. Buffer.Last - Sln2 - 1) :=
931 Buffer.Table (P3 + Sln2 + 1 .. Buffer.Last);
932 Buffer.Set_Last (Buffer.Last - Sln2 - 1);
934 else
935 P3 := P3 + 1;
936 end if;
937 end loop;
939 if Wild_Card then
940 P2 := P2 + 1;
941 end if;
943 else
944 pragma Assert (S (P2) /= '*');
945 Place (' ');
947 if S (P1) = '`' then
948 P1 := P1 + 1;
949 end if;
951 Place (S (P1 .. P2));
952 end if;
954 P1 := P2 + 2;
955 end loop;
956 end Place_Unix_Switches;
958 -----------------------------
959 -- Preprocess_Command_Data --
960 -----------------------------
962 procedure Preprocess_Command_Data is
963 begin
964 for C in Real_Command_Type loop
965 declare
966 Command : constant Item_Ptr := new Command_Item;
968 Last_Switch : Item_Ptr;
969 -- Last switch in list
971 begin
972 -- Link new command item into list of commands
974 if Last_Command = null then
975 Commands := Command;
976 else
977 Last_Command.Next := Command;
978 end if;
980 Last_Command := Command;
982 -- Fill in fields of new command item
984 Command.Name := Command_List (C).Cname;
985 Command.Usage := Command_List (C).Usage;
986 Command.Command := C;
988 if Command_List (C).Unixsws = null then
989 Command.Unix_String := Command_List (C).Unixcmd;
990 else
991 declare
992 Cmd : String (1 .. 5_000);
993 Last : Natural := 0;
994 Sws : constant Argument_List_Access :=
995 Command_List (C).Unixsws;
997 begin
998 Cmd (1 .. Command_List (C).Unixcmd'Length) :=
999 Command_List (C).Unixcmd.all;
1000 Last := Command_List (C).Unixcmd'Length;
1002 for J in Sws'Range loop
1003 Last := Last + 1;
1004 Cmd (Last) := ' ';
1005 Cmd (Last + 1 .. Last + Sws (J)'Length) :=
1006 Sws (J).all;
1007 Last := Last + Sws (J)'Length;
1008 end loop;
1010 Command.Unix_String := new String'(Cmd (1 .. Last));
1011 end;
1012 end if;
1014 Command.Params := Command_List (C).Params;
1015 Command.Defext := Command_List (C).Defext;
1017 Validate_Command_Or_Option (Command.Name);
1019 -- Process the switch list
1021 for S in Command_List (C).Switches'Range loop
1022 declare
1023 SS : constant VMS_Data.String_Ptr :=
1024 Command_List (C).Switches (S);
1025 P : Natural := SS'First;
1026 Sw : Item_Ptr := new Switch_Item;
1028 Last_Opt : Item_Ptr;
1029 -- Pointer to last option
1031 begin
1032 -- Link new switch item into list of switches
1034 if Last_Switch = null then
1035 Command.Switches := Sw;
1036 else
1037 Last_Switch.Next := Sw;
1038 end if;
1040 Last_Switch := Sw;
1042 -- Process switch string, first get name
1044 while SS (P) /= ' ' and then SS (P) /= '=' loop
1045 P := P + 1;
1046 end loop;
1048 Sw.Name := new String'(SS (SS'First .. P - 1));
1050 -- Direct translation case
1052 if SS (P) = ' ' then
1053 Sw.Translation := T_Direct;
1054 Sw.Unix_String := new String'(SS (P + 1 .. SS'Last));
1055 Validate_Unix_Switch (Sw.Unix_String);
1057 if SS (P - 1) = '>' then
1058 Sw.Translation := T_Other;
1060 elsif SS (P + 1) = '`' then
1061 null;
1063 -- Create the inverted case (/NO ..)
1065 elsif SS (SS'First + 1 .. SS'First + 2) /= "NO" then
1066 Sw := new Switch_Item;
1067 Last_Switch.Next := Sw;
1068 Last_Switch := Sw;
1070 Sw.Name :=
1071 new String'("/NO" & SS (SS'First + 1 .. P - 1));
1072 Sw.Translation := T_Direct;
1073 Sw.Unix_String := Invert_Sense (SS (P + 1 .. SS'Last));
1074 Validate_Unix_Switch (Sw.Unix_String);
1075 end if;
1077 -- Directories translation case
1079 elsif SS (P + 1) = '*' then
1080 pragma Assert (SS (SS'Last) = '*');
1081 Sw.Translation := T_Directories;
1082 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1083 Validate_Unix_Switch (Sw.Unix_String);
1085 -- Directory translation case
1087 elsif SS (P + 1) = '%' then
1088 pragma Assert (SS (SS'Last) = '%');
1089 Sw.Translation := T_Directory;
1090 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1091 Validate_Unix_Switch (Sw.Unix_String);
1093 -- File translation case
1095 elsif SS (P + 1) = '@' then
1096 pragma Assert (SS (SS'Last) = '@');
1097 Sw.Translation := T_File;
1098 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1099 Validate_Unix_Switch (Sw.Unix_String);
1101 -- No space file translation case
1103 elsif SS (P + 1) = '<' then
1104 pragma Assert (SS (SS'Last) = '>');
1105 Sw.Translation := T_No_Space_File;
1106 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1107 Validate_Unix_Switch (Sw.Unix_String);
1109 -- Numeric translation case
1111 elsif SS (P + 1) = '#' then
1112 pragma Assert (SS (SS'Last) = '#');
1113 Sw.Translation := T_Numeric;
1114 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1115 Validate_Unix_Switch (Sw.Unix_String);
1117 -- Alphanumerplus translation case
1119 elsif SS (P + 1) = '|' then
1120 pragma Assert (SS (SS'Last) = '|');
1121 Sw.Translation := T_Alphanumplus;
1122 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1123 Validate_Unix_Switch (Sw.Unix_String);
1125 -- String translation case
1127 elsif SS (P + 1) = '"' then
1128 pragma Assert (SS (SS'Last) = '"');
1129 Sw.Translation := T_String;
1130 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last - 1));
1131 Validate_Unix_Switch (Sw.Unix_String);
1133 -- Commands translation case
1135 elsif SS (P + 1) = '?' then
1136 Sw.Translation := T_Commands;
1137 Sw.Unix_String := new String'(SS (P + 2 .. SS'Last));
1139 -- Options translation case
1141 else
1142 Sw.Translation := T_Options;
1143 Sw.Unix_String := new String'("");
1145 P := P + 1; -- bump past =
1146 while P <= SS'Last loop
1147 declare
1148 Opt : constant Item_Ptr := new Option_Item;
1149 Q : Natural;
1151 begin
1152 -- Link new option item into options list
1154 if Last_Opt = null then
1155 Sw.Options := Opt;
1156 else
1157 Last_Opt.Next := Opt;
1158 end if;
1160 Last_Opt := Opt;
1162 -- Fill in fields of new option item
1164 Q := P;
1165 while SS (Q) /= ' ' loop
1166 Q := Q + 1;
1167 end loop;
1169 Opt.Name := new String'(SS (P .. Q - 1));
1170 Validate_Command_Or_Option (Opt.Name);
1172 P := Q + 1;
1173 Q := P;
1175 while Q <= SS'Last and then SS (Q) /= ' ' loop
1176 Q := Q + 1;
1177 end loop;
1179 Opt.Unix_String := new String'(SS (P .. Q - 1));
1180 Validate_Unix_Switch (Opt.Unix_String);
1181 P := Q + 1;
1182 end;
1183 end loop;
1184 end if;
1185 end;
1186 end loop;
1187 end;
1188 end loop;
1189 end Preprocess_Command_Data;
1191 ----------------------
1192 -- Process_Argument --
1193 ----------------------
1195 procedure Process_Argument (The_Command : in out Command_Type) is
1196 Argv : String_Access;
1197 Arg_Idx : Integer;
1199 function Get_Arg_End
1200 (Argv : String;
1201 Arg_Idx : Integer) return Integer;
1202 -- Begins looking at Arg_Idx + 1 and returns the index of the
1203 -- last character before a slash or else the index of the last
1204 -- character in the string Argv.
1206 -----------------
1207 -- Get_Arg_End --
1208 -----------------
1210 function Get_Arg_End
1211 (Argv : String;
1212 Arg_Idx : Integer) return Integer
1214 begin
1215 for J in Arg_Idx + 1 .. Argv'Last loop
1216 if Argv (J) = '/' then
1217 return J - 1;
1218 end if;
1219 end loop;
1221 return Argv'Last;
1222 end Get_Arg_End;
1224 -- Start of processing for Process_Argument
1226 begin
1227 Cargs := False;
1229 -- If an argument file is open, read the next non empty line
1231 if Is_Open (Arg_File) then
1232 declare
1233 Line : String (1 .. 256);
1234 Last : Natural;
1235 begin
1236 loop
1237 Get_Line (Arg_File, Line, Last);
1238 exit when Last /= 0 or else End_Of_File (Arg_File);
1239 end loop;
1241 -- If the end of the argument file has been reached, close it
1243 if End_Of_File (Arg_File) then
1244 Close (Arg_File);
1246 -- If the last line was empty, return after increasing Arg_Num
1247 -- to go to the next argument on the comment line.
1249 if Last = 0 then
1250 Arg_Num := Arg_Num + 1;
1251 return;
1252 end if;
1253 end if;
1255 Argv := new String'(Line (1 .. Last));
1256 Arg_Idx := 1;
1258 if Argv (1) = '@' then
1259 Put_Line (Standard_Error, "argument file cannot contain @cmd");
1260 raise Error_Exit;
1261 end if;
1262 end;
1264 else
1265 -- No argument file is open, get the argument on the command line
1267 Argv := new String'(Argument (Arg_Num));
1268 Arg_Idx := Argv'First;
1270 -- Check if this is the specification of an argument file
1272 if Argv (Arg_Idx) = '@' then
1273 -- The first argument on the command line cannot be an argument
1274 -- file.
1276 if Arg_Num = 1 then
1277 Put_Line
1278 (Standard_Error,
1279 "Cannot specify argument line before command");
1280 raise Error_Exit;
1281 end if;
1283 -- Open the file, after conversion of the name to canonical form.
1284 -- Fail if file is not found.
1286 declare
1287 Canonical_File_Name : String_Access :=
1288 To_Canonical_File_Spec (Argv (Arg_Idx + 1 .. Argv'Last));
1289 begin
1290 Open (Arg_File, In_File, Canonical_File_Name.all);
1291 Free (Canonical_File_Name);
1292 return;
1294 exception
1295 when others =>
1296 Put (Standard_Error, "Cannot open argument file """);
1297 Put (Standard_Error, Argv (Arg_Idx + 1 .. Argv'Last));
1298 Put_Line (Standard_Error, """");
1299 raise Error_Exit;
1300 end;
1301 end if;
1302 end if;
1304 <<Tryagain_After_Coalesce>>
1305 loop
1306 declare
1307 Next_Arg_Idx : Integer;
1308 Arg : String_Access;
1310 begin
1311 Next_Arg_Idx := Get_Arg_End (Argv.all, Arg_Idx);
1312 Arg := new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1314 -- The first one must be a command name
1316 if Arg_Num = 1 and then Arg_Idx = Argv'First then
1317 Command := Matching_Name (Arg.all, Commands);
1319 if Command = null then
1320 raise Error_Exit;
1321 end if;
1323 The_Command := Command.Command;
1324 Output_File_Expected := False;
1326 -- Give usage information if only command given
1328 if Argument_Count = 1
1329 and then Next_Arg_Idx = Argv'Last
1330 then
1331 Output_Version;
1332 New_Line;
1333 Put_Line
1334 ("List of available qualifiers and options");
1335 New_Line;
1337 Put (Command.Usage.all);
1338 Set_Col (53);
1339 Put_Line (Command.Unix_String.all);
1341 declare
1342 Sw : Item_Ptr := Command.Switches;
1344 begin
1345 while Sw /= null loop
1346 Put (" ");
1347 Put (Sw.Name.all);
1349 case Sw.Translation is
1351 when T_Other =>
1352 Set_Col (53);
1353 Put_Line (Sw.Unix_String.all &
1354 "/<other>");
1356 when T_Direct =>
1357 Set_Col (53);
1358 Put_Line (Sw.Unix_String.all);
1360 when T_Directories =>
1361 Put ("=(direc,direc,..direc)");
1362 Set_Col (53);
1363 Put (Sw.Unix_String.all);
1364 Put (" direc ");
1365 Put (Sw.Unix_String.all);
1366 Put_Line (" direc ...");
1368 when T_Directory =>
1369 Put ("=directory");
1370 Set_Col (53);
1371 Put (Sw.Unix_String.all);
1373 if Sw.Unix_String (Sw.Unix_String'Last)
1374 /= '='
1375 then
1376 Put (' ');
1377 end if;
1379 Put_Line ("directory ");
1381 when T_File | T_No_Space_File =>
1382 Put ("=file");
1383 Set_Col (53);
1384 Put (Sw.Unix_String.all);
1386 if Sw.Translation = T_File
1387 and then Sw.Unix_String
1388 (Sw.Unix_String'Last) /= '='
1389 then
1390 Put (' ');
1391 end if;
1393 Put_Line ("file ");
1395 when T_Numeric =>
1396 Put ("=nnn");
1397 Set_Col (53);
1399 if Sw.Unix_String
1400 (Sw.Unix_String'First) = '`'
1401 then
1402 Put (Sw.Unix_String
1403 (Sw.Unix_String'First + 1
1404 .. Sw.Unix_String'Last));
1405 else
1406 Put (Sw.Unix_String.all);
1407 end if;
1409 Put_Line ("nnn");
1411 when T_Alphanumplus =>
1412 Put ("=xyz");
1413 Set_Col (53);
1415 if Sw.Unix_String
1416 (Sw.Unix_String'First) = '`'
1417 then
1418 Put (Sw.Unix_String
1419 (Sw.Unix_String'First + 1
1420 .. Sw.Unix_String'Last));
1421 else
1422 Put (Sw.Unix_String.all);
1423 end if;
1425 Put_Line ("xyz");
1427 when T_String =>
1428 Put ("=");
1429 Put ('"');
1430 Put ("<string>");
1431 Put ('"');
1432 Set_Col (53);
1434 Put (Sw.Unix_String.all);
1436 if Sw.Unix_String
1437 (Sw.Unix_String'Last) /= '='
1438 then
1439 Put (' ');
1440 end if;
1442 Put ("<string>");
1443 New_Line;
1445 when T_Commands =>
1446 Put (" (switches for ");
1447 Put (Sw.Unix_String
1448 (Sw.Unix_String'First + 7
1449 .. Sw.Unix_String'Last));
1450 Put (')');
1451 Set_Col (53);
1452 Put (Sw.Unix_String
1453 (Sw.Unix_String'First
1454 .. Sw.Unix_String'First + 5));
1455 Put_Line (" switches");
1457 when T_Options =>
1458 declare
1459 Opt : Item_Ptr := Sw.Options;
1461 begin
1462 Put_Line ("=(option,option..)");
1464 while Opt /= null loop
1465 Put (" ");
1466 Put (Opt.Name.all);
1468 if Opt = Sw.Options then
1469 Put (" (D)");
1470 end if;
1472 Set_Col (53);
1473 Put_Line (Opt.Unix_String.all);
1474 Opt := Opt.Next;
1475 end loop;
1476 end;
1478 end case;
1480 Sw := Sw.Next;
1481 end loop;
1482 end;
1484 raise Normal_Exit;
1485 end if;
1487 -- Special handling for internal debugging switch /?
1489 elsif Arg.all = "/?" then
1490 Display_Command := True;
1491 Output_File_Expected := False;
1493 -- Special handling of internal option /KEEP_TEMPORARY_FILES
1495 elsif Arg'Length >= 7
1496 and then Matching_Name
1497 (Arg.all, Keep_Temps_Option, True) /= null
1498 then
1499 Opt.Keep_Temporary_Files := True;
1501 -- Copy -switch unchanged, as well as +rule
1503 elsif Arg (Arg'First) = '-' or else Arg (Arg'First) = '+' then
1504 Place (' ');
1505 Place (Arg.all);
1507 -- Set Output_File_Expected for the next argument
1509 Output_File_Expected :=
1510 Arg.all = "-o" and then The_Command = Link;
1512 -- Copy quoted switch with quotes stripped
1514 elsif Arg (Arg'First) = '"' then
1515 if Arg (Arg'Last) /= '"' then
1516 Put (Standard_Error, "misquoted argument: ");
1517 Put_Line (Standard_Error, Arg.all);
1518 Errors := Errors + 1;
1520 else
1521 Place (' ');
1522 Place (Arg (Arg'First + 1 .. Arg'Last - 1));
1523 end if;
1525 Output_File_Expected := False;
1527 -- Parameter Argument
1529 elsif Arg (Arg'First) /= '/'
1530 and then Make_Commands_Active = null
1531 then
1532 Param_Count := Param_Count + 1;
1534 if Param_Count <= Command.Params'Length then
1536 case Command.Params (Param_Count) is
1538 when File | Optional_File =>
1539 declare
1540 Normal_File : constant String_Access :=
1541 To_Canonical_File_Spec
1542 (Arg.all);
1544 begin
1545 Place (' ');
1546 Place_Lower (Normal_File.all);
1548 if Is_Extensionless (Normal_File.all)
1549 and then Command.Defext /= " "
1550 then
1551 Place ('.');
1552 Place (Command.Defext);
1553 end if;
1554 end;
1556 when Unlimited_Files =>
1557 declare
1558 Normal_File : constant String_Access :=
1559 To_Canonical_File_Spec
1560 (Arg.all);
1562 File_Is_Wild : Boolean := False;
1563 File_List : String_Access_List_Access;
1565 begin
1566 for J in Arg'Range loop
1567 if Arg (J) = '*'
1568 or else Arg (J) = '%'
1569 then
1570 File_Is_Wild := True;
1571 end if;
1572 end loop;
1574 if File_Is_Wild then
1575 File_List := To_Canonical_File_List
1576 (Arg.all, False);
1578 for J in File_List.all'Range loop
1579 Place (' ');
1580 Place_Lower (File_List.all (J).all);
1581 end loop;
1583 else
1584 Place (' ');
1585 Place_Lower (Normal_File.all);
1587 -- Add extension if not present, except after
1588 -- switch -o.
1590 if Is_Extensionless (Normal_File.all)
1591 and then Command.Defext /= " "
1592 and then not Output_File_Expected
1593 then
1594 Place ('.');
1595 Place (Command.Defext);
1596 end if;
1597 end if;
1599 Param_Count := Param_Count - 1;
1600 end;
1602 when Other_As_Is =>
1603 Place (' ');
1604 Place (Arg.all);
1606 when Unlimited_As_Is =>
1607 Place (' ');
1608 Place (Arg.all);
1609 Param_Count := Param_Count - 1;
1611 when Files_Or_Wildcard =>
1613 -- Remove spaces from a comma separated list
1614 -- of file names and adjust control variables
1615 -- accordingly.
1617 while Arg_Num < Argument_Count and then
1618 (Argv (Argv'Last) = ',' xor
1619 Argument (Arg_Num + 1)
1620 (Argument (Arg_Num + 1)'First) = ',')
1621 loop
1622 Argv := new String'
1623 (Argv.all & Argument (Arg_Num + 1));
1624 Arg_Num := Arg_Num + 1;
1625 Arg_Idx := Argv'First;
1626 Next_Arg_Idx :=
1627 Get_Arg_End (Argv.all, Arg_Idx);
1628 Arg := new String'
1629 (Argv (Arg_Idx .. Next_Arg_Idx));
1630 end loop;
1632 -- Parse the comma separated list of VMS
1633 -- filenames and place them on the command
1634 -- line as space separated Unix style
1635 -- filenames. Lower case and add default
1636 -- extension as appropriate.
1638 declare
1639 Arg1_Idx : Integer := Arg'First;
1641 function Get_Arg1_End
1642 (Arg : String;
1643 Arg_Idx : Integer) return Integer;
1644 -- Begins looking at Arg_Idx + 1 and
1645 -- returns the index of the last character
1646 -- before a comma or else the index of the
1647 -- last character in the string Arg.
1649 ------------------
1650 -- Get_Arg1_End --
1651 ------------------
1653 function Get_Arg1_End
1654 (Arg : String;
1655 Arg_Idx : Integer) return Integer
1657 begin
1658 for J in Arg_Idx + 1 .. Arg'Last loop
1659 if Arg (J) = ',' then
1660 return J - 1;
1661 end if;
1662 end loop;
1664 return Arg'Last;
1665 end Get_Arg1_End;
1667 begin
1668 loop
1669 declare
1670 Next_Arg1_Idx :
1671 constant Integer :=
1672 Get_Arg1_End (Arg.all, Arg1_Idx);
1674 Arg1 :
1675 constant String :=
1676 Arg (Arg1_Idx .. Next_Arg1_Idx);
1678 Normal_File :
1679 constant String_Access :=
1680 To_Canonical_File_Spec (Arg1);
1682 begin
1683 Place (' ');
1684 Place_Lower (Normal_File.all);
1686 if Is_Extensionless (Normal_File.all)
1687 and then Command.Defext /= " "
1688 then
1689 Place ('.');
1690 Place (Command.Defext);
1691 end if;
1693 Arg1_Idx := Next_Arg1_Idx + 1;
1694 end;
1696 exit when Arg1_Idx > Arg'Last;
1698 -- Don't allow two or more commas in
1699 -- a row
1701 if Arg (Arg1_Idx) = ',' then
1702 Arg1_Idx := Arg1_Idx + 1;
1703 if Arg1_Idx > Arg'Last or else
1704 Arg (Arg1_Idx) = ','
1705 then
1706 Put_Line
1707 (Standard_Error,
1708 "Malformed Parameter: " &
1709 Arg.all);
1710 Put (Standard_Error, "usage: ");
1711 Put_Line (Standard_Error,
1712 Command.Usage.all);
1713 raise Error_Exit;
1714 end if;
1715 end if;
1717 end loop;
1718 end;
1719 end case;
1720 end if;
1722 -- Reset Output_File_Expected, in case it was True
1724 Output_File_Expected := False;
1726 -- Qualifier argument
1728 else
1729 Output_File_Expected := False;
1731 Cargs := Command.Name.all = "COMPILE";
1733 -- This code is too heavily nested, should be
1734 -- separated out as separate subprogram ???
1736 declare
1737 Sw : Item_Ptr;
1738 SwP : Natural;
1739 P2 : Natural;
1740 Endp : Natural := 0; -- avoid warning
1741 Opt : Item_Ptr;
1743 begin
1744 SwP := Arg'First;
1745 while SwP < Arg'Last
1746 and then Arg (SwP + 1) /= '='
1747 loop
1748 SwP := SwP + 1;
1749 end loop;
1751 -- At this point, the switch name is in
1752 -- Arg (Arg'First..SwP) and if that is not the
1753 -- whole switch, then there is an equal sign at
1754 -- Arg (SwP + 1) and the rest of Arg is what comes
1755 -- after the equal sign.
1757 -- If make commands are active, see if we have
1758 -- another COMMANDS_TRANSLATION switch belonging
1759 -- to gnatmake.
1761 if Make_Commands_Active /= null then
1762 Sw :=
1763 Matching_Name
1764 (Arg (Arg'First .. SwP),
1765 Command.Switches,
1766 Quiet => True);
1768 if Sw /= null
1769 and then Sw.Translation = T_Commands
1770 then
1771 null;
1773 else
1774 Sw :=
1775 Matching_Name
1776 (Arg (Arg'First .. SwP),
1777 Make_Commands_Active.Switches,
1778 Quiet => False);
1779 end if;
1781 -- For case of GNAT MAKE or CHOP, if we cannot
1782 -- find the switch, then see if it is a
1783 -- recognized compiler switch instead, and if
1784 -- so process the compiler switch.
1786 elsif Command.Name.all = "MAKE"
1787 or else
1788 Command.Name.all = "CHOP"
1789 then
1790 Sw :=
1791 Matching_Name
1792 (Arg (Arg'First .. SwP),
1793 Command.Switches,
1794 Quiet => True);
1796 if Sw = null then
1797 Sw :=
1798 Matching_Name
1799 (Arg (Arg'First .. SwP),
1800 Matching_Name
1801 ("COMPILE", Commands).Switches,
1802 Quiet => False);
1803 end if;
1805 -- For all other cases, just search the relevant
1806 -- command.
1808 else
1809 Sw :=
1810 Matching_Name
1811 (Arg (Arg'First .. SwP),
1812 Command.Switches,
1813 Quiet => False);
1815 -- Special case for GNAT COMPILE /UNCHECKED...
1816 -- because the corresponding switch --unchecked... is
1817 -- for gnatmake, not for the compiler.
1819 if Cargs
1820 and then Sw.Name.all = "/UNCHECKED_SHARED_LIB_IMPORTS"
1821 then
1822 Cargs := False;
1823 end if;
1824 end if;
1826 if Sw /= null then
1827 if Cargs
1828 and then Sw.Name /= null
1829 and then
1830 (Sw.Name.all = "/PROJECT_FILE" or else
1831 Sw.Name.all = "/MESSAGES_PROJECT_FILE" or else
1832 Sw.Name.all = "/EXTERNAL_REFERENCE")
1833 then
1834 Cargs := False;
1835 end if;
1837 case Sw.Translation is
1838 when T_Direct =>
1839 Place_Unix_Switches (Sw.Unix_String);
1841 if SwP < Arg'Last
1842 and then Arg (SwP + 1) = '='
1843 then
1844 Put (Standard_Error,
1845 "qualifier options ignored: ");
1846 Put_Line (Standard_Error, Arg.all);
1847 end if;
1849 when T_Directories =>
1850 if SwP + 1 > Arg'Last then
1851 Put (Standard_Error,
1852 "missing directories for: ");
1853 Put_Line (Standard_Error, Arg.all);
1854 Errors := Errors + 1;
1856 elsif Arg (SwP + 2) /= '(' then
1857 SwP := SwP + 2;
1858 Endp := Arg'Last;
1860 elsif Arg (Arg'Last) /= ')' then
1862 -- Remove spaces from a comma separated
1863 -- list of file names and adjust
1864 -- control variables accordingly.
1866 if Arg_Num < Argument_Count and then
1867 (Argv (Argv'Last) = ',' xor
1868 Argument (Arg_Num + 1)
1869 (Argument (Arg_Num + 1)'First) = ',')
1870 then
1871 Argv :=
1872 new String'(Argv.all
1873 & Argument
1874 (Arg_Num + 1));
1875 Arg_Num := Arg_Num + 1;
1876 Arg_Idx := Argv'First;
1877 Next_Arg_Idx :=
1878 Get_Arg_End (Argv.all, Arg_Idx);
1879 Arg :=
1880 new String'(Argv (Arg_Idx .. Next_Arg_Idx));
1881 goto Tryagain_After_Coalesce;
1882 end if;
1884 Put (Standard_Error,
1885 "incorrectly parenthesized " &
1886 "or malformed argument: ");
1887 Put_Line (Standard_Error, Arg.all);
1888 Errors := Errors + 1;
1890 else
1891 SwP := SwP + 3;
1892 Endp := Arg'Last - 1;
1893 end if;
1895 while SwP <= Endp loop
1896 declare
1897 Dir_Is_Wild : Boolean := False;
1898 Dir_Maybe_Is_Wild : Boolean := False;
1900 Dir_List : String_Access_List_Access;
1902 begin
1903 P2 := SwP;
1905 while P2 < Endp
1906 and then Arg (P2 + 1) /= ','
1907 loop
1908 -- A wildcard directory spec on VMS will
1909 -- contain either * or % or ...
1911 if Arg (P2) = '*' then
1912 Dir_Is_Wild := True;
1914 elsif Arg (P2) = '%' then
1915 Dir_Is_Wild := True;
1917 elsif Dir_Maybe_Is_Wild
1918 and then Arg (P2) = '.'
1919 and then Arg (P2 + 1) = '.'
1920 then
1921 Dir_Is_Wild := True;
1922 Dir_Maybe_Is_Wild := False;
1924 elsif Dir_Maybe_Is_Wild then
1925 Dir_Maybe_Is_Wild := False;
1927 elsif Arg (P2) = '.'
1928 and then Arg (P2 + 1) = '.'
1929 then
1930 Dir_Maybe_Is_Wild := True;
1932 end if;
1934 P2 := P2 + 1;
1935 end loop;
1937 if Dir_Is_Wild then
1938 Dir_List :=
1939 To_Canonical_File_List
1940 (Arg (SwP .. P2), True);
1942 for J in Dir_List.all'Range loop
1943 Place_Unix_Switches (Sw.Unix_String);
1944 Place_Lower (Dir_List.all (J).all);
1945 end loop;
1947 else
1948 Place_Unix_Switches (Sw.Unix_String);
1949 Place_Lower
1950 (To_Canonical_Dir_Spec
1951 (Arg (SwP .. P2), False).all);
1952 end if;
1954 SwP := P2 + 2;
1955 end;
1956 end loop;
1958 when T_Directory =>
1959 if SwP + 1 > Arg'Last then
1960 Put (Standard_Error,
1961 "missing directory for: ");
1962 Put_Line (Standard_Error, Arg.all);
1963 Errors := Errors + 1;
1965 else
1966 Place_Unix_Switches (Sw.Unix_String);
1968 -- Some switches end in "=", no space here
1970 if Sw.Unix_String
1971 (Sw.Unix_String'Last) /= '='
1972 then
1973 Place (' ');
1974 end if;
1976 Place_Lower
1977 (To_Canonical_Dir_Spec
1978 (Arg (SwP + 2 .. Arg'Last), False).all);
1979 end if;
1981 when T_File | T_No_Space_File =>
1982 if SwP + 2 > Arg'Last then
1983 Put (Standard_Error, "missing file for: ");
1984 Put_Line (Standard_Error, Arg.all);
1985 Errors := Errors + 1;
1987 else
1988 Place_Unix_Switches (Sw.Unix_String);
1990 -- Some switches end in "=", no space here.
1992 if Sw.Translation = T_File
1993 and then Sw.Unix_String
1994 (Sw.Unix_String'Last) /= '='
1995 then
1996 Place (' ');
1997 end if;
1999 Place_Lower
2000 (To_Canonical_File_Spec
2001 (Arg (SwP + 2 .. Arg'Last)).all);
2002 end if;
2004 when T_Numeric =>
2005 if OK_Integer (Arg (SwP + 2 .. Arg'Last)) then
2006 Place_Unix_Switches (Sw.Unix_String);
2007 Place (Arg (SwP + 2 .. Arg'Last));
2009 else
2010 Put (Standard_Error, "argument for ");
2011 Put (Standard_Error, Sw.Name.all);
2012 Put_Line (Standard_Error, " must be numeric");
2013 Errors := Errors + 1;
2014 end if;
2016 when T_Alphanumplus =>
2017 if OK_Alphanumerplus
2018 (Arg (SwP + 2 .. Arg'Last))
2019 then
2020 Place_Unix_Switches (Sw.Unix_String);
2021 Place (Arg (SwP + 2 .. Arg'Last));
2023 else
2024 Put (Standard_Error, "argument for ");
2025 Put (Standard_Error, Sw.Name.all);
2026 Put_Line (Standard_Error,
2027 " must be alphanumeric");
2028 Errors := Errors + 1;
2029 end if;
2031 when T_String =>
2033 -- A String value must be extended to the end of
2034 -- the Argv, otherwise strings like "foo/bar" get
2035 -- split at the slash.
2037 -- The beginning and ending of the string are
2038 -- flagged with embedded nulls which are removed
2039 -- when building the Spawn call. Nulls are use
2040 -- because they won't show up in a /? output.
2041 -- Quotes aren't used because that would make it
2042 -- difficult to embed them.
2044 Place_Unix_Switches (Sw.Unix_String);
2046 if Next_Arg_Idx /= Argv'Last then
2047 Next_Arg_Idx := Argv'Last;
2048 Arg :=
2049 new String'(Argv (Arg_Idx .. Next_Arg_Idx));
2051 SwP := Arg'First;
2052 while SwP < Arg'Last
2053 and then Arg (SwP + 1) /= '='
2054 loop
2055 SwP := SwP + 1;
2056 end loop;
2057 end if;
2059 Place (ASCII.NUL);
2060 Place (Arg (SwP + 2 .. Arg'Last));
2061 Place (ASCII.NUL);
2063 when T_Commands =>
2065 -- Output -largs/-bargs/-cargs
2067 Place (' ');
2068 Place (Sw.Unix_String
2069 (Sw.Unix_String'First ..
2070 Sw.Unix_String'First + 5));
2072 if Sw.Unix_String
2073 (Sw.Unix_String'First + 7 ..
2074 Sw.Unix_String'Last) = "MAKE"
2075 then
2076 Make_Commands_Active := null;
2078 else
2079 -- Set source of new commands, also setting this
2080 -- non-null indicates that we are in the special
2081 -- commands mode for processing the -xargs case.
2083 Make_Commands_Active :=
2084 Matching_Name
2085 (Sw.Unix_String
2086 (Sw.Unix_String'First + 7 ..
2087 Sw.Unix_String'Last),
2088 Commands);
2089 end if;
2091 when T_Options =>
2092 if SwP + 1 > Arg'Last then
2093 Place_Unix_Switches (Sw.Options.Unix_String);
2094 SwP := Endp + 1;
2096 elsif Arg (SwP + 2) /= '(' then
2097 SwP := SwP + 2;
2098 Endp := Arg'Last;
2100 elsif Arg (Arg'Last) /= ')' then
2101 Put (Standard_Error,
2102 "incorrectly parenthesized argument: ");
2103 Put_Line (Standard_Error, Arg.all);
2104 Errors := Errors + 1;
2105 SwP := Endp + 1;
2107 else
2108 SwP := SwP + 3;
2109 Endp := Arg'Last - 1;
2110 end if;
2112 while SwP <= Endp loop
2113 P2 := SwP;
2114 while P2 < Endp
2115 and then Arg (P2 + 1) /= ','
2116 loop
2117 P2 := P2 + 1;
2118 end loop;
2120 -- Option name is in Arg (SwP .. P2)
2122 Opt := Matching_Name (Arg (SwP .. P2),
2123 Sw.Options);
2125 if Opt /= null then
2126 Place_Unix_Switches (Opt.Unix_String);
2127 end if;
2129 SwP := P2 + 2;
2130 end loop;
2132 when T_Other =>
2133 Place_Unix_Switches
2134 (new String'(Sw.Unix_String.all & Arg.all));
2136 end case;
2137 end if;
2138 end;
2139 end if;
2141 Arg_Idx := Next_Arg_Idx + 1;
2142 end;
2144 exit when Arg_Idx > Argv'Last;
2146 end loop;
2148 if not Is_Open (Arg_File) then
2149 Arg_Num := Arg_Num + 1;
2150 end if;
2151 end Process_Argument;
2153 --------------------
2154 -- Process_Buffer --
2155 --------------------
2157 procedure Process_Buffer (S : String) is
2158 P1, P2 : Natural;
2159 Inside_Nul : Boolean := False;
2160 Arg : String (1 .. 1024);
2161 Arg_Ctr : Natural;
2163 begin
2164 P1 := 1;
2165 while P1 <= S'Last and then S (P1) = ' ' loop
2166 P1 := P1 + 1;
2167 end loop;
2169 Arg_Ctr := 1;
2170 Arg (Arg_Ctr) := S (P1);
2172 while P1 <= S'Last loop
2173 if S (P1) = ASCII.NUL then
2174 if Inside_Nul then
2175 Inside_Nul := False;
2176 else
2177 Inside_Nul := True;
2178 end if;
2179 end if;
2181 if S (P1) = ' ' and then not Inside_Nul then
2182 P1 := P1 + 1;
2183 Arg_Ctr := Arg_Ctr + 1;
2184 Arg (Arg_Ctr) := S (P1);
2186 else
2187 Last_Switches.Increment_Last;
2188 P2 := P1;
2190 while P2 < S'Last
2191 and then (S (P2 + 1) /= ' ' or else
2192 Inside_Nul)
2193 loop
2194 P2 := P2 + 1;
2195 Arg_Ctr := Arg_Ctr + 1;
2196 Arg (Arg_Ctr) := S (P2);
2197 if S (P2) = ASCII.NUL then
2198 Arg_Ctr := Arg_Ctr - 1;
2200 if Inside_Nul then
2201 Inside_Nul := False;
2202 else
2203 Inside_Nul := True;
2204 end if;
2205 end if;
2206 end loop;
2208 Last_Switches.Table (Last_Switches.Last) :=
2209 new String'(String (Arg (1 .. Arg_Ctr)));
2210 P1 := P2 + 2;
2212 exit when P1 > S'Last;
2214 Arg_Ctr := 1;
2215 Arg (Arg_Ctr) := S (P1);
2216 end if;
2217 end loop;
2218 end Process_Buffer;
2220 --------------------------------
2221 -- Validate_Command_Or_Option --
2222 --------------------------------
2224 procedure Validate_Command_Or_Option (N : VMS_Data.String_Ptr) is
2225 begin
2226 pragma Assert (N'Length > 0);
2228 for J in N'Range loop
2229 if N (J) = '_' then
2230 pragma Assert (N (J - 1) /= '_');
2231 null;
2232 else
2233 pragma Assert (Is_Upper (N (J)) or else Is_Digit (N (J)));
2234 null;
2235 end if;
2236 end loop;
2237 end Validate_Command_Or_Option;
2239 --------------------------
2240 -- Validate_Unix_Switch --
2241 --------------------------
2243 procedure Validate_Unix_Switch (S : VMS_Data.String_Ptr) is
2244 begin
2245 if S (S'First) = '`' then
2246 return;
2247 end if;
2249 pragma Assert (S (S'First) = '-' or else S (S'First) = '!');
2251 for J in S'First + 1 .. S'Last loop
2252 pragma Assert (S (J) /= ' ');
2254 if S (J) = '!' then
2255 pragma Assert (S (J - 1) = ',' and then S (J + 1) = '-');
2256 null;
2257 end if;
2258 end loop;
2259 end Validate_Unix_Switch;
2261 --------------------
2262 -- VMS_Conversion --
2263 --------------------
2265 procedure VMS_Conversion (The_Command : out Command_Type) is
2266 Result : Command_Type := Undefined;
2267 Result_Set : Boolean := False;
2269 begin
2270 Buffer.Init;
2272 -- First we must preprocess the string form of the command and options
2273 -- list into the internal form that we use.
2275 Preprocess_Command_Data;
2277 -- If no parameters, give complete list of commands
2279 if Argument_Count = 0 then
2280 Output_Version;
2281 New_Line;
2282 Put_Line ("List of available commands");
2283 New_Line;
2285 while Commands /= null loop
2287 -- No usage for GNAT SYNC
2289 if Commands.Command /= Sync then
2290 Put (Commands.Usage.all);
2291 Set_Col (53);
2292 Put_Line (Commands.Unix_String.all);
2293 end if;
2295 Commands := Commands.Next;
2296 end loop;
2298 raise Normal_Exit;
2299 end if;
2301 -- Loop through arguments
2303 Arg_Num := 1;
2304 while Arg_Num <= Argument_Count loop
2305 Process_Argument (Result);
2307 if not Result_Set then
2308 The_Command := Result;
2309 Result_Set := True;
2310 end if;
2311 end loop;
2313 -- Gross error checking that the number of parameters is correct.
2314 -- Not applicable to Unlimited_Files parameters.
2316 if (Param_Count = Command.Params'Length - 1
2317 and then Command.Params (Param_Count + 1) = Unlimited_Files)
2318 or else Param_Count <= Command.Params'Length
2319 then
2320 null;
2322 else
2323 Put_Line (Standard_Error,
2324 "Parameter count of "
2325 & Integer'Image (Param_Count)
2326 & " not equal to expected "
2327 & Integer'Image (Command.Params'Length));
2328 Put (Standard_Error, "usage: ");
2329 Put_Line (Standard_Error, Command.Usage.all);
2330 Errors := Errors + 1;
2331 end if;
2333 if Errors > 0 then
2334 raise Error_Exit;
2335 else
2336 -- Prepare arguments for a call to spawn, filtering out
2337 -- embedded nulls place there to delineate strings.
2339 Process_Buffer (String (Buffer.Table (1 .. Buffer.Last)));
2341 if Cargs_Buffer.Last > 1 then
2342 Last_Switches.Append (new String'("-cargs"));
2343 Process_Buffer
2344 (String (Cargs_Buffer.Table (1 .. Cargs_Buffer.Last)));
2345 end if;
2346 end if;
2347 end VMS_Conversion;
2349 end VMS_Conv;