1------------------------------------------------------------------------------ 2-- -- 3-- GNAT COMPILER COMPONENTS -- 4-- -- 5-- G N A T . C O M M A N D _ L I N E -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1999-2018, 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. -- 17-- -- 18-- As a special exception under Section 7 of GPL version 3, you are granted -- 19-- additional permissions described in the GCC Runtime Library Exception, -- 20-- version 3.1, as published by the Free Software Foundation. -- 21-- -- 22-- You should have received a copy of the GNU General Public License and -- 23-- a copy of the GCC Runtime Library Exception along with this program; -- 24-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- 25-- <http://www.gnu.org/licenses/>. -- 26-- -- 27-- GNAT was originally developed by the GNAT team at New York University. -- 28-- Extensive contributions were provided by Ada Core Technologies Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32with Ada.Characters.Handling; use Ada.Characters.Handling; 33with Ada.Strings.Unbounded; 34with Ada.Text_IO; use Ada.Text_IO; 35with Ada.Unchecked_Deallocation; 36 37with GNAT.Directory_Operations; use GNAT.Directory_Operations; 38with GNAT.OS_Lib; use GNAT.OS_Lib; 39 40package body GNAT.Command_Line is 41 42 -- General note: this entire body could use much more commenting. There 43 -- are large sections of uncommented code throughout, and many formal 44 -- parameters of local subprograms are not documented at all ??? 45 46 package CL renames Ada.Command_Line; 47 48 type Switch_Parameter_Type is 49 (Parameter_None, 50 Parameter_With_Optional_Space, -- ':' in getopt 51 Parameter_With_Space_Or_Equal, -- '=' in getopt 52 Parameter_No_Space, -- '!' in getopt 53 Parameter_Optional); -- '?' in getopt 54 55 procedure Set_Parameter 56 (Variable : out Parameter_Type; 57 Arg_Num : Positive; 58 First : Positive; 59 Last : Natural; 60 Extra : Character := ASCII.NUL); 61 pragma Inline (Set_Parameter); 62 -- Set the parameter that will be returned by Parameter below 63 -- 64 -- Extra is a character that needs to be added when reporting Full_Switch. 65 -- (it will in general be the switch character, for instance '-'). 66 -- Otherwise, Full_Switch will report 'f' instead of '-f'. In particular, 67 -- it needs to be set when reporting an invalid switch or handling '*'. 68 -- 69 -- Parameters need to be defined ??? 70 71 function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean; 72 -- Go to the next argument on the command line. If we are at the end of 73 -- the current section, we want to make sure there is no other identical 74 -- section on the command line (there might be multiple instances of 75 -- -largs). Returns True iff there is another argument. 76 77 function Get_File_Names_Case_Sensitive return Integer; 78 pragma Import (C, Get_File_Names_Case_Sensitive, 79 "__gnat_get_file_names_case_sensitive"); 80 81 File_Names_Case_Sensitive : constant Boolean := 82 Get_File_Names_Case_Sensitive /= 0; 83 84 procedure Canonical_Case_File_Name (S : in out String); 85 -- Given a file name, converts it to canonical case form. For systems where 86 -- file names are case sensitive, this procedure has no effect. If file 87 -- names are not case sensitive (i.e. for example if you have the file 88 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call 89 -- converts the given string to canonical all lower case form, so that two 90 -- file names compare equal if they refer to the same file. 91 92 procedure Internal_Initialize_Option_Scan 93 (Parser : Opt_Parser; 94 Switch_Char : Character; 95 Stop_At_First_Non_Switch : Boolean; 96 Section_Delimiters : String); 97 -- Initialize Parser, which must have been allocated already 98 99 function Argument (Parser : Opt_Parser; Index : Integer) return String; 100 -- Return the index-th command line argument 101 102 procedure Find_Longest_Matching_Switch 103 (Switches : String; 104 Arg : String; 105 Index_In_Switches : out Integer; 106 Switch_Length : out Integer; 107 Param : out Switch_Parameter_Type); 108 -- Return the Longest switch from Switches that at least partially matches 109 -- Arg. Index_In_Switches is set to 0 if none matches. What are other 110 -- parameters??? in particular Param is not always set??? 111 112 procedure Unchecked_Free is new Ada.Unchecked_Deallocation 113 (Argument_List, Argument_List_Access); 114 115 procedure Unchecked_Free is new Ada.Unchecked_Deallocation 116 (Command_Line_Configuration_Record, Command_Line_Configuration); 117 118 procedure Remove (Line : in out Argument_List_Access; Index : Integer); 119 -- Remove a specific element from Line 120 121 procedure Add 122 (Line : in out Argument_List_Access; 123 Str : String_Access; 124 Before : Boolean := False); 125 -- Add a new element to Line. If Before is True, the item is inserted at 126 -- the beginning, else it is appended. 127 128 procedure Add 129 (Config : in out Command_Line_Configuration; 130 Switch : Switch_Definition); 131 procedure Add 132 (Def : in out Alias_Definitions_List; 133 Alias : Alias_Definition); 134 -- Add a new element to Def 135 136 procedure Initialize_Switch_Def 137 (Def : out Switch_Definition; 138 Switch : String := ""; 139 Long_Switch : String := ""; 140 Help : String := ""; 141 Section : String := ""; 142 Argument : String := "ARG"); 143 -- Initialize [Def] with the contents of the other parameters. 144 -- This also checks consistency of the switch parameters, and will raise 145 -- Invalid_Switch if they do not match. 146 147 procedure Decompose_Switch 148 (Switch : String; 149 Parameter_Type : out Switch_Parameter_Type; 150 Switch_Last : out Integer); 151 -- Given a switch definition ("name:" for instance), extracts the type of 152 -- parameter that is expected, and the name of the switch 153 154 function Can_Have_Parameter (S : String) return Boolean; 155 -- True if S can have a parameter 156 157 function Require_Parameter (S : String) return Boolean; 158 -- True if S requires a parameter 159 160 function Actual_Switch (S : String) return String; 161 -- Remove any possible trailing '!', ':', '?' and '=' 162 163 generic 164 with procedure Callback 165 (Simple_Switch : String; 166 Separator : String; 167 Parameter : String; 168 Index : Integer); -- Index in Config.Switches, or -1 169 procedure For_Each_Simple_Switch 170 (Config : Command_Line_Configuration; 171 Section : String; 172 Switch : String; 173 Parameter : String := ""; 174 Unalias : Boolean := True); 175 -- Breaks Switch into as simple switches as possible (expanding aliases and 176 -- ungrouping common prefixes when possible), and call Callback for each of 177 -- these. 178 179 procedure Sort_Sections 180 (Line : not null GNAT.OS_Lib.Argument_List_Access; 181 Sections : GNAT.OS_Lib.Argument_List_Access; 182 Params : GNAT.OS_Lib.Argument_List_Access); 183 -- Reorder the command line switches so that the switches belonging to a 184 -- section are grouped together. 185 186 procedure Group_Switches 187 (Cmd : Command_Line; 188 Result : Argument_List_Access; 189 Sections : Argument_List_Access; 190 Params : Argument_List_Access); 191 -- Group switches with common prefixes whenever possible. Once they have 192 -- been grouped, we also check items for possible aliasing. 193 194 procedure Alias_Switches 195 (Cmd : Command_Line; 196 Result : Argument_List_Access; 197 Params : Argument_List_Access); 198 -- When possible, replace one or more switches by an alias, i.e. a shorter 199 -- version. 200 201 function Looking_At 202 (Type_Str : String; 203 Index : Natural; 204 Substring : String) return Boolean; 205 -- Return True if the characters starting at Index in Type_Str are 206 -- equivalent to Substring. 207 208 generic 209 with function Callback (S : String; Index : Integer) return Boolean; 210 procedure Foreach_Switch 211 (Config : Command_Line_Configuration; 212 Section : String); 213 -- Iterate over all switches defined in Config, for a specific section. 214 -- Index is set to the index in Config.Switches. Stop iterating when 215 -- Callback returns False. 216 217 -------------- 218 -- Argument -- 219 -------------- 220 221 function Argument (Parser : Opt_Parser; Index : Integer) return String is 222 begin 223 if Parser.Arguments /= null then 224 return Parser.Arguments (Index + Parser.Arguments'First - 1).all; 225 else 226 return CL.Argument (Index); 227 end if; 228 end Argument; 229 230 ------------------------------ 231 -- Canonical_Case_File_Name -- 232 ------------------------------ 233 234 procedure Canonical_Case_File_Name (S : in out String) is 235 begin 236 if not File_Names_Case_Sensitive then 237 for J in S'Range loop 238 if S (J) in 'A' .. 'Z' then 239 S (J) := Character'Val 240 (Character'Pos (S (J)) + 241 (Character'Pos ('a') - Character'Pos ('A'))); 242 end if; 243 end loop; 244 end if; 245 end Canonical_Case_File_Name; 246 247 --------------- 248 -- Expansion -- 249 --------------- 250 251 function Expansion (Iterator : Expansion_Iterator) return String is 252 type Pointer is access all Expansion_Iterator; 253 254 It : constant Pointer := Iterator'Unrestricted_Access; 255 S : String (1 .. 1024); 256 Last : Natural; 257 258 Current : Depth := It.Current_Depth; 259 NL : Positive; 260 261 begin 262 -- It is assumed that a directory is opened at the current level. 263 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised 264 -- at the first call to Read. 265 266 loop 267 Read (It.Levels (Current).Dir, S, Last); 268 269 -- If we have exhausted the directory, close it and go back one level 270 271 if Last = 0 then 272 Close (It.Levels (Current).Dir); 273 274 -- If we are at level 1, we are finished; return an empty string 275 276 if Current = 1 then 277 return String'(1 .. 0 => ' '); 278 279 -- Otherwise continue with the directory at the previous level 280 281 else 282 Current := Current - 1; 283 It.Current_Depth := Current; 284 end if; 285 286 -- If this is a directory, that is neither "." or "..", attempt to 287 -- go to the next level. 288 289 elsif Is_Directory 290 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & 291 S (1 .. Last)) 292 and then S (1 .. Last) /= "." 293 and then S (1 .. Last) /= ".." 294 then 295 -- We can go to the next level only if we have not reached the 296 -- maximum depth, 297 298 if Current < It.Maximum_Depth then 299 NL := It.Levels (Current).Name_Last; 300 301 -- And if relative path of this new directory is not too long 302 303 if NL + Last + 1 < Max_Path_Length then 304 Current := Current + 1; 305 It.Current_Depth := Current; 306 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last); 307 NL := NL + Last + 1; 308 It.Dir_Name (NL) := Directory_Separator; 309 It.Levels (Current).Name_Last := NL; 310 Canonical_Case_File_Name (It.Dir_Name (1 .. NL)); 311 312 -- Open the new directory, and read from it 313 314 GNAT.Directory_Operations.Open 315 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL)); 316 end if; 317 end if; 318 end if; 319 320 -- Check the relative path against the pattern 321 322 -- Note that we try to match also against directory names, since 323 -- clients of this function may expect to retrieve directories. 324 325 declare 326 Name : String := 327 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) 328 & S (1 .. Last); 329 330 begin 331 Canonical_Case_File_Name (Name); 332 333 -- If it matches return the relative path 334 335 if GNAT.Regexp.Match (Name, Iterator.Regexp) then 336 return Name; 337 end if; 338 end; 339 end loop; 340 end Expansion; 341 342 --------------------- 343 -- Current_Section -- 344 --------------------- 345 346 function Current_Section 347 (Parser : Opt_Parser := Command_Line_Parser) return String 348 is 349 begin 350 if Parser.Current_Section = 1 then 351 return ""; 352 end if; 353 354 for Index in reverse 1 .. Integer'Min (Parser.Current_Argument - 1, 355 Parser.Section'Last) 356 loop 357 if Parser.Section (Index) = 0 then 358 return Argument (Parser, Index); 359 end if; 360 end loop; 361 362 return ""; 363 end Current_Section; 364 365 ----------------- 366 -- Full_Switch -- 367 ----------------- 368 369 function Full_Switch 370 (Parser : Opt_Parser := Command_Line_Parser) return String 371 is 372 begin 373 if Parser.The_Switch.Extra = ASCII.NUL then 374 return Argument (Parser, Parser.The_Switch.Arg_Num) 375 (Parser.The_Switch.First .. Parser.The_Switch.Last); 376 else 377 return Parser.The_Switch.Extra 378 & Argument (Parser, Parser.The_Switch.Arg_Num) 379 (Parser.The_Switch.First .. Parser.The_Switch.Last); 380 end if; 381 end Full_Switch; 382 383 ------------------ 384 -- Get_Argument -- 385 ------------------ 386 387 function Get_Argument 388 (Do_Expansion : Boolean := False; 389 Parser : Opt_Parser := Command_Line_Parser) return String 390 is 391 begin 392 if Parser.In_Expansion then 393 declare 394 S : constant String := Expansion (Parser.Expansion_It); 395 begin 396 if S'Length /= 0 then 397 return S; 398 else 399 Parser.In_Expansion := False; 400 end if; 401 end; 402 end if; 403 404 if Parser.Current_Argument > Parser.Arg_Count then 405 406 -- If this is the first time this function is called 407 408 if Parser.Current_Index = 1 then 409 Parser.Current_Argument := 1; 410 while Parser.Current_Argument <= Parser.Arg_Count 411 and then Parser.Section (Parser.Current_Argument) /= 412 Parser.Current_Section 413 loop 414 Parser.Current_Argument := Parser.Current_Argument + 1; 415 end loop; 416 417 else 418 return String'(1 .. 0 => ' '); 419 end if; 420 421 elsif Parser.Section (Parser.Current_Argument) = 0 then 422 while Parser.Current_Argument <= Parser.Arg_Count 423 and then Parser.Section (Parser.Current_Argument) /= 424 Parser.Current_Section 425 loop 426 Parser.Current_Argument := Parser.Current_Argument + 1; 427 end loop; 428 end if; 429 430 Parser.Current_Index := Integer'Last; 431 432 while Parser.Current_Argument <= Parser.Arg_Count 433 and then Parser.Is_Switch (Parser.Current_Argument) 434 loop 435 Parser.Current_Argument := Parser.Current_Argument + 1; 436 end loop; 437 438 if Parser.Current_Argument > Parser.Arg_Count then 439 return String'(1 .. 0 => ' '); 440 elsif Parser.Section (Parser.Current_Argument) = 0 then 441 return Get_Argument (Do_Expansion); 442 end if; 443 444 Parser.Current_Argument := Parser.Current_Argument + 1; 445 446 -- Could it be a file name with wild cards to expand? 447 448 if Do_Expansion then 449 declare 450 Arg : constant String := 451 Argument (Parser, Parser.Current_Argument - 1); 452 begin 453 for Index in Arg'Range loop 454 if Arg (Index) = '*' 455 or else Arg (Index) = '?' 456 or else Arg (Index) = '[' 457 then 458 Parser.In_Expansion := True; 459 Start_Expansion (Parser.Expansion_It, Arg); 460 return Get_Argument (Do_Expansion, Parser); 461 end if; 462 end loop; 463 end; 464 end if; 465 466 return Argument (Parser, Parser.Current_Argument - 1); 467 end Get_Argument; 468 469 ---------------------- 470 -- Decompose_Switch -- 471 ---------------------- 472 473 procedure Decompose_Switch 474 (Switch : String; 475 Parameter_Type : out Switch_Parameter_Type; 476 Switch_Last : out Integer) 477 is 478 begin 479 if Switch = "" then 480 Parameter_Type := Parameter_None; 481 Switch_Last := Switch'Last; 482 return; 483 end if; 484 485 case Switch (Switch'Last) is 486 when ':' => 487 Parameter_Type := Parameter_With_Optional_Space; 488 Switch_Last := Switch'Last - 1; 489 490 when '=' => 491 Parameter_Type := Parameter_With_Space_Or_Equal; 492 Switch_Last := Switch'Last - 1; 493 494 when '!' => 495 Parameter_Type := Parameter_No_Space; 496 Switch_Last := Switch'Last - 1; 497 498 when '?' => 499 Parameter_Type := Parameter_Optional; 500 Switch_Last := Switch'Last - 1; 501 502 when others => 503 Parameter_Type := Parameter_None; 504 Switch_Last := Switch'Last; 505 end case; 506 end Decompose_Switch; 507 508 ---------------------------------- 509 -- Find_Longest_Matching_Switch -- 510 ---------------------------------- 511 512 procedure Find_Longest_Matching_Switch 513 (Switches : String; 514 Arg : String; 515 Index_In_Switches : out Integer; 516 Switch_Length : out Integer; 517 Param : out Switch_Parameter_Type) 518 is 519 Index : Natural; 520 Length : Natural := 1; 521 Last : Natural; 522 P : Switch_Parameter_Type; 523 524 begin 525 Index_In_Switches := 0; 526 Switch_Length := 0; 527 528 -- Remove all leading spaces first to make sure that Index points 529 -- at the start of the first switch. 530 531 Index := Switches'First; 532 while Index <= Switches'Last and then Switches (Index) = ' ' loop 533 Index := Index + 1; 534 end loop; 535 536 while Index <= Switches'Last loop 537 538 -- Search the length of the parameter at this position in Switches 539 540 Length := Index; 541 while Length <= Switches'Last 542 and then Switches (Length) /= ' ' 543 loop 544 Length := Length + 1; 545 end loop; 546 547 -- Length now marks the separator after the current switch. Last will 548 -- mark the last character of the name of the switch. 549 550 if Length = Index + 1 then 551 P := Parameter_None; 552 Last := Index; 553 else 554 Decompose_Switch (Switches (Index .. Length - 1), P, Last); 555 end if; 556 557 -- If it is the one we searched, it may be a candidate 558 559 if Arg'First + Last - Index <= Arg'Last 560 and then Switches (Index .. Last) = 561 Arg (Arg'First .. Arg'First + Last - Index) 562 and then Last - Index + 1 > Switch_Length 563 and then 564 (P /= Parameter_With_Space_Or_Equal 565 or else Arg'Last = Arg'First + Last - Index 566 or else Arg (Arg'First + Last - Index + 1) = '=') 567 then 568 Param := P; 569 Index_In_Switches := Index; 570 Switch_Length := Last - Index + 1; 571 end if; 572 573 -- Look for the next switch in Switches 574 575 while Index <= Switches'Last 576 and then Switches (Index) /= ' ' 577 loop 578 Index := Index + 1; 579 end loop; 580 581 Index := Index + 1; 582 end loop; 583 end Find_Longest_Matching_Switch; 584 585 ------------ 586 -- Getopt -- 587 ------------ 588 589 function Getopt 590 (Switches : String; 591 Concatenate : Boolean := True; 592 Parser : Opt_Parser := Command_Line_Parser) return Character 593 is 594 Dummy : Boolean; 595 596 begin 597 <<Restart>> 598 599 -- If we have finished parsing the current command line item (there 600 -- might be multiple switches in a single item), then go to the next 601 -- element. 602 603 if Parser.Current_Argument > Parser.Arg_Count 604 or else (Parser.Current_Index > 605 Argument (Parser, Parser.Current_Argument)'Last 606 and then not Goto_Next_Argument_In_Section (Parser)) 607 then 608 return ASCII.NUL; 609 end if; 610 611 -- By default, the switch will not have a parameter 612 613 Parser.The_Parameter := 614 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL); 615 Parser.The_Separator := ASCII.NUL; 616 617 declare 618 Arg : constant String := 619 Argument (Parser, Parser.Current_Argument); 620 Index_Switches : Natural := 0; 621 Max_Length : Natural := 0; 622 End_Index : Natural; 623 Param : Switch_Parameter_Type; 624 begin 625 -- If we are on a new item, test if this might be a switch 626 627 if Parser.Current_Index = Arg'First then 628 if Arg = "" or else Arg (Arg'First) /= Parser.Switch_Character then 629 630 -- If it isn't a switch, return it immediately. We also know it 631 -- isn't the parameter to a previous switch, since that has 632 -- already been handled. 633 634 if Switches (Switches'First) = '*' then 635 Set_Parameter 636 (Parser.The_Switch, 637 Arg_Num => Parser.Current_Argument, 638 First => Arg'First, 639 Last => Arg'Last); 640 Parser.Is_Switch (Parser.Current_Argument) := True; 641 Dummy := Goto_Next_Argument_In_Section (Parser); 642 return '*'; 643 end if; 644 645 if Parser.Stop_At_First then 646 Parser.Current_Argument := Positive'Last; 647 return ASCII.NUL; 648 649 elsif not Goto_Next_Argument_In_Section (Parser) then 650 return ASCII.NUL; 651 652 else 653 -- Recurse to get the next switch on the command line 654 655 goto Restart; 656 end if; 657 end if; 658 659 -- We are on the first character of a new command line argument, 660 -- which starts with Switch_Character. Further analysis is needed. 661 662 Parser.Current_Index := Parser.Current_Index + 1; 663 Parser.Is_Switch (Parser.Current_Argument) := True; 664 end if; 665 666 Find_Longest_Matching_Switch 667 (Switches => Switches, 668 Arg => Arg (Parser.Current_Index .. Arg'Last), 669 Index_In_Switches => Index_Switches, 670 Switch_Length => Max_Length, 671 Param => Param); 672 673 -- If switch is not accepted, it is either invalid or is returned 674 -- in the context of '*'. 675 676 if Index_Switches = 0 then 677 678 -- Find the current switch that we did not recognize. This is in 679 -- fact difficult because Getopt does not know explicitly about 680 -- short and long switches. Ideally, we would want the following 681 -- behavior: 682 683 -- * for short switches, with Concatenate: 684 -- if -a is not recognized, and the command line has -daf 685 -- we should report the invalid switch as "-a". 686 687 -- * for short switches, wihtout Concatenate: 688 -- we should report the invalid switch as "-daf". 689 690 -- * for long switches: 691 -- if the commadn line is "--long" we should report --long 692 -- as unrecongized. 693 694 -- Unfortunately, the fact that long switches start with a 695 -- duplicate switch character is just a convention (so we could 696 -- have a long switch "-long" for instance). We'll still rely on 697 -- this convention here to try and get as helpful an error message 698 -- as possible. 699 700 -- Long switch case (starting with double switch character) 701 702 if Arg (Arg'First + 1) = Parser.Switch_Character then 703 End_Index := Arg'Last; 704 705 -- Short switch case 706 707 else 708 End_Index := 709 (if Concatenate then Parser.Current_Index else Arg'Last); 710 end if; 711 712 if Switches /= "" and then Switches (Switches'First) = '*' then 713 714 -- Always prepend the switch character, so that users know 715 -- that this comes from a switch on the command line. This 716 -- is especially important when Concatenate is False, since 717 -- otherwise the current argument first character is lost. 718 719 if Parser.Section (Parser.Current_Argument) = 0 then 720 721 -- A section transition should not be returned to the user 722 723 Dummy := Goto_Next_Argument_In_Section (Parser); 724 goto Restart; 725 726 else 727 Set_Parameter 728 (Parser.The_Switch, 729 Arg_Num => Parser.Current_Argument, 730 First => Parser.Current_Index, 731 Last => Arg'Last, 732 Extra => Parser.Switch_Character); 733 Parser.Is_Switch (Parser.Current_Argument) := True; 734 Dummy := Goto_Next_Argument_In_Section (Parser); 735 return '*'; 736 end if; 737 end if; 738 739 if Parser.Current_Index = Arg'First then 740 Set_Parameter 741 (Parser.The_Switch, 742 Arg_Num => Parser.Current_Argument, 743 First => Parser.Current_Index, 744 Last => End_Index); 745 else 746 Set_Parameter 747 (Parser.The_Switch, 748 Arg_Num => Parser.Current_Argument, 749 First => Parser.Current_Index, 750 Last => End_Index, 751 Extra => Parser.Switch_Character); 752 end if; 753 754 Parser.Current_Index := End_Index + 1; 755 756 raise Invalid_Switch; 757 end if; 758 759 End_Index := Parser.Current_Index + Max_Length - 1; 760 Set_Parameter 761 (Parser.The_Switch, 762 Arg_Num => Parser.Current_Argument, 763 First => Parser.Current_Index, 764 Last => End_Index); 765 766 case Param is 767 when Parameter_With_Optional_Space => 768 if End_Index < Arg'Last then 769 Set_Parameter 770 (Parser.The_Parameter, 771 Arg_Num => Parser.Current_Argument, 772 First => End_Index + 1, 773 Last => Arg'Last); 774 Dummy := Goto_Next_Argument_In_Section (Parser); 775 776 elsif Parser.Current_Argument < Parser.Arg_Count 777 and then Parser.Section (Parser.Current_Argument + 1) /= 0 778 then 779 Parser.Current_Argument := Parser.Current_Argument + 1; 780 Parser.The_Separator := ' '; 781 Set_Parameter 782 (Parser.The_Parameter, 783 Arg_Num => Parser.Current_Argument, 784 First => Argument (Parser, Parser.Current_Argument)'First, 785 Last => Argument (Parser, Parser.Current_Argument)'Last); 786 Parser.Is_Switch (Parser.Current_Argument) := True; 787 Dummy := Goto_Next_Argument_In_Section (Parser); 788 789 else 790 Parser.Current_Index := End_Index + 1; 791 raise Invalid_Parameter; 792 end if; 793 794 when Parameter_With_Space_Or_Equal => 795 796 -- If the switch is of the form <switch>=xxx 797 798 if End_Index < Arg'Last then 799 if Arg (End_Index + 1) = '=' 800 and then End_Index + 1 < Arg'Last 801 then 802 Parser.The_Separator := '='; 803 Set_Parameter 804 (Parser.The_Parameter, 805 Arg_Num => Parser.Current_Argument, 806 First => End_Index + 2, 807 Last => Arg'Last); 808 Dummy := Goto_Next_Argument_In_Section (Parser); 809 810 else 811 Parser.Current_Index := End_Index + 1; 812 raise Invalid_Parameter; 813 end if; 814 815 -- Case of switch of the form <switch> xxx 816 817 elsif Parser.Current_Argument < Parser.Arg_Count 818 and then Parser.Section (Parser.Current_Argument + 1) /= 0 819 then 820 Parser.Current_Argument := Parser.Current_Argument + 1; 821 Parser.The_Separator := ' '; 822 Set_Parameter 823 (Parser.The_Parameter, 824 Arg_Num => Parser.Current_Argument, 825 First => Argument (Parser, Parser.Current_Argument)'First, 826 Last => Argument (Parser, Parser.Current_Argument)'Last); 827 Parser.Is_Switch (Parser.Current_Argument) := True; 828 Dummy := Goto_Next_Argument_In_Section (Parser); 829 830 else 831 Parser.Current_Index := End_Index + 1; 832 raise Invalid_Parameter; 833 end if; 834 835 when Parameter_No_Space => 836 if End_Index < Arg'Last then 837 Set_Parameter 838 (Parser.The_Parameter, 839 Arg_Num => Parser.Current_Argument, 840 First => End_Index + 1, 841 Last => Arg'Last); 842 Dummy := Goto_Next_Argument_In_Section (Parser); 843 844 else 845 Parser.Current_Index := End_Index + 1; 846 raise Invalid_Parameter; 847 end if; 848 849 when Parameter_Optional => 850 if End_Index < Arg'Last then 851 Set_Parameter 852 (Parser.The_Parameter, 853 Arg_Num => Parser.Current_Argument, 854 First => End_Index + 1, 855 Last => Arg'Last); 856 end if; 857 858 Dummy := Goto_Next_Argument_In_Section (Parser); 859 860 when Parameter_None => 861 if Concatenate or else End_Index = Arg'Last then 862 Parser.Current_Index := End_Index + 1; 863 864 else 865 -- If Concatenate is False and the full argument is not 866 -- recognized as a switch, this is an invalid switch. 867 868 if Switches (Switches'First) = '*' then 869 Set_Parameter 870 (Parser.The_Switch, 871 Arg_Num => Parser.Current_Argument, 872 First => Arg'First, 873 Last => Arg'Last); 874 Parser.Is_Switch (Parser.Current_Argument) := True; 875 Dummy := Goto_Next_Argument_In_Section (Parser); 876 return '*'; 877 end if; 878 879 Set_Parameter 880 (Parser.The_Switch, 881 Arg_Num => Parser.Current_Argument, 882 First => Parser.Current_Index, 883 Last => Arg'Last, 884 Extra => Parser.Switch_Character); 885 Parser.Current_Index := Arg'Last + 1; 886 raise Invalid_Switch; 887 end if; 888 end case; 889 890 return Switches (Index_Switches); 891 end; 892 end Getopt; 893 894 ----------------------------------- 895 -- Goto_Next_Argument_In_Section -- 896 ----------------------------------- 897 898 function Goto_Next_Argument_In_Section 899 (Parser : Opt_Parser) return Boolean 900 is 901 begin 902 Parser.Current_Argument := Parser.Current_Argument + 1; 903 904 if Parser.Current_Argument > Parser.Arg_Count 905 or else Parser.Section (Parser.Current_Argument) = 0 906 then 907 loop 908 Parser.Current_Argument := Parser.Current_Argument + 1; 909 910 if Parser.Current_Argument > Parser.Arg_Count then 911 Parser.Current_Index := 1; 912 return False; 913 end if; 914 915 exit when Parser.Section (Parser.Current_Argument) = 916 Parser.Current_Section; 917 end loop; 918 end if; 919 920 Parser.Current_Index := 921 Argument (Parser, Parser.Current_Argument)'First; 922 923 return True; 924 end Goto_Next_Argument_In_Section; 925 926 ------------------ 927 -- Goto_Section -- 928 ------------------ 929 930 procedure Goto_Section 931 (Name : String := ""; 932 Parser : Opt_Parser := Command_Line_Parser) 933 is 934 Index : Integer; 935 936 begin 937 Parser.In_Expansion := False; 938 939 if Name = "" then 940 Parser.Current_Argument := 1; 941 Parser.Current_Index := 1; 942 Parser.Current_Section := 1; 943 return; 944 end if; 945 946 Index := 1; 947 while Index <= Parser.Arg_Count loop 948 if Parser.Section (Index) = 0 949 and then Argument (Parser, Index) = Parser.Switch_Character & Name 950 then 951 Parser.Current_Argument := Index + 1; 952 Parser.Current_Index := 1; 953 954 if Parser.Current_Argument <= Parser.Arg_Count then 955 Parser.Current_Section := 956 Parser.Section (Parser.Current_Argument); 957 end if; 958 959 -- Exit from loop if we have the start of another section 960 961 if Index = Parser.Section'Last 962 or else Parser.Section (Index + 1) /= 0 963 then 964 return; 965 end if; 966 end if; 967 968 Index := Index + 1; 969 end loop; 970 971 Parser.Current_Argument := Positive'Last; 972 Parser.Current_Index := 2; -- so that Get_Argument returns nothing 973 end Goto_Section; 974 975 ---------------------------- 976 -- Initialize_Option_Scan -- 977 ---------------------------- 978 979 procedure Initialize_Option_Scan 980 (Switch_Char : Character := '-'; 981 Stop_At_First_Non_Switch : Boolean := False; 982 Section_Delimiters : String := "") 983 is 984 begin 985 Internal_Initialize_Option_Scan 986 (Parser => Command_Line_Parser, 987 Switch_Char => Switch_Char, 988 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch, 989 Section_Delimiters => Section_Delimiters); 990 end Initialize_Option_Scan; 991 992 ---------------------------- 993 -- Initialize_Option_Scan -- 994 ---------------------------- 995 996 procedure Initialize_Option_Scan 997 (Parser : out Opt_Parser; 998 Command_Line : GNAT.OS_Lib.Argument_List_Access; 999 Switch_Char : Character := '-'; 1000 Stop_At_First_Non_Switch : Boolean := False; 1001 Section_Delimiters : String := "") 1002 is 1003 begin 1004 Free (Parser); 1005 1006 if Command_Line = null then 1007 Parser := new Opt_Parser_Data (CL.Argument_Count); 1008 Internal_Initialize_Option_Scan 1009 (Parser => Parser, 1010 Switch_Char => Switch_Char, 1011 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch, 1012 Section_Delimiters => Section_Delimiters); 1013 else 1014 Parser := new Opt_Parser_Data (Command_Line'Length); 1015 Parser.Arguments := Command_Line; 1016 Internal_Initialize_Option_Scan 1017 (Parser => Parser, 1018 Switch_Char => Switch_Char, 1019 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch, 1020 Section_Delimiters => Section_Delimiters); 1021 end if; 1022 end Initialize_Option_Scan; 1023 1024 ------------------------------------- 1025 -- Internal_Initialize_Option_Scan -- 1026 ------------------------------------- 1027 1028 procedure Internal_Initialize_Option_Scan 1029 (Parser : Opt_Parser; 1030 Switch_Char : Character; 1031 Stop_At_First_Non_Switch : Boolean; 1032 Section_Delimiters : String) 1033 is 1034 Section_Num : Section_Number; 1035 Section_Index : Integer; 1036 Last : Integer; 1037 Delimiter_Found : Boolean; 1038 1039 Discard : Boolean; 1040 pragma Warnings (Off, Discard); 1041 1042 begin 1043 Parser.Current_Argument := 0; 1044 Parser.Current_Index := 0; 1045 Parser.In_Expansion := False; 1046 Parser.Switch_Character := Switch_Char; 1047 Parser.Stop_At_First := Stop_At_First_Non_Switch; 1048 Parser.Section := (others => 1); 1049 1050 -- If we are using sections, we have to preprocess the command line to 1051 -- delimit them. A section can be repeated, so we just give each item 1052 -- on the command line a section number 1053 1054 Section_Num := 1; 1055 Section_Index := Section_Delimiters'First; 1056 while Section_Index <= Section_Delimiters'Last loop 1057 Last := Section_Index; 1058 while Last <= Section_Delimiters'Last 1059 and then Section_Delimiters (Last) /= ' ' 1060 loop 1061 Last := Last + 1; 1062 end loop; 1063 1064 Delimiter_Found := False; 1065 Section_Num := Section_Num + 1; 1066 1067 for Index in 1 .. Parser.Arg_Count loop 1068 pragma Assert (Argument (Parser, Index)'First = 1); 1069 if Argument (Parser, Index) /= "" 1070 and then Argument (Parser, Index)(1) = Parser.Switch_Character 1071 and then 1072 Argument (Parser, Index) = Parser.Switch_Character & 1073 Section_Delimiters 1074 (Section_Index .. Last - 1) 1075 then 1076 Parser.Section (Index) := 0; 1077 Delimiter_Found := True; 1078 1079 elsif Parser.Section (Index) = 0 then 1080 1081 -- A previous section delimiter 1082 1083 Delimiter_Found := False; 1084 1085 elsif Delimiter_Found then 1086 Parser.Section (Index) := Section_Num; 1087 end if; 1088 end loop; 1089 1090 Section_Index := Last + 1; 1091 while Section_Index <= Section_Delimiters'Last 1092 and then Section_Delimiters (Section_Index) = ' ' 1093 loop 1094 Section_Index := Section_Index + 1; 1095 end loop; 1096 end loop; 1097 1098 Discard := Goto_Next_Argument_In_Section (Parser); 1099 end Internal_Initialize_Option_Scan; 1100 1101 --------------- 1102 -- Parameter -- 1103 --------------- 1104 1105 function Parameter 1106 (Parser : Opt_Parser := Command_Line_Parser) return String 1107 is 1108 begin 1109 if Parser.The_Parameter.First > Parser.The_Parameter.Last then 1110 return String'(1 .. 0 => ' '); 1111 else 1112 return Argument (Parser, Parser.The_Parameter.Arg_Num) 1113 (Parser.The_Parameter.First .. Parser.The_Parameter.Last); 1114 end if; 1115 end Parameter; 1116 1117 --------------- 1118 -- Separator -- 1119 --------------- 1120 1121 function Separator 1122 (Parser : Opt_Parser := Command_Line_Parser) return Character 1123 is 1124 begin 1125 return Parser.The_Separator; 1126 end Separator; 1127 1128 ------------------- 1129 -- Set_Parameter -- 1130 ------------------- 1131 1132 procedure Set_Parameter 1133 (Variable : out Parameter_Type; 1134 Arg_Num : Positive; 1135 First : Positive; 1136 Last : Natural; 1137 Extra : Character := ASCII.NUL) 1138 is 1139 begin 1140 Variable.Arg_Num := Arg_Num; 1141 Variable.First := First; 1142 Variable.Last := Last; 1143 Variable.Extra := Extra; 1144 end Set_Parameter; 1145 1146 --------------------- 1147 -- Start_Expansion -- 1148 --------------------- 1149 1150 procedure Start_Expansion 1151 (Iterator : out Expansion_Iterator; 1152 Pattern : String; 1153 Directory : String := ""; 1154 Basic_Regexp : Boolean := True) 1155 is 1156 Directory_Separator : Character; 1157 pragma Import (C, Directory_Separator, "__gnat_dir_separator"); 1158 1159 First : Positive := Pattern'First; 1160 Pat : String := Pattern; 1161 1162 begin 1163 Canonical_Case_File_Name (Pat); 1164 Iterator.Current_Depth := 1; 1165 1166 -- If Directory is unspecified, use the current directory ("./" or ".\") 1167 1168 if Directory = "" then 1169 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator; 1170 Iterator.Start := 3; 1171 1172 else 1173 Iterator.Dir_Name (1 .. Directory'Length) := Directory; 1174 Iterator.Start := Directory'Length + 1; 1175 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length)); 1176 1177 -- Make sure that the last character is a directory separator 1178 1179 if Directory (Directory'Last) /= Directory_Separator then 1180 Iterator.Dir_Name (Iterator.Start) := Directory_Separator; 1181 Iterator.Start := Iterator.Start + 1; 1182 end if; 1183 end if; 1184 1185 Iterator.Levels (1).Name_Last := Iterator.Start - 1; 1186 1187 -- Open the initial Directory, at depth 1 1188 1189 GNAT.Directory_Operations.Open 1190 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1)); 1191 1192 -- If in the current directory and the pattern starts with "./" or ".\", 1193 -- drop the "./" or ".\" from the pattern. 1194 1195 if Directory = "" and then Pat'Length > 2 1196 and then Pat (Pat'First) = '.' 1197 and then Pat (Pat'First + 1) = Directory_Separator 1198 then 1199 First := Pat'First + 2; 1200 end if; 1201 1202 Iterator.Regexp := 1203 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True); 1204 1205 Iterator.Maximum_Depth := 1; 1206 1207 -- Maximum_Depth is equal to 1 plus the number of directory separators 1208 -- in the pattern. 1209 1210 for Index in First .. Pat'Last loop 1211 if Pat (Index) = Directory_Separator then 1212 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1; 1213 exit when Iterator.Maximum_Depth = Max_Depth; 1214 end if; 1215 end loop; 1216 end Start_Expansion; 1217 1218 ---------- 1219 -- Free -- 1220 ---------- 1221 1222 procedure Free (Parser : in out Opt_Parser) is 1223 procedure Unchecked_Free is new 1224 Ada.Unchecked_Deallocation (Opt_Parser_Data, Opt_Parser); 1225 begin 1226 if Parser /= null and then Parser /= Command_Line_Parser then 1227 Free (Parser.Arguments); 1228 Unchecked_Free (Parser); 1229 end if; 1230 end Free; 1231 1232 ------------------ 1233 -- Define_Alias -- 1234 ------------------ 1235 1236 procedure Define_Alias 1237 (Config : in out Command_Line_Configuration; 1238 Switch : String; 1239 Expanded : String; 1240 Section : String := "") 1241 is 1242 Def : Alias_Definition; 1243 1244 begin 1245 if Config = null then 1246 Config := new Command_Line_Configuration_Record; 1247 end if; 1248 1249 Def.Alias := new String'(Switch); 1250 Def.Expansion := new String'(Expanded); 1251 Def.Section := new String'(Section); 1252 Add (Config.Aliases, Def); 1253 end Define_Alias; 1254 1255 ------------------- 1256 -- Define_Prefix -- 1257 ------------------- 1258 1259 procedure Define_Prefix 1260 (Config : in out Command_Line_Configuration; 1261 Prefix : String) 1262 is 1263 begin 1264 if Config = null then 1265 Config := new Command_Line_Configuration_Record; 1266 end if; 1267 1268 Add (Config.Prefixes, new String'(Prefix)); 1269 end Define_Prefix; 1270 1271 --------- 1272 -- Add -- 1273 --------- 1274 1275 procedure Add 1276 (Config : in out Command_Line_Configuration; 1277 Switch : Switch_Definition) 1278 is 1279 procedure Unchecked_Free is new Ada.Unchecked_Deallocation 1280 (Switch_Definitions, Switch_Definitions_List); 1281 1282 Tmp : Switch_Definitions_List; 1283 1284 begin 1285 if Config = null then 1286 Config := new Command_Line_Configuration_Record; 1287 end if; 1288 1289 Tmp := Config.Switches; 1290 1291 if Tmp = null then 1292 Config.Switches := new Switch_Definitions (1 .. 1); 1293 else 1294 Config.Switches := new Switch_Definitions (1 .. Tmp'Length + 1); 1295 Config.Switches (1 .. Tmp'Length) := Tmp.all; 1296 Unchecked_Free (Tmp); 1297 end if; 1298 1299 if Switch.Switch /= null and then Switch.Switch.all = "*" then 1300 Config.Star_Switch := True; 1301 end if; 1302 1303 Config.Switches (Config.Switches'Last) := Switch; 1304 end Add; 1305 1306 --------- 1307 -- Add -- 1308 --------- 1309 1310 procedure Add 1311 (Def : in out Alias_Definitions_List; 1312 Alias : Alias_Definition) 1313 is 1314 procedure Unchecked_Free is new 1315 Ada.Unchecked_Deallocation 1316 (Alias_Definitions, Alias_Definitions_List); 1317 1318 Tmp : Alias_Definitions_List := Def; 1319 1320 begin 1321 if Tmp = null then 1322 Def := new Alias_Definitions (1 .. 1); 1323 else 1324 Def := new Alias_Definitions (1 .. Tmp'Length + 1); 1325 Def (1 .. Tmp'Length) := Tmp.all; 1326 Unchecked_Free (Tmp); 1327 end if; 1328 1329 Def (Def'Last) := Alias; 1330 end Add; 1331 1332 --------------------------- 1333 -- Initialize_Switch_Def -- 1334 --------------------------- 1335 1336 procedure Initialize_Switch_Def 1337 (Def : out Switch_Definition; 1338 Switch : String := ""; 1339 Long_Switch : String := ""; 1340 Help : String := ""; 1341 Section : String := ""; 1342 Argument : String := "ARG") 1343 is 1344 P1, P2 : Switch_Parameter_Type := Parameter_None; 1345 Last1, Last2 : Integer; 1346 1347 begin 1348 if Switch /= "" then 1349 Def.Switch := new String'(Switch); 1350 Decompose_Switch (Switch, P1, Last1); 1351 end if; 1352 1353 if Long_Switch /= "" then 1354 Def.Long_Switch := new String'(Long_Switch); 1355 Decompose_Switch (Long_Switch, P2, Last2); 1356 end if; 1357 1358 if Switch /= "" and then Long_Switch /= "" then 1359 if (P1 = Parameter_None and then P2 /= P1) 1360 or else (P2 = Parameter_None and then P1 /= P2) 1361 or else (P1 = Parameter_Optional and then P2 /= P1) 1362 or else (P2 = Parameter_Optional and then P2 /= P1) 1363 then 1364 raise Invalid_Switch 1365 with "Inconsistent parameter types for " 1366 & Switch & " and " & Long_Switch; 1367 end if; 1368 end if; 1369 1370 if Section /= "" then 1371 Def.Section := new String'(Section); 1372 end if; 1373 1374 if Argument /= "ARG" then 1375 Def.Argument := new String'(Argument); 1376 end if; 1377 1378 if Help /= "" then 1379 Def.Help := new String'(Help); 1380 end if; 1381 end Initialize_Switch_Def; 1382 1383 ------------------- 1384 -- Define_Switch -- 1385 ------------------- 1386 1387 procedure Define_Switch 1388 (Config : in out Command_Line_Configuration; 1389 Switch : String := ""; 1390 Long_Switch : String := ""; 1391 Help : String := ""; 1392 Section : String := ""; 1393 Argument : String := "ARG") 1394 is 1395 Def : Switch_Definition; 1396 begin 1397 if Switch /= "" or else Long_Switch /= "" then 1398 Initialize_Switch_Def 1399 (Def, Switch, Long_Switch, Help, Section, Argument); 1400 Add (Config, Def); 1401 end if; 1402 end Define_Switch; 1403 1404 ------------------- 1405 -- Define_Switch -- 1406 ------------------- 1407 1408 procedure Define_Switch 1409 (Config : in out Command_Line_Configuration; 1410 Output : access Boolean; 1411 Switch : String := ""; 1412 Long_Switch : String := ""; 1413 Help : String := ""; 1414 Section : String := ""; 1415 Value : Boolean := True) 1416 is 1417 Def : Switch_Definition (Switch_Boolean); 1418 begin 1419 if Switch /= "" or else Long_Switch /= "" then 1420 Initialize_Switch_Def (Def, Switch, Long_Switch, Help, Section); 1421 Def.Boolean_Output := Output.all'Unchecked_Access; 1422 Def.Boolean_Value := Value; 1423 Add (Config, Def); 1424 end if; 1425 end Define_Switch; 1426 1427 ------------------- 1428 -- Define_Switch -- 1429 ------------------- 1430 1431 procedure Define_Switch 1432 (Config : in out Command_Line_Configuration; 1433 Output : access Integer; 1434 Switch : String := ""; 1435 Long_Switch : String := ""; 1436 Help : String := ""; 1437 Section : String := ""; 1438 Initial : Integer := 0; 1439 Default : Integer := 1; 1440 Argument : String := "ARG") 1441 is 1442 Def : Switch_Definition (Switch_Integer); 1443 begin 1444 if Switch /= "" or else Long_Switch /= "" then 1445 Initialize_Switch_Def 1446 (Def, Switch, Long_Switch, Help, Section, Argument); 1447 Def.Integer_Output := Output.all'Unchecked_Access; 1448 Def.Integer_Default := Default; 1449 Def.Integer_Initial := Initial; 1450 Add (Config, Def); 1451 end if; 1452 end Define_Switch; 1453 1454 ------------------- 1455 -- Define_Switch -- 1456 ------------------- 1457 1458 procedure Define_Switch 1459 (Config : in out Command_Line_Configuration; 1460 Output : access GNAT.Strings.String_Access; 1461 Switch : String := ""; 1462 Long_Switch : String := ""; 1463 Help : String := ""; 1464 Section : String := ""; 1465 Argument : String := "ARG") 1466 is 1467 Def : Switch_Definition (Switch_String); 1468 begin 1469 if Switch /= "" or else Long_Switch /= "" then 1470 Initialize_Switch_Def 1471 (Def, Switch, Long_Switch, Help, Section, Argument); 1472 Def.String_Output := Output.all'Unchecked_Access; 1473 Add (Config, Def); 1474 end if; 1475 end Define_Switch; 1476 1477 -------------------- 1478 -- Define_Section -- 1479 -------------------- 1480 1481 procedure Define_Section 1482 (Config : in out Command_Line_Configuration; 1483 Section : String) 1484 is 1485 begin 1486 if Config = null then 1487 Config := new Command_Line_Configuration_Record; 1488 end if; 1489 1490 Add (Config.Sections, new String'(Section)); 1491 end Define_Section; 1492 1493 -------------------- 1494 -- Foreach_Switch -- 1495 -------------------- 1496 1497 procedure Foreach_Switch 1498 (Config : Command_Line_Configuration; 1499 Section : String) 1500 is 1501 begin 1502 if Config /= null and then Config.Switches /= null then 1503 for J in Config.Switches'Range loop 1504 if (Section = "" and then Config.Switches (J).Section = null) 1505 or else 1506 (Config.Switches (J).Section /= null 1507 and then Config.Switches (J).Section.all = Section) 1508 then 1509 exit when Config.Switches (J).Switch /= null 1510 and then not Callback (Config.Switches (J).Switch.all, J); 1511 1512 exit when Config.Switches (J).Long_Switch /= null 1513 and then 1514 not Callback (Config.Switches (J).Long_Switch.all, J); 1515 end if; 1516 end loop; 1517 end if; 1518 end Foreach_Switch; 1519 1520 ------------------ 1521 -- Get_Switches -- 1522 ------------------ 1523 1524 function Get_Switches 1525 (Config : Command_Line_Configuration; 1526 Switch_Char : Character := '-'; 1527 Section : String := "") return String 1528 is 1529 Ret : Ada.Strings.Unbounded.Unbounded_String; 1530 use Ada.Strings.Unbounded; 1531 1532 function Add_Switch (S : String; Index : Integer) return Boolean; 1533 -- Add a switch to Ret 1534 1535 ---------------- 1536 -- Add_Switch -- 1537 ---------------- 1538 1539 function Add_Switch (S : String; Index : Integer) return Boolean is 1540 pragma Unreferenced (Index); 1541 begin 1542 if S = "*" then 1543 Ret := "*" & Ret; -- Always first 1544 elsif S (S'First) = Switch_Char then 1545 Append (Ret, " " & S (S'First + 1 .. S'Last)); 1546 else 1547 Append (Ret, " " & S); 1548 end if; 1549 1550 return True; 1551 end Add_Switch; 1552 1553 Tmp : Boolean; 1554 pragma Unreferenced (Tmp); 1555 1556 procedure Foreach is new Foreach_Switch (Add_Switch); 1557 1558 -- Start of processing for Get_Switches 1559 1560 begin 1561 if Config = null then 1562 return ""; 1563 end if; 1564 1565 Foreach (Config, Section => Section); 1566 1567 -- Add relevant aliases 1568 1569 if Config.Aliases /= null then 1570 for A in Config.Aliases'Range loop 1571 if Config.Aliases (A).Section.all = Section then 1572 Tmp := Add_Switch (Config.Aliases (A).Alias.all, -1); 1573 end if; 1574 end loop; 1575 end if; 1576 1577 return To_String (Ret); 1578 end Get_Switches; 1579 1580 ------------------------ 1581 -- Section_Delimiters -- 1582 ------------------------ 1583 1584 function Section_Delimiters 1585 (Config : Command_Line_Configuration) return String 1586 is 1587 use Ada.Strings.Unbounded; 1588 Result : Unbounded_String; 1589 1590 begin 1591 if Config /= null and then Config.Sections /= null then 1592 for S in Config.Sections'Range loop 1593 Append (Result, " " & Config.Sections (S).all); 1594 end loop; 1595 end if; 1596 1597 return To_String (Result); 1598 end Section_Delimiters; 1599 1600 ----------------------- 1601 -- Set_Configuration -- 1602 ----------------------- 1603 1604 procedure Set_Configuration 1605 (Cmd : in out Command_Line; 1606 Config : Command_Line_Configuration) 1607 is 1608 begin 1609 Cmd.Config := Config; 1610 end Set_Configuration; 1611 1612 ----------------------- 1613 -- Get_Configuration -- 1614 ----------------------- 1615 1616 function Get_Configuration 1617 (Cmd : Command_Line) return Command_Line_Configuration 1618 is 1619 begin 1620 return Cmd.Config; 1621 end Get_Configuration; 1622 1623 ---------------------- 1624 -- Set_Command_Line -- 1625 ---------------------- 1626 1627 procedure Set_Command_Line 1628 (Cmd : in out Command_Line; 1629 Switches : String; 1630 Getopt_Description : String := ""; 1631 Switch_Char : Character := '-') 1632 is 1633 Tmp : Argument_List_Access; 1634 Parser : Opt_Parser; 1635 S : Character; 1636 Section : String_Access := null; 1637 1638 function Real_Full_Switch 1639 (S : Character; 1640 Parser : Opt_Parser) return String; 1641 -- Ensure that the returned switch value contains the Switch_Char prefix 1642 -- if needed. 1643 1644 ---------------------- 1645 -- Real_Full_Switch -- 1646 ---------------------- 1647 1648 function Real_Full_Switch 1649 (S : Character; 1650 Parser : Opt_Parser) return String 1651 is 1652 begin 1653 if S = '*' then 1654 return Full_Switch (Parser); 1655 else 1656 return Switch_Char & Full_Switch (Parser); 1657 end if; 1658 end Real_Full_Switch; 1659 1660 -- Start of processing for Set_Command_Line 1661 1662 begin 1663 Free (Cmd.Expanded); 1664 Free (Cmd.Params); 1665 1666 if Switches /= "" then 1667 Tmp := Argument_String_To_List (Switches); 1668 Initialize_Option_Scan (Parser, Tmp, Switch_Char); 1669 1670 loop 1671 begin 1672 if Cmd.Config /= null then 1673 1674 -- Do not use Getopt_Description in this case. Otherwise, 1675 -- if we have defined a prefix -gnaty, and two switches 1676 -- -gnatya and -gnatyL!, we would have a different behavior 1677 -- depending on the order of switches: 1678 1679 -- -gnatyL1a => -gnatyL with argument "1a" 1680 -- -gnatyaL1 => -gnatya and -gnatyL with argument "1" 1681 1682 -- This is because the call to Getopt below knows nothing 1683 -- about prefixes, and in the first case finds a valid 1684 -- switch with arguments, so returns it without analyzing 1685 -- the argument. In the second case, the switch matches "*", 1686 -- and is then decomposed below. 1687 1688 -- Note: When a Command_Line object is associated with a 1689 -- Command_Line_Config (which is mostly the case for tools 1690 -- that let users choose the command line before spawning 1691 -- other tools, for instance IDEs), the configuration of 1692 -- the switches must be taken from the Command_Line_Config. 1693 1694 S := Getopt (Switches => "* " & Get_Switches (Cmd.Config), 1695 Concatenate => False, 1696 Parser => Parser); 1697 1698 else 1699 S := Getopt (Switches => "* " & Getopt_Description, 1700 Concatenate => False, 1701 Parser => Parser); 1702 end if; 1703 1704 exit when S = ASCII.NUL; 1705 1706 declare 1707 Sw : constant String := Real_Full_Switch (S, Parser); 1708 Is_Section : Boolean := False; 1709 1710 begin 1711 if Cmd.Config /= null 1712 and then Cmd.Config.Sections /= null 1713 then 1714 Section_Search : 1715 for S in Cmd.Config.Sections'Range loop 1716 if Sw = Cmd.Config.Sections (S).all then 1717 Section := Cmd.Config.Sections (S); 1718 Is_Section := True; 1719 1720 exit Section_Search; 1721 end if; 1722 end loop Section_Search; 1723 end if; 1724 1725 if not Is_Section then 1726 if Section = null then 1727 Add_Switch (Cmd, Sw, Parameter (Parser)); 1728 else 1729 Add_Switch 1730 (Cmd, Sw, Parameter (Parser), 1731 Section => Section.all); 1732 end if; 1733 end if; 1734 end; 1735 1736 exception 1737 when Invalid_Parameter => 1738 1739 -- Add it with no parameter, if that's the way the user 1740 -- wants it. 1741 1742 -- Specify the separator in all cases, as the switch might 1743 -- need to be unaliased, and the alias might contain 1744 -- switches with parameters. 1745 1746 if Section = null then 1747 Add_Switch 1748 (Cmd, Switch_Char & Full_Switch (Parser)); 1749 else 1750 Add_Switch 1751 (Cmd, Switch_Char & Full_Switch (Parser), 1752 Section => Section.all); 1753 end if; 1754 end; 1755 end loop; 1756 1757 Free (Parser); 1758 end if; 1759 end Set_Command_Line; 1760 1761 ---------------- 1762 -- Looking_At -- 1763 ---------------- 1764 1765 function Looking_At 1766 (Type_Str : String; 1767 Index : Natural; 1768 Substring : String) return Boolean 1769 is 1770 begin 1771 return Index + Substring'Length - 1 <= Type_Str'Last 1772 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring; 1773 end Looking_At; 1774 1775 ------------------------ 1776 -- Can_Have_Parameter -- 1777 ------------------------ 1778 1779 function Can_Have_Parameter (S : String) return Boolean is 1780 begin 1781 if S'Length <= 1 then 1782 return False; 1783 end if; 1784 1785 case S (S'Last) is 1786 when '!' | ':' | '?' | '=' => 1787 return True; 1788 when others => 1789 return False; 1790 end case; 1791 end Can_Have_Parameter; 1792 1793 ----------------------- 1794 -- Require_Parameter -- 1795 ----------------------- 1796 1797 function Require_Parameter (S : String) return Boolean is 1798 begin 1799 if S'Length <= 1 then 1800 return False; 1801 end if; 1802 1803 case S (S'Last) is 1804 when '!' | ':' | '=' => 1805 return True; 1806 when others => 1807 return False; 1808 end case; 1809 end Require_Parameter; 1810 1811 ------------------- 1812 -- Actual_Switch -- 1813 ------------------- 1814 1815 function Actual_Switch (S : String) return String is 1816 begin 1817 if S'Length <= 1 then 1818 return S; 1819 end if; 1820 1821 case S (S'Last) is 1822 when '!' | ':' | '?' | '=' => 1823 return S (S'First .. S'Last - 1); 1824 when others => 1825 return S; 1826 end case; 1827 end Actual_Switch; 1828 1829 ---------------------------- 1830 -- For_Each_Simple_Switch -- 1831 ---------------------------- 1832 1833 procedure For_Each_Simple_Switch 1834 (Config : Command_Line_Configuration; 1835 Section : String; 1836 Switch : String; 1837 Parameter : String := ""; 1838 Unalias : Boolean := True) 1839 is 1840 function Group_Analysis 1841 (Prefix : String; 1842 Group : String) return Boolean; 1843 -- Perform the analysis of a group of switches 1844 1845 Found_In_Config : Boolean := False; 1846 function Is_In_Config 1847 (Config_Switch : String; Index : Integer) return Boolean; 1848 -- If Switch is the same as Config_Switch, run the callback and sets 1849 -- Found_In_Config to True. 1850 1851 function Starts_With 1852 (Config_Switch : String; Index : Integer) return Boolean; 1853 -- if Switch starts with Config_Switch, sets Found_In_Config to True. 1854 -- The return value is for the Foreach_Switch iterator. 1855 1856 -------------------- 1857 -- Group_Analysis -- 1858 -------------------- 1859 1860 function Group_Analysis 1861 (Prefix : String; 1862 Group : String) return Boolean 1863 is 1864 Idx : Natural; 1865 Found : Boolean; 1866 1867 function Analyze_Simple_Switch 1868 (Switch : String; Index : Integer) return Boolean; 1869 -- "Switches" is one of the switch definitions passed to the 1870 -- configuration, not one of the switches found on the command line. 1871 1872 --------------------------- 1873 -- Analyze_Simple_Switch -- 1874 --------------------------- 1875 1876 function Analyze_Simple_Switch 1877 (Switch : String; Index : Integer) return Boolean 1878 is 1879 pragma Unreferenced (Index); 1880 1881 Full : constant String := Prefix & Group (Idx .. Group'Last); 1882 1883 Sw : constant String := Actual_Switch (Switch); 1884 -- Switches definition minus argument definition 1885 1886 Last : Natural; 1887 Param : Natural; 1888 1889 begin 1890 -- Verify that sw starts with Prefix 1891 1892 if Looking_At (Sw, Sw'First, Prefix) 1893 1894 -- Verify that the group starts with sw 1895 1896 and then Looking_At (Full, Full'First, Sw) 1897 then 1898 Last := Idx + Sw'Length - Prefix'Length - 1; 1899 Param := Last + 1; 1900 1901 if Can_Have_Parameter (Switch) then 1902 1903 -- Include potential parameter to the recursive call. Only 1904 -- numbers are allowed. 1905 1906 while Last < Group'Last 1907 and then Group (Last + 1) in '0' .. '9' 1908 loop 1909 Last := Last + 1; 1910 end loop; 1911 end if; 1912 1913 if not Require_Parameter (Switch) or else Last >= Param then 1914 if Idx = Group'First 1915 and then Last = Group'Last 1916 and then Last < Param 1917 then 1918 -- The group only concerns a single switch. Do not 1919 -- perform recursive call. 1920 1921 -- Note that we still perform a recursive call if 1922 -- a parameter is detected in the switch, as this 1923 -- is a way to correctly identify such a parameter 1924 -- in aliases. 1925 1926 return False; 1927 end if; 1928 1929 Found := True; 1930 1931 -- Recursive call, using the detected parameter if any 1932 1933 if Last >= Param then 1934 For_Each_Simple_Switch 1935 (Config, 1936 Section, 1937 Prefix & Group (Idx .. Param - 1), 1938 Group (Param .. Last)); 1939 1940 else 1941 For_Each_Simple_Switch 1942 (Config, Section, Prefix & Group (Idx .. Last), ""); 1943 end if; 1944 1945 Idx := Last + 1; 1946 return False; 1947 end if; 1948 end if; 1949 1950 return True; 1951 end Analyze_Simple_Switch; 1952 1953 procedure Foreach is new Foreach_Switch (Analyze_Simple_Switch); 1954 1955 -- Start of processing for Group_Analysis 1956 1957 begin 1958 Idx := Group'First; 1959 while Idx <= Group'Last loop 1960 Found := False; 1961 Foreach (Config, Section); 1962 1963 if not Found then 1964 For_Each_Simple_Switch 1965 (Config, Section, Prefix & Group (Idx), ""); 1966 Idx := Idx + 1; 1967 end if; 1968 end loop; 1969 1970 return True; 1971 end Group_Analysis; 1972 1973 ------------------ 1974 -- Is_In_Config -- 1975 ------------------ 1976 1977 function Is_In_Config 1978 (Config_Switch : String; Index : Integer) return Boolean 1979 is 1980 Last : Natural; 1981 P : Switch_Parameter_Type; 1982 1983 begin 1984 Decompose_Switch (Config_Switch, P, Last); 1985 1986 if Config_Switch (Config_Switch'First .. Last) = Switch then 1987 case P is 1988 when Parameter_None => 1989 if Parameter = "" then 1990 Callback (Switch, "", "", Index => Index); 1991 Found_In_Config := True; 1992 return False; 1993 end if; 1994 1995 when Parameter_With_Optional_Space => 1996 Callback (Switch, " ", Parameter, Index => Index); 1997 Found_In_Config := True; 1998 return False; 1999 2000 when Parameter_With_Space_Or_Equal => 2001 Callback (Switch, "=", Parameter, Index => Index); 2002 Found_In_Config := True; 2003 return False; 2004 2005 when Parameter_No_Space 2006 | Parameter_Optional 2007 => 2008 Callback (Switch, "", Parameter, Index); 2009 Found_In_Config := True; 2010 return False; 2011 end case; 2012 end if; 2013 2014 return True; 2015 end Is_In_Config; 2016 2017 ----------------- 2018 -- Starts_With -- 2019 ----------------- 2020 2021 function Starts_With 2022 (Config_Switch : String; Index : Integer) return Boolean 2023 is 2024 Last : Natural; 2025 Param : Natural; 2026 P : Switch_Parameter_Type; 2027 2028 begin 2029 -- This function is called when we believe the parameter was 2030 -- specified as part of the switch, instead of separately. Thus we 2031 -- look in the config to find all possible switches. 2032 2033 Decompose_Switch (Config_Switch, P, Last); 2034 2035 if Looking_At 2036 (Switch, Switch'First, 2037 Config_Switch (Config_Switch'First .. Last)) 2038 then 2039 -- Set first char of Param, and last char of Switch 2040 2041 Param := Switch'First + Last; 2042 Last := Switch'First + Last - Config_Switch'First; 2043 2044 case P is 2045 2046 -- None is already handled in Is_In_Config 2047 2048 when Parameter_None => 2049 null; 2050 2051 when Parameter_With_Space_Or_Equal => 2052 if Param <= Switch'Last 2053 and then 2054 (Switch (Param) = ' ' or else Switch (Param) = '=') 2055 then 2056 Callback (Switch (Switch'First .. Last), 2057 "=", Switch (Param + 1 .. Switch'Last), Index); 2058 Found_In_Config := True; 2059 return False; 2060 end if; 2061 2062 when Parameter_With_Optional_Space => 2063 if Param <= Switch'Last and then Switch (Param) = ' ' then 2064 Param := Param + 1; 2065 end if; 2066 2067 Callback (Switch (Switch'First .. Last), 2068 " ", Switch (Param .. Switch'Last), Index); 2069 Found_In_Config := True; 2070 return False; 2071 2072 when Parameter_No_Space 2073 | Parameter_Optional 2074 => 2075 Callback (Switch (Switch'First .. Last), 2076 "", Switch (Param .. Switch'Last), Index); 2077 Found_In_Config := True; 2078 return False; 2079 end case; 2080 end if; 2081 return True; 2082 end Starts_With; 2083 2084 procedure Foreach_In_Config is new Foreach_Switch (Is_In_Config); 2085 procedure Foreach_Starts_With is new Foreach_Switch (Starts_With); 2086 2087 -- Start of processing for For_Each_Simple_Switch 2088 2089 begin 2090 -- First determine if the switch corresponds to one belonging to the 2091 -- configuration. If so, run callback and exit. 2092 2093 -- ??? Is this necessary. On simple tests, we seem to have the same 2094 -- results with or without this call. 2095 2096 Foreach_In_Config (Config, Section); 2097 2098 if Found_In_Config then 2099 return; 2100 end if; 2101 2102 -- If adding a switch that can in fact be expanded through aliases, 2103 -- add separately each of its expansions. 2104 2105 -- This takes care of expansions like "-T" -> "-gnatwrs", where the 2106 -- alias and its expansion do not have the same prefix. Given the order 2107 -- in which we do things here, the expansion of the alias will itself 2108 -- be checked for a common prefix and split into simple switches. 2109 2110 if Unalias 2111 and then Config /= null 2112 and then Config.Aliases /= null 2113 then 2114 for A in Config.Aliases'Range loop 2115 if Config.Aliases (A).Section.all = Section 2116 and then Config.Aliases (A).Alias.all = Switch 2117 and then Parameter = "" 2118 then 2119 For_Each_Simple_Switch 2120 (Config, Section, Config.Aliases (A).Expansion.all, ""); 2121 return; 2122 end if; 2123 end loop; 2124 end if; 2125 2126 -- If adding a switch grouping several switches, add each of the simple 2127 -- switches instead. 2128 2129 if Config /= null and then Config.Prefixes /= null then 2130 for P in Config.Prefixes'Range loop 2131 if Switch'Length > Config.Prefixes (P)'Length + 1 2132 and then 2133 Looking_At (Switch, Switch'First, Config.Prefixes (P).all) 2134 then 2135 -- Alias expansion will be done recursively 2136 2137 if Config.Switches = null then 2138 for S in Switch'First + Config.Prefixes (P)'Length 2139 .. Switch'Last 2140 loop 2141 For_Each_Simple_Switch 2142 (Config, Section, 2143 Config.Prefixes (P).all & Switch (S), ""); 2144 end loop; 2145 2146 return; 2147 2148 elsif Group_Analysis 2149 (Config.Prefixes (P).all, 2150 Switch 2151 (Switch'First + Config.Prefixes (P)'Length .. Switch'Last)) 2152 then 2153 -- Recursive calls already done on each switch of the group: 2154 -- Return without executing Callback. 2155 2156 return; 2157 end if; 2158 end if; 2159 end loop; 2160 end if; 2161 2162 -- Test if added switch is a known switch with parameter attached 2163 -- instead of being specified separately 2164 2165 if Parameter = "" 2166 and then Config /= null 2167 and then Config.Switches /= null 2168 then 2169 Found_In_Config := False; 2170 Foreach_Starts_With (Config, Section); 2171 2172 if Found_In_Config then 2173 return; 2174 end if; 2175 end if; 2176 2177 -- The switch is invalid in the config, but we still want to report it. 2178 -- The config could, for instance, include "*" to specify it accepts 2179 -- all switches. 2180 2181 Callback (Switch, " ", Parameter, Index => -1); 2182 end For_Each_Simple_Switch; 2183 2184 ---------------- 2185 -- Add_Switch -- 2186 ---------------- 2187 2188 procedure Add_Switch 2189 (Cmd : in out Command_Line; 2190 Switch : String; 2191 Parameter : String := ""; 2192 Separator : Character := ASCII.NUL; 2193 Section : String := ""; 2194 Add_Before : Boolean := False) 2195 is 2196 Success : Boolean; 2197 pragma Unreferenced (Success); 2198 begin 2199 Add_Switch (Cmd, Switch, Parameter, Separator, 2200 Section, Add_Before, Success); 2201 end Add_Switch; 2202 2203 ---------------- 2204 -- Add_Switch -- 2205 ---------------- 2206 2207 procedure Add_Switch 2208 (Cmd : in out Command_Line; 2209 Switch : String; 2210 Parameter : String := ""; 2211 Separator : Character := ASCII.NUL; 2212 Section : String := ""; 2213 Add_Before : Boolean := False; 2214 Success : out Boolean) 2215 is 2216 procedure Add_Simple_Switch 2217 (Simple : String; 2218 Sepa : String; 2219 Param : String; 2220 Index : Integer); 2221 -- Add a new switch that has had all its aliases expanded, and switches 2222 -- ungrouped. We know there are no more aliases in Switches. 2223 2224 ----------------------- 2225 -- Add_Simple_Switch -- 2226 ----------------------- 2227 2228 procedure Add_Simple_Switch 2229 (Simple : String; 2230 Sepa : String; 2231 Param : String; 2232 Index : Integer) 2233 is 2234 Sep : Character; 2235 2236 begin 2237 if Index = -1 2238 and then Cmd.Config /= null 2239 and then not Cmd.Config.Star_Switch 2240 then 2241 raise Invalid_Switch 2242 with "Invalid switch " & Simple; 2243 end if; 2244 2245 if Separator /= ASCII.NUL then 2246 Sep := Separator; 2247 2248 elsif Sepa = "" then 2249 Sep := ASCII.NUL; 2250 else 2251 Sep := Sepa (Sepa'First); 2252 end if; 2253 2254 if Cmd.Expanded = null then 2255 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple)); 2256 2257 if Param /= "" then 2258 Cmd.Params := 2259 new Argument_List'(1 .. 1 => new String'(Sep & Param)); 2260 else 2261 Cmd.Params := new Argument_List'(1 .. 1 => null); 2262 end if; 2263 2264 if Section = "" then 2265 Cmd.Sections := new Argument_List'(1 .. 1 => null); 2266 else 2267 Cmd.Sections := 2268 new Argument_List'(1 .. 1 => new String'(Section)); 2269 end if; 2270 2271 else 2272 -- Do we already have this switch? 2273 2274 for C in Cmd.Expanded'Range loop 2275 if Cmd.Expanded (C).all = Simple 2276 and then 2277 ((Cmd.Params (C) = null and then Param = "") 2278 or else 2279 (Cmd.Params (C) /= null 2280 and then Cmd.Params (C).all = Sep & Param)) 2281 and then 2282 ((Cmd.Sections (C) = null and then Section = "") 2283 or else 2284 (Cmd.Sections (C) /= null 2285 and then Cmd.Sections (C).all = Section)) 2286 then 2287 return; 2288 end if; 2289 end loop; 2290 2291 -- Inserting at least one switch 2292 2293 Success := True; 2294 Add (Cmd.Expanded, new String'(Simple), Add_Before); 2295 2296 if Param /= "" then 2297 Add 2298 (Cmd.Params, 2299 new String'(Sep & Param), 2300 Add_Before); 2301 else 2302 Add 2303 (Cmd.Params, 2304 null, 2305 Add_Before); 2306 end if; 2307 2308 if Section = "" then 2309 Add 2310 (Cmd.Sections, 2311 null, 2312 Add_Before); 2313 else 2314 Add 2315 (Cmd.Sections, 2316 new String'(Section), 2317 Add_Before); 2318 end if; 2319 end if; 2320 end Add_Simple_Switch; 2321 2322 procedure Add_Simple_Switches is 2323 new For_Each_Simple_Switch (Add_Simple_Switch); 2324 2325 -- Local Variables 2326 2327 Section_Valid : Boolean := False; 2328 2329 -- Start of processing for Add_Switch 2330 2331 begin 2332 if Section /= "" and then Cmd.Config /= null then 2333 for S in Cmd.Config.Sections'Range loop 2334 if Section = Cmd.Config.Sections (S).all then 2335 Section_Valid := True; 2336 exit; 2337 end if; 2338 end loop; 2339 2340 if not Section_Valid then 2341 raise Invalid_Section; 2342 end if; 2343 end if; 2344 2345 Success := False; 2346 Add_Simple_Switches (Cmd.Config, Section, Switch, Parameter); 2347 Free (Cmd.Coalesce); 2348 end Add_Switch; 2349 2350 ------------ 2351 -- Remove -- 2352 ------------ 2353 2354 procedure Remove (Line : in out Argument_List_Access; Index : Integer) is 2355 Tmp : Argument_List_Access := Line; 2356 2357 begin 2358 Line := new Argument_List (Tmp'First .. Tmp'Last - 1); 2359 2360 if Index /= Tmp'First then 2361 Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1); 2362 end if; 2363 2364 Free (Tmp (Index)); 2365 2366 if Index /= Tmp'Last then 2367 Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last); 2368 end if; 2369 2370 Unchecked_Free (Tmp); 2371 end Remove; 2372 2373 --------- 2374 -- Add -- 2375 --------- 2376 2377 procedure Add 2378 (Line : in out Argument_List_Access; 2379 Str : String_Access; 2380 Before : Boolean := False) 2381 is 2382 Tmp : Argument_List_Access := Line; 2383 2384 begin 2385 if Tmp /= null then 2386 Line := new Argument_List (Tmp'First .. Tmp'Last + 1); 2387 2388 if Before then 2389 Line (Tmp'First) := Str; 2390 Line (Tmp'First + 1 .. Tmp'Last + 1) := Tmp.all; 2391 else 2392 Line (Tmp'Range) := Tmp.all; 2393 Line (Tmp'Last + 1) := Str; 2394 end if; 2395 2396 Unchecked_Free (Tmp); 2397 2398 else 2399 Line := new Argument_List'(1 .. 1 => Str); 2400 end if; 2401 end Add; 2402 2403 ------------------- 2404 -- Remove_Switch -- 2405 ------------------- 2406 2407 procedure Remove_Switch 2408 (Cmd : in out Command_Line; 2409 Switch : String; 2410 Remove_All : Boolean := False; 2411 Has_Parameter : Boolean := False; 2412 Section : String := "") 2413 is 2414 Success : Boolean; 2415 pragma Unreferenced (Success); 2416 begin 2417 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success); 2418 end Remove_Switch; 2419 2420 ------------------- 2421 -- Remove_Switch -- 2422 ------------------- 2423 2424 procedure Remove_Switch 2425 (Cmd : in out Command_Line; 2426 Switch : String; 2427 Remove_All : Boolean := False; 2428 Has_Parameter : Boolean := False; 2429 Section : String := ""; 2430 Success : out Boolean) 2431 is 2432 procedure Remove_Simple_Switch 2433 (Simple, Separator, Param : String; Index : Integer); 2434 -- Removes a simple switch, with no aliasing or grouping 2435 2436 -------------------------- 2437 -- Remove_Simple_Switch -- 2438 -------------------------- 2439 2440 procedure Remove_Simple_Switch 2441 (Simple, Separator, Param : String; Index : Integer) 2442 is 2443 C : Integer; 2444 pragma Unreferenced (Param, Separator, Index); 2445 2446 begin 2447 if Cmd.Expanded /= null then 2448 C := Cmd.Expanded'First; 2449 while C <= Cmd.Expanded'Last loop 2450 if Cmd.Expanded (C).all = Simple 2451 and then 2452 (Remove_All 2453 or else (Cmd.Sections (C) = null 2454 and then Section = "") 2455 or else (Cmd.Sections (C) /= null 2456 and then Section = Cmd.Sections (C).all)) 2457 and then (not Has_Parameter or else Cmd.Params (C) /= null) 2458 then 2459 Remove (Cmd.Expanded, C); 2460 Remove (Cmd.Params, C); 2461 Remove (Cmd.Sections, C); 2462 Success := True; 2463 2464 if not Remove_All then 2465 return; 2466 end if; 2467 2468 else 2469 C := C + 1; 2470 end if; 2471 end loop; 2472 end if; 2473 end Remove_Simple_Switch; 2474 2475 procedure Remove_Simple_Switches is 2476 new For_Each_Simple_Switch (Remove_Simple_Switch); 2477 2478 -- Start of processing for Remove_Switch 2479 2480 begin 2481 Success := False; 2482 Remove_Simple_Switches 2483 (Cmd.Config, Section, Switch, "", Unalias => not Has_Parameter); 2484 Free (Cmd.Coalesce); 2485 end Remove_Switch; 2486 2487 ------------------- 2488 -- Remove_Switch -- 2489 ------------------- 2490 2491 procedure Remove_Switch 2492 (Cmd : in out Command_Line; 2493 Switch : String; 2494 Parameter : String; 2495 Section : String := "") 2496 is 2497 procedure Remove_Simple_Switch 2498 (Simple, Separator, Param : String; Index : Integer); 2499 -- Removes a simple switch, with no aliasing or grouping 2500 2501 -------------------------- 2502 -- Remove_Simple_Switch -- 2503 -------------------------- 2504 2505 procedure Remove_Simple_Switch 2506 (Simple, Separator, Param : String; Index : Integer) 2507 is 2508 pragma Unreferenced (Separator, Index); 2509 C : Integer; 2510 2511 begin 2512 if Cmd.Expanded /= null then 2513 C := Cmd.Expanded'First; 2514 while C <= Cmd.Expanded'Last loop 2515 if Cmd.Expanded (C).all = Simple 2516 and then 2517 ((Cmd.Sections (C) = null 2518 and then Section = "") 2519 or else 2520 (Cmd.Sections (C) /= null 2521 and then Section = Cmd.Sections (C).all)) 2522 and then 2523 ((Cmd.Params (C) = null and then Param = "") 2524 or else 2525 (Cmd.Params (C) /= null 2526 2527 -- Ignore the separator stored in Parameter 2528 2529 and then 2530 Cmd.Params (C) (Cmd.Params (C)'First + 1 2531 .. Cmd.Params (C)'Last) = Param)) 2532 then 2533 Remove (Cmd.Expanded, C); 2534 Remove (Cmd.Params, C); 2535 Remove (Cmd.Sections, C); 2536 2537 -- The switch is necessarily unique by construction of 2538 -- Add_Switch. 2539 2540 return; 2541 2542 else 2543 C := C + 1; 2544 end if; 2545 end loop; 2546 end if; 2547 end Remove_Simple_Switch; 2548 2549 procedure Remove_Simple_Switches is 2550 new For_Each_Simple_Switch (Remove_Simple_Switch); 2551 2552 -- Start of processing for Remove_Switch 2553 2554 begin 2555 Remove_Simple_Switches (Cmd.Config, Section, Switch, Parameter); 2556 Free (Cmd.Coalesce); 2557 end Remove_Switch; 2558 2559 -------------------- 2560 -- Group_Switches -- 2561 -------------------- 2562 2563 procedure Group_Switches 2564 (Cmd : Command_Line; 2565 Result : Argument_List_Access; 2566 Sections : Argument_List_Access; 2567 Params : Argument_List_Access) 2568 is 2569 function Compatible_Parameter (Param : String_Access) return Boolean; 2570 -- True when the parameter can be part of a group 2571 2572 -------------------------- 2573 -- Compatible_Parameter -- 2574 -------------------------- 2575 2576 function Compatible_Parameter (Param : String_Access) return Boolean is 2577 begin 2578 -- No parameter OK 2579 2580 if Param = null then 2581 return True; 2582 2583 -- We need parameters without separators 2584 2585 elsif Param (Param'First) /= ASCII.NUL then 2586 return False; 2587 2588 -- Parameters must be all digits 2589 2590 else 2591 for J in Param'First + 1 .. Param'Last loop 2592 if Param (J) not in '0' .. '9' then 2593 return False; 2594 end if; 2595 end loop; 2596 2597 return True; 2598 end if; 2599 end Compatible_Parameter; 2600 2601 -- Local declarations 2602 2603 Group : Ada.Strings.Unbounded.Unbounded_String; 2604 First : Natural; 2605 use type Ada.Strings.Unbounded.Unbounded_String; 2606 2607 -- Start of processing for Group_Switches 2608 2609 begin 2610 if Cmd.Config = null or else Cmd.Config.Prefixes = null then 2611 return; 2612 end if; 2613 2614 for P in Cmd.Config.Prefixes'Range loop 2615 Group := Ada.Strings.Unbounded.Null_Unbounded_String; 2616 First := 0; 2617 2618 for C in Result'Range loop 2619 if Result (C) /= null 2620 and then Compatible_Parameter (Params (C)) 2621 and then Looking_At 2622 (Result (C).all, 2623 Result (C)'First, 2624 Cmd.Config.Prefixes (P).all) 2625 then 2626 -- If we are still in the same section, group the switches 2627 2628 if First = 0 2629 or else 2630 (Sections (C) = null 2631 and then Sections (First) = null) 2632 or else 2633 (Sections (C) /= null 2634 and then Sections (First) /= null 2635 and then Sections (C).all = Sections (First).all) 2636 then 2637 Group := 2638 Group & 2639 Result (C) 2640 (Result (C)'First + Cmd.Config.Prefixes (P)'Length .. 2641 Result (C)'Last); 2642 2643 if Params (C) /= null then 2644 Group := 2645 Group & 2646 Params (C) (Params (C)'First + 1 .. Params (C)'Last); 2647 Free (Params (C)); 2648 end if; 2649 2650 if First = 0 then 2651 First := C; 2652 end if; 2653 2654 Free (Result (C)); 2655 2656 -- We changed section: we put the grouped switches to the first 2657 -- place, on continue with the new section. 2658 2659 else 2660 Result (First) := 2661 new String' 2662 (Cmd.Config.Prefixes (P).all & 2663 Ada.Strings.Unbounded.To_String (Group)); 2664 Group := 2665 Ada.Strings.Unbounded.To_Unbounded_String 2666 (Result (C) 2667 (Result (C)'First + Cmd.Config.Prefixes (P)'Length .. 2668 Result (C)'Last)); 2669 First := C; 2670 end if; 2671 end if; 2672 end loop; 2673 2674 if First > 0 then 2675 Result (First) := 2676 new String' 2677 (Cmd.Config.Prefixes (P).all & 2678 Ada.Strings.Unbounded.To_String (Group)); 2679 end if; 2680 end loop; 2681 end Group_Switches; 2682 2683 -------------------- 2684 -- Alias_Switches -- 2685 -------------------- 2686 2687 procedure Alias_Switches 2688 (Cmd : Command_Line; 2689 Result : Argument_List_Access; 2690 Params : Argument_List_Access) 2691 is 2692 Found : Boolean; 2693 First : Natural; 2694 2695 procedure Check_Cb (Switch, Separator, Param : String; Index : Integer); 2696 -- Checks whether the command line contains [Switch]. Sets the global 2697 -- variable [Found] appropriately. This is called for each simple switch 2698 -- that make up an alias, to know whether the alias should be applied. 2699 2700 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer); 2701 -- Remove the simple switch [Switch] from the command line, since it is 2702 -- part of a simpler alias 2703 2704 -------------- 2705 -- Check_Cb -- 2706 -------------- 2707 2708 procedure Check_Cb 2709 (Switch, Separator, Param : String; Index : Integer) 2710 is 2711 pragma Unreferenced (Separator, Index); 2712 2713 begin 2714 if Found then 2715 for E in Result'Range loop 2716 if Result (E) /= null 2717 and then 2718 (Params (E) = null 2719 or else Params (E) (Params (E)'First + 1 .. 2720 Params (E)'Last) = Param) 2721 and then Result (E).all = Switch 2722 then 2723 return; 2724 end if; 2725 end loop; 2726 2727 Found := False; 2728 end if; 2729 end Check_Cb; 2730 2731 --------------- 2732 -- Remove_Cb -- 2733 --------------- 2734 2735 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer) 2736 is 2737 pragma Unreferenced (Separator, Index); 2738 2739 begin 2740 for E in Result'Range loop 2741 if Result (E) /= null 2742 and then 2743 (Params (E) = null 2744 or else Params (E) (Params (E)'First + 1 2745 .. Params (E)'Last) = Param) 2746 and then Result (E).all = Switch 2747 then 2748 if First > E then 2749 First := E; 2750 end if; 2751 2752 Free (Result (E)); 2753 Free (Params (E)); 2754 return; 2755 end if; 2756 end loop; 2757 end Remove_Cb; 2758 2759 procedure Check_All is new For_Each_Simple_Switch (Check_Cb); 2760 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb); 2761 2762 -- Start of processing for Alias_Switches 2763 2764 begin 2765 if Cmd.Config = null or else Cmd.Config.Aliases = null then 2766 return; 2767 end if; 2768 2769 for A in Cmd.Config.Aliases'Range loop 2770 2771 -- Compute the various simple switches that make up the alias. We 2772 -- split the expansion into as many simple switches as possible, and 2773 -- then check whether the expanded command line has all of them. 2774 2775 Found := True; 2776 Check_All (Cmd.Config, 2777 Switch => Cmd.Config.Aliases (A).Expansion.all, 2778 Section => Cmd.Config.Aliases (A).Section.all); 2779 2780 if Found then 2781 First := Integer'Last; 2782 Remove_All (Cmd.Config, 2783 Switch => Cmd.Config.Aliases (A).Expansion.all, 2784 Section => Cmd.Config.Aliases (A).Section.all); 2785 Result (First) := new String'(Cmd.Config.Aliases (A).Alias.all); 2786 end if; 2787 end loop; 2788 end Alias_Switches; 2789 2790 ------------------- 2791 -- Sort_Sections -- 2792 ------------------- 2793 2794 procedure Sort_Sections 2795 (Line : not null GNAT.OS_Lib.Argument_List_Access; 2796 Sections : GNAT.OS_Lib.Argument_List_Access; 2797 Params : GNAT.OS_Lib.Argument_List_Access) 2798 is 2799 Sections_List : Argument_List_Access := 2800 new Argument_List'(1 .. 1 => null); 2801 Found : Boolean; 2802 Old_Line : constant Argument_List := Line.all; 2803 Old_Sections : constant Argument_List := Sections.all; 2804 Old_Params : constant Argument_List := Params.all; 2805 Index : Natural; 2806 2807 begin 2808 -- First construct a list of all sections 2809 2810 for E in Line'Range loop 2811 if Sections (E) /= null then 2812 Found := False; 2813 for S in Sections_List'Range loop 2814 if (Sections_List (S) = null and then Sections (E) = null) 2815 or else 2816 (Sections_List (S) /= null 2817 and then Sections (E) /= null 2818 and then Sections_List (S).all = Sections (E).all) 2819 then 2820 Found := True; 2821 exit; 2822 end if; 2823 end loop; 2824 2825 if not Found then 2826 Add (Sections_List, Sections (E)); 2827 end if; 2828 end if; 2829 end loop; 2830 2831 Index := Line'First; 2832 2833 for S in Sections_List'Range loop 2834 for E in Old_Line'Range loop 2835 if (Sections_List (S) = null and then Old_Sections (E) = null) 2836 or else 2837 (Sections_List (S) /= null 2838 and then Old_Sections (E) /= null 2839 and then Sections_List (S).all = Old_Sections (E).all) 2840 then 2841 Line (Index) := Old_Line (E); 2842 Sections (Index) := Old_Sections (E); 2843 Params (Index) := Old_Params (E); 2844 Index := Index + 1; 2845 end if; 2846 end loop; 2847 end loop; 2848 2849 Unchecked_Free (Sections_List); 2850 end Sort_Sections; 2851 2852 ----------- 2853 -- Start -- 2854 ----------- 2855 2856 procedure Start 2857 (Cmd : in out Command_Line; 2858 Iter : in out Command_Line_Iterator; 2859 Expanded : Boolean := False) 2860 is 2861 begin 2862 if Cmd.Expanded = null then 2863 Iter.List := null; 2864 return; 2865 end if; 2866 2867 -- Reorder the expanded line so that sections are grouped 2868 2869 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params); 2870 2871 -- Coalesce the switches as much as possible 2872 2873 if not Expanded 2874 and then Cmd.Coalesce = null 2875 then 2876 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range); 2877 for E in Cmd.Expanded'Range loop 2878 Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all); 2879 end loop; 2880 2881 Free (Cmd.Coalesce_Sections); 2882 Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range); 2883 for E in Cmd.Sections'Range loop 2884 Cmd.Coalesce_Sections (E) := 2885 (if Cmd.Sections (E) = null then null 2886 else new String'(Cmd.Sections (E).all)); 2887 end loop; 2888 2889 Free (Cmd.Coalesce_Params); 2890 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range); 2891 for E in Cmd.Params'Range loop 2892 Cmd.Coalesce_Params (E) := 2893 (if Cmd.Params (E) = null then null 2894 else new String'(Cmd.Params (E).all)); 2895 end loop; 2896 2897 -- Not a clone, since we will not modify the parameters anyway 2898 2899 Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params); 2900 Group_Switches 2901 (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params); 2902 end if; 2903 2904 if Expanded then 2905 Iter.List := Cmd.Expanded; 2906 Iter.Params := Cmd.Params; 2907 Iter.Sections := Cmd.Sections; 2908 else 2909 Iter.List := Cmd.Coalesce; 2910 Iter.Params := Cmd.Coalesce_Params; 2911 Iter.Sections := Cmd.Coalesce_Sections; 2912 end if; 2913 2914 if Iter.List = null then 2915 Iter.Current := Integer'Last; 2916 else 2917 Iter.Current := Iter.List'First - 1; 2918 Next (Iter); 2919 end if; 2920 end Start; 2921 2922 -------------------- 2923 -- Current_Switch -- 2924 -------------------- 2925 2926 function Current_Switch (Iter : Command_Line_Iterator) return String is 2927 begin 2928 return Iter.List (Iter.Current).all; 2929 end Current_Switch; 2930 2931 -------------------- 2932 -- Is_New_Section -- 2933 -------------------- 2934 2935 function Is_New_Section (Iter : Command_Line_Iterator) return Boolean is 2936 Section : constant String := Current_Section (Iter); 2937 2938 begin 2939 if Iter.Sections = null then 2940 return False; 2941 2942 elsif Iter.Current = Iter.Sections'First 2943 or else Iter.Sections (Iter.Current - 1) = null 2944 then 2945 return Section /= ""; 2946 2947 else 2948 return Section /= Iter.Sections (Iter.Current - 1).all; 2949 end if; 2950 end Is_New_Section; 2951 2952 --------------------- 2953 -- Current_Section -- 2954 --------------------- 2955 2956 function Current_Section (Iter : Command_Line_Iterator) return String is 2957 begin 2958 if Iter.Sections = null 2959 or else Iter.Current > Iter.Sections'Last 2960 or else Iter.Sections (Iter.Current) = null 2961 then 2962 return ""; 2963 end if; 2964 2965 return Iter.Sections (Iter.Current).all; 2966 end Current_Section; 2967 2968 ----------------------- 2969 -- Current_Separator -- 2970 ----------------------- 2971 2972 function Current_Separator (Iter : Command_Line_Iterator) return String is 2973 begin 2974 if Iter.Params = null 2975 or else Iter.Current > Iter.Params'Last 2976 or else Iter.Params (Iter.Current) = null 2977 then 2978 return ""; 2979 2980 else 2981 declare 2982 Sep : constant Character := 2983 Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First); 2984 begin 2985 if Sep = ASCII.NUL then 2986 return ""; 2987 else 2988 return "" & Sep; 2989 end if; 2990 end; 2991 end if; 2992 end Current_Separator; 2993 2994 ----------------------- 2995 -- Current_Parameter -- 2996 ----------------------- 2997 2998 function Current_Parameter (Iter : Command_Line_Iterator) return String is 2999 begin 3000 if Iter.Params = null 3001 or else Iter.Current > Iter.Params'Last 3002 or else Iter.Params (Iter.Current) = null 3003 then 3004 return ""; 3005 3006 else 3007 -- Return result, skipping separator 3008 3009 declare 3010 P : constant String := Iter.Params (Iter.Current).all; 3011 begin 3012 return P (P'First + 1 .. P'Last); 3013 end; 3014 end if; 3015 end Current_Parameter; 3016 3017 -------------- 3018 -- Has_More -- 3019 -------------- 3020 3021 function Has_More (Iter : Command_Line_Iterator) return Boolean is 3022 begin 3023 return Iter.List /= null and then Iter.Current <= Iter.List'Last; 3024 end Has_More; 3025 3026 ---------- 3027 -- Next -- 3028 ---------- 3029 3030 procedure Next (Iter : in out Command_Line_Iterator) is 3031 begin 3032 Iter.Current := Iter.Current + 1; 3033 while Iter.Current <= Iter.List'Last 3034 and then Iter.List (Iter.Current) = null 3035 loop 3036 Iter.Current := Iter.Current + 1; 3037 end loop; 3038 end Next; 3039 3040 ---------- 3041 -- Free -- 3042 ---------- 3043 3044 procedure Free (Config : in out Command_Line_Configuration) is 3045 procedure Unchecked_Free is new 3046 Ada.Unchecked_Deallocation 3047 (Switch_Definitions, Switch_Definitions_List); 3048 3049 procedure Unchecked_Free is new 3050 Ada.Unchecked_Deallocation 3051 (Alias_Definitions, Alias_Definitions_List); 3052 3053 begin 3054 if Config /= null then 3055 Free (Config.Prefixes); 3056 Free (Config.Sections); 3057 Free (Config.Usage); 3058 Free (Config.Help); 3059 Free (Config.Help_Msg); 3060 3061 if Config.Aliases /= null then 3062 for A in Config.Aliases'Range loop 3063 Free (Config.Aliases (A).Alias); 3064 Free (Config.Aliases (A).Expansion); 3065 Free (Config.Aliases (A).Section); 3066 end loop; 3067 3068 Unchecked_Free (Config.Aliases); 3069 end if; 3070 3071 if Config.Switches /= null then 3072 for S in Config.Switches'Range loop 3073 Free (Config.Switches (S).Switch); 3074 Free (Config.Switches (S).Long_Switch); 3075 Free (Config.Switches (S).Help); 3076 Free (Config.Switches (S).Section); 3077 Free (Config.Switches (S).Argument); 3078 end loop; 3079 3080 Unchecked_Free (Config.Switches); 3081 end if; 3082 3083 Unchecked_Free (Config); 3084 end if; 3085 end Free; 3086 3087 ---------- 3088 -- Free -- 3089 ---------- 3090 3091 procedure Free (Cmd : in out Command_Line) is 3092 begin 3093 Free (Cmd.Expanded); 3094 Free (Cmd.Coalesce); 3095 Free (Cmd.Coalesce_Sections); 3096 Free (Cmd.Coalesce_Params); 3097 Free (Cmd.Params); 3098 Free (Cmd.Sections); 3099 end Free; 3100 3101 --------------- 3102 -- Set_Usage -- 3103 --------------- 3104 3105 procedure Set_Usage 3106 (Config : in out Command_Line_Configuration; 3107 Usage : String := "[switches] [arguments]"; 3108 Help : String := ""; 3109 Help_Msg : String := "") 3110 is 3111 begin 3112 if Config = null then 3113 Config := new Command_Line_Configuration_Record; 3114 end if; 3115 3116 Free (Config.Usage); 3117 Free (Config.Help); 3118 Free (Config.Help_Msg); 3119 3120 Config.Usage := new String'(Usage); 3121 Config.Help := new String'(Help); 3122 Config.Help_Msg := new String'(Help_Msg); 3123 end Set_Usage; 3124 3125 ------------------ 3126 -- Display_Help -- 3127 ------------------ 3128 3129 procedure Display_Help (Config : Command_Line_Configuration) is 3130 function Switch_Name 3131 (Def : Switch_Definition; 3132 Section : String) return String; 3133 -- Return the "-short, --long=ARG" string for Def. 3134 -- Returns "" if the switch is not in the section. 3135 3136 function Param_Name 3137 (P : Switch_Parameter_Type; 3138 Name : String := "ARG") return String; 3139 -- Return the display for a switch parameter 3140 3141 procedure Display_Section_Help (Section : String); 3142 -- Display the help for a specific section ("" is the default section) 3143 3144 -------------------------- 3145 -- Display_Section_Help -- 3146 -------------------------- 3147 3148 procedure Display_Section_Help (Section : String) is 3149 Max_Len : Natural := 0; 3150 3151 begin 3152 -- ??? Special display for "*" 3153 3154 New_Line; 3155 3156 if Section /= "" then 3157 Put_Line ("Switches after " & Section); 3158 end if; 3159 3160 -- Compute size of the switches column 3161 3162 for S in Config.Switches'Range loop 3163 Max_Len := Natural'Max 3164 (Max_Len, Switch_Name (Config.Switches (S), Section)'Length); 3165 end loop; 3166 3167 if Config.Aliases /= null then 3168 for A in Config.Aliases'Range loop 3169 if Config.Aliases (A).Section.all = Section then 3170 Max_Len := Natural'Max 3171 (Max_Len, Config.Aliases (A).Alias'Length); 3172 end if; 3173 end loop; 3174 end if; 3175 3176 -- Display the switches 3177 3178 for S in Config.Switches'Range loop 3179 declare 3180 N : constant String := 3181 Switch_Name (Config.Switches (S), Section); 3182 3183 begin 3184 if N /= "" then 3185 Put (" "); 3186 Put (N); 3187 Put ((1 .. Max_Len - N'Length + 1 => ' ')); 3188 3189 if Config.Switches (S).Help /= null then 3190 Put (Config.Switches (S).Help.all); 3191 end if; 3192 3193 New_Line; 3194 end if; 3195 end; 3196 end loop; 3197 3198 -- Display the aliases 3199 3200 if Config.Aliases /= null then 3201 for A in Config.Aliases'Range loop 3202 if Config.Aliases (A).Section.all = Section then 3203 Put (" "); 3204 Put (Config.Aliases (A).Alias.all); 3205 Put ((1 .. Max_Len - Config.Aliases (A).Alias'Length + 1 3206 => ' ')); 3207 Put ("Equivalent to " & Config.Aliases (A).Expansion.all); 3208 New_Line; 3209 end if; 3210 end loop; 3211 end if; 3212 end Display_Section_Help; 3213 3214 ---------------- 3215 -- Param_Name -- 3216 ---------------- 3217 3218 function Param_Name 3219 (P : Switch_Parameter_Type; 3220 Name : String := "ARG") return String 3221 is 3222 begin 3223 case P is 3224 when Parameter_None => 3225 return ""; 3226 3227 when Parameter_With_Optional_Space => 3228 return " " & To_Upper (Name); 3229 3230 when Parameter_With_Space_Or_Equal => 3231 return "=" & To_Upper (Name); 3232 3233 when Parameter_No_Space => 3234 return To_Upper (Name); 3235 3236 when Parameter_Optional => 3237 return '[' & To_Upper (Name) & ']'; 3238 end case; 3239 end Param_Name; 3240 3241 ----------------- 3242 -- Switch_Name -- 3243 ----------------- 3244 3245 function Switch_Name 3246 (Def : Switch_Definition; 3247 Section : String) return String 3248 is 3249 use Ada.Strings.Unbounded; 3250 Result : Unbounded_String; 3251 P1, P2 : Switch_Parameter_Type; 3252 Last1, Last2 : Integer := 0; 3253 3254 begin 3255 if (Section = "" and then Def.Section = null) 3256 or else (Def.Section /= null and then Def.Section.all = Section) 3257 then 3258 if Def.Switch /= null and then Def.Switch.all = "*" then 3259 return "[any switch]"; 3260 end if; 3261 3262 if Def.Switch /= null then 3263 Decompose_Switch (Def.Switch.all, P1, Last1); 3264 Append (Result, Def.Switch (Def.Switch'First .. Last1)); 3265 3266 if Def.Long_Switch /= null then 3267 Decompose_Switch (Def.Long_Switch.all, P2, Last2); 3268 Append (Result, ", " 3269 & Def.Long_Switch (Def.Long_Switch'First .. Last2)); 3270 3271 if Def.Argument = null then 3272 Append (Result, Param_Name (P2, "ARG")); 3273 else 3274 Append (Result, Param_Name (P2, Def.Argument.all)); 3275 end if; 3276 3277 else 3278 if Def.Argument = null then 3279 Append (Result, Param_Name (P1, "ARG")); 3280 else 3281 Append (Result, Param_Name (P1, Def.Argument.all)); 3282 end if; 3283 end if; 3284 3285 -- Def.Switch is null (Long_Switch must be non-null) 3286 3287 else 3288 Decompose_Switch (Def.Long_Switch.all, P2, Last2); 3289 Append (Result, 3290 Def.Long_Switch (Def.Long_Switch'First .. Last2)); 3291 3292 if Def.Argument = null then 3293 Append (Result, Param_Name (P2, "ARG")); 3294 else 3295 Append (Result, Param_Name (P2, Def.Argument.all)); 3296 end if; 3297 end if; 3298 end if; 3299 3300 return To_String (Result); 3301 end Switch_Name; 3302 3303 -- Start of processing for Display_Help 3304 3305 begin 3306 if Config = null then 3307 return; 3308 end if; 3309 3310 if Config.Help /= null and then Config.Help.all /= "" then 3311 Put_Line (Config.Help.all); 3312 end if; 3313 3314 if Config.Usage /= null then 3315 Put_Line ("Usage: " 3316 & Base_Name 3317 (Ada.Command_Line.Command_Name) & " " & Config.Usage.all); 3318 else 3319 Put_Line ("Usage: " & Base_Name (Ada.Command_Line.Command_Name) 3320 & " [switches] [arguments]"); 3321 end if; 3322 3323 if Config.Help_Msg /= null and then Config.Help_Msg.all /= "" then 3324 Put_Line (Config.Help_Msg.all); 3325 3326 else 3327 Display_Section_Help (""); 3328 3329 if Config.Sections /= null and then Config.Switches /= null then 3330 for S in Config.Sections'Range loop 3331 Display_Section_Help (Config.Sections (S).all); 3332 end loop; 3333 end if; 3334 end if; 3335 end Display_Help; 3336 3337 ------------ 3338 -- Getopt -- 3339 ------------ 3340 3341 procedure Getopt 3342 (Config : Command_Line_Configuration; 3343 Callback : Switch_Handler := null; 3344 Parser : Opt_Parser := Command_Line_Parser; 3345 Concatenate : Boolean := True) 3346 is 3347 Local_Config : Command_Line_Configuration := Config; 3348 Getopt_Switches : String_Access; 3349 C : Character := ASCII.NUL; 3350 3351 Empty_Name : aliased constant String := ""; 3352 Current_Section : Integer := -1; 3353 Section_Name : not null access constant String := Empty_Name'Access; 3354 3355 procedure Simple_Callback 3356 (Simple_Switch : String; 3357 Separator : String; 3358 Parameter : String; 3359 Index : Integer); 3360 -- Needs comments ??? 3361 3362 procedure Do_Callback (Switch, Parameter : String; Index : Integer); 3363 3364 ----------------- 3365 -- Do_Callback -- 3366 ----------------- 3367 3368 procedure Do_Callback (Switch, Parameter : String; Index : Integer) is 3369 begin 3370 -- Do automatic handling when possible 3371 3372 if Index /= -1 then 3373 case Local_Config.Switches (Index).Typ is 3374 when Switch_Untyped => 3375 null; -- no automatic handling 3376 3377 when Switch_Boolean => 3378 Local_Config.Switches (Index).Boolean_Output.all := 3379 Local_Config.Switches (Index).Boolean_Value; 3380 return; 3381 3382 when Switch_Integer => 3383 begin 3384 if Parameter = "" then 3385 Local_Config.Switches (Index).Integer_Output.all := 3386 Local_Config.Switches (Index).Integer_Default; 3387 else 3388 Local_Config.Switches (Index).Integer_Output.all := 3389 Integer'Value (Parameter); 3390 end if; 3391 3392 exception 3393 when Constraint_Error => 3394 raise Invalid_Parameter 3395 with "Expected integer parameter for '" 3396 & Switch & "'"; 3397 end; 3398 3399 return; 3400 3401 when Switch_String => 3402 Free (Local_Config.Switches (Index).String_Output.all); 3403 Local_Config.Switches (Index).String_Output.all := 3404 new String'(Parameter); 3405 return; 3406 end case; 3407 end if; 3408 3409 -- Otherwise calls the user callback if one was defined 3410 3411 if Callback /= null then 3412 Callback (Switch => Switch, 3413 Parameter => Parameter, 3414 Section => Section_Name.all); 3415 end if; 3416 end Do_Callback; 3417 3418 procedure For_Each_Simple 3419 is new For_Each_Simple_Switch (Simple_Callback); 3420 3421 --------------------- 3422 -- Simple_Callback -- 3423 --------------------- 3424 3425 procedure Simple_Callback 3426 (Simple_Switch : String; 3427 Separator : String; 3428 Parameter : String; 3429 Index : Integer) 3430 is 3431 pragma Unreferenced (Separator); 3432 begin 3433 Do_Callback (Switch => Simple_Switch, 3434 Parameter => Parameter, 3435 Index => Index); 3436 end Simple_Callback; 3437 3438 -- Start of processing for Getopt 3439 3440 begin 3441 -- We work with a local copy of Config, because Config can be null, for 3442 -- example if Define_Switch was never called. We could modify Config 3443 -- itself, but then we would have to make it into an 'in out' parameter, 3444 -- which would be incompatible. 3445 3446 if Local_Config = null then 3447 Local_Config := new Command_Line_Configuration_Record; 3448 end if; 3449 3450 if Local_Config.Switches = null then 3451 Local_Config.Switches := new Switch_Definitions (1 .. 0); 3452 end if; 3453 3454 -- Initialize sections 3455 3456 if Local_Config.Sections = null then 3457 Local_Config.Sections := new Argument_List'(1 .. 0 => null); 3458 end if; 3459 3460 Internal_Initialize_Option_Scan 3461 (Parser => Parser, 3462 Switch_Char => Parser.Switch_Character, 3463 Stop_At_First_Non_Switch => Parser.Stop_At_First, 3464 Section_Delimiters => Section_Delimiters (Local_Config)); 3465 3466 Getopt_Switches := new String' 3467 (Get_Switches (Local_Config, Parser.Switch_Character, Section_Name.all) 3468 & " h -help"); 3469 3470 -- Initialize output values for automatically handled switches 3471 3472 for S in Local_Config.Switches'Range loop 3473 case Local_Config.Switches (S).Typ is 3474 when Switch_Untyped => 3475 null; -- Nothing to do 3476 3477 when Switch_Boolean => 3478 Local_Config.Switches (S).Boolean_Output.all := 3479 not Local_Config.Switches (S).Boolean_Value; 3480 3481 when Switch_Integer => 3482 Local_Config.Switches (S).Integer_Output.all := 3483 Local_Config.Switches (S).Integer_Initial; 3484 3485 when Switch_String => 3486 if Local_Config.Switches (S).String_Output.all = null then 3487 Local_Config.Switches (S).String_Output.all := 3488 new String'(""); 3489 end if; 3490 end case; 3491 end loop; 3492 3493 -- For all sections, and all switches within those sections 3494 3495 loop 3496 C := Getopt (Switches => Getopt_Switches.all, 3497 Concatenate => Concatenate, 3498 Parser => Parser); 3499 3500 if C = '*' then 3501 -- Full_Switch already includes the leading '-' 3502 3503 Do_Callback (Switch => Full_Switch (Parser), 3504 Parameter => Parameter (Parser), 3505 Index => -1); 3506 3507 elsif C /= ASCII.NUL then 3508 if Full_Switch (Parser) = "h" 3509 or else 3510 Full_Switch (Parser) = "-help" 3511 then 3512 Display_Help (Local_Config); 3513 raise Exit_From_Command_Line; 3514 end if; 3515 3516 -- Do switch expansion if needed 3517 3518 For_Each_Simple 3519 (Local_Config, 3520 Section => Section_Name.all, 3521 Switch => Parser.Switch_Character & Full_Switch (Parser), 3522 Parameter => Parameter (Parser)); 3523 3524 else 3525 if Current_Section = -1 then 3526 Current_Section := Local_Config.Sections'First; 3527 else 3528 Current_Section := Current_Section + 1; 3529 end if; 3530 3531 exit when Current_Section > Local_Config.Sections'Last; 3532 3533 Section_Name := Local_Config.Sections (Current_Section); 3534 Goto_Section (Section_Name.all, Parser); 3535 3536 Free (Getopt_Switches); 3537 Getopt_Switches := new String' 3538 (Get_Switches 3539 (Local_Config, Parser.Switch_Character, Section_Name.all)); 3540 end if; 3541 end loop; 3542 3543 Free (Getopt_Switches); 3544 3545 exception 3546 when Invalid_Switch => 3547 Free (Getopt_Switches); 3548 3549 -- Message inspired by "ls" on Unix 3550 3551 Put_Line (Standard_Error, 3552 Base_Name (Ada.Command_Line.Command_Name) 3553 & ": unrecognized option '" 3554 & Full_Switch (Parser) 3555 & "'"); 3556 Try_Help; 3557 3558 raise; 3559 3560 when others => 3561 Free (Getopt_Switches); 3562 raise; 3563 end Getopt; 3564 3565 ----------- 3566 -- Build -- 3567 ----------- 3568 3569 procedure Build 3570 (Line : in out Command_Line; 3571 Args : out GNAT.OS_Lib.Argument_List_Access; 3572 Expanded : Boolean := False; 3573 Switch_Char : Character := '-') 3574 is 3575 Iter : Command_Line_Iterator; 3576 Count : Natural := 0; 3577 3578 begin 3579 Start (Line, Iter, Expanded => Expanded); 3580 while Has_More (Iter) loop 3581 if Is_New_Section (Iter) then 3582 Count := Count + 1; 3583 end if; 3584 3585 Count := Count + 1; 3586 Next (Iter); 3587 end loop; 3588 3589 Args := new Argument_List (1 .. Count); 3590 Count := Args'First; 3591 3592 Start (Line, Iter, Expanded => Expanded); 3593 while Has_More (Iter) loop 3594 if Is_New_Section (Iter) then 3595 Args (Count) := new String'(Switch_Char & Current_Section (Iter)); 3596 Count := Count + 1; 3597 end if; 3598 3599 Args (Count) := new String'(Current_Switch (Iter) 3600 & Current_Separator (Iter) 3601 & Current_Parameter (Iter)); 3602 Count := Count + 1; 3603 Next (Iter); 3604 end loop; 3605 end Build; 3606 3607 -------------- 3608 -- Try_Help -- 3609 -------------- 3610 3611 -- Note: Any change to the message displayed should also be done in 3612 -- gnatbind.adb that does not use this interface. 3613 3614 procedure Try_Help is 3615 begin 3616 Put_Line 3617 (Standard_Error, 3618 "try """ & Base_Name (Ada.Command_Line.Command_Name, Suffix => ".exe") 3619 & " --help"" for more information."); 3620 end Try_Help; 3621 3622end GNAT.Command_Line; 3623