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-2003 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 2, or (at your option) any later ver- -- 14-- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- 15-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- 16-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- 17-- for more details. You should have received a copy of the GNU General -- 18-- Public License distributed with GNAT; see file COPYING. If not, write -- 19-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- 20-- MA 02111-1307, USA. -- 21-- -- 22-- As a special exception, if other files instantiate generics from this -- 23-- unit, or you link this unit with other files to produce an executable, -- 24-- this unit does not by itself cause the resulting executable to be -- 25-- covered by the GNU General Public License. This exception does not -- 26-- however invalidate any other reasons why the executable file might be -- 27-- covered by the GNU Public License. -- 28-- -- 29-- GNAT was originally developed by the GNAT team at New York University. -- 30-- Extensive contributions were provided by Ada Core Technologies Inc. -- 31-- -- 32------------------------------------------------------------------------------ 33 34with Ada.Command_Line; 35with GNAT.OS_Lib; use GNAT.OS_Lib; 36 37package body GNAT.Command_Line is 38 39 package CL renames Ada.Command_Line; 40 41 type Section_Number is new Natural range 0 .. 65534; 42 for Section_Number'Size use 16; 43 44 type Parameter_Type is record 45 Arg_Num : Positive; 46 First : Positive; 47 Last : Positive; 48 end record; 49 The_Parameter : Parameter_Type; 50 The_Switch : Parameter_Type; 51 -- This type and this variable are provided to store the current switch 52 -- and parameter 53 54 type Is_Switch_Type is array (1 .. CL.Argument_Count) of Boolean; 55 pragma Pack (Is_Switch_Type); 56 57 Is_Switch : Is_Switch_Type := (others => False); 58 -- Indicates wich arguments on the command line are considered not be 59 -- switches or parameters to switches (this leaves e.g. the filenames...) 60 61 type Section_Type is array (1 .. CL.Argument_Count + 1) of Section_Number; 62 pragma Pack (Section_Type); 63 Section : Section_Type := (others => 1); 64 -- Contains the number of the section associated with the current 65 -- switch. If this number is 0, then it is a section delimiter, which 66 -- is never returns by GetOpt. 67 -- The last element of this array is set to 0 to avoid the need to test for 68 -- if we have reached the end of the command line in loops. 69 70 Current_Argument : Natural := 1; 71 -- Number of the current argument parsed on the command line 72 73 Current_Index : Natural := 1; 74 -- Index in the current argument of the character to be processed 75 76 Current_Section : Section_Number := 1; 77 78 Expansion_It : aliased Expansion_Iterator; 79 -- When Get_Argument is expanding a file name, this is the iterator used 80 81 In_Expansion : Boolean := False; 82 -- True if we are expanding a file 83 84 Switch_Character : Character := '-'; 85 -- The character at the beginning of the command line arguments, 86 -- indicating the beginning of a switch 87 88 Stop_At_First : Boolean := False; 89 -- If it is True then Getopt stops at the first non-switch argument 90 91 procedure Set_Parameter 92 (Variable : out Parameter_Type; 93 Arg_Num : Positive; 94 First : Positive; 95 Last : Positive); 96 pragma Inline (Set_Parameter); 97 -- Set the parameter that will be returned by Parameter below 98 99 function Goto_Next_Argument_In_Section return Boolean; 100 -- Go to the next argument on the command line. If we are at the end 101 -- of the current section, we want to make sure there is no other 102 -- identical section on the command line (there might be multiple 103 -- instances of -largs). Returns True iff there is another argument. 104 105 function Get_File_Names_Case_Sensitive return Integer; 106 pragma Import (C, Get_File_Names_Case_Sensitive, 107 "__gnat_get_file_names_case_sensitive"); 108 File_Names_Case_Sensitive : constant Boolean := 109 Get_File_Names_Case_Sensitive /= 0; 110 111 procedure Canonical_Case_File_Name (S : in out String); 112 -- Given a file name, converts it to canonical case form. For systems 113 -- where file names are case sensitive, this procedure has no effect. 114 -- If file names are not case sensitive (i.e. for example if you have 115 -- the file "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then 116 -- this call converts the given string to canonical all lower case form, 117 -- so that two file names compare equal if they refer to the same file. 118 119 ------------------------------ 120 -- Canonical_Case_File_Name -- 121 ------------------------------ 122 123 procedure Canonical_Case_File_Name (S : in out String) is 124 begin 125 if not File_Names_Case_Sensitive then 126 for J in S'Range loop 127 if S (J) in 'A' .. 'Z' then 128 S (J) := Character'Val ( 129 Character'Pos (S (J)) + 130 Character'Pos ('a') - 131 Character'Pos ('A')); 132 end if; 133 end loop; 134 end if; 135 end Canonical_Case_File_Name; 136 137 --------------- 138 -- Expansion -- 139 --------------- 140 141 function Expansion (Iterator : Expansion_Iterator) return String is 142 use GNAT.Directory_Operations; 143 type Pointer is access all Expansion_Iterator; 144 145 S : String (1 .. 1024); 146 Last : Natural; 147 It : Pointer := Iterator'Unrestricted_Access; 148 149 Current : Depth := It.Current_Depth; 150 NL : Positive; 151 152 begin 153 -- It is assumed that a directory is opened at the current level; 154 -- otherwise, GNAT.Directory_Operations.Directory_Error will be raised 155 -- at the first call to Read. 156 157 loop 158 Read (It.Levels (Current).Dir, S, Last); 159 160 -- If we have exhausted the directory, close it and go back one level 161 162 if Last = 0 then 163 Close (It.Levels (Current).Dir); 164 165 -- If we are at level 1, we are finished; return an empty string. 166 167 if Current = 1 then 168 return String'(1 .. 0 => ' '); 169 else 170 -- Otherwise, continue with the directory at the previous level 171 172 Current := Current - 1; 173 It.Current_Depth := Current; 174 end if; 175 176 -- If this is a directory, that is neither "." or "..", attempt to 177 -- go to the next level. 178 179 elsif Is_Directory 180 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last)) 181 and then S (1 .. Last) /= "." 182 and then S (1 .. Last) /= ".." 183 then 184 -- We can go to the next level only if we have not reached the 185 -- maximum depth, 186 187 if Current < It.Maximum_Depth then 188 NL := It.Levels (Current).Name_Last; 189 190 -- And if relative path of this new directory is not too long 191 192 if NL + Last + 1 < Max_Path_Length then 193 Current := Current + 1; 194 It.Current_Depth := Current; 195 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last); 196 NL := NL + Last + 1; 197 It.Dir_Name (NL) := Directory_Separator; 198 It.Levels (Current).Name_Last := NL; 199 Canonical_Case_File_Name (It.Dir_Name (1 .. NL)); 200 201 -- Open the new directory, and read from it 202 203 GNAT.Directory_Operations.Open 204 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL)); 205 end if; 206 end if; 207 208 -- If not a directory, check the relative path against the pattern 209 210 else 211 declare 212 Name : String := 213 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) & 214 S (1 .. Last); 215 begin 216 Canonical_Case_File_Name (Name); 217 218 -- If it matches, return the relative path 219 220 if GNAT.Regexp.Match (Name, Iterator.Regexp) then 221 return Name; 222 end if; 223 end; 224 end if; 225 226 end loop; 227 228 return String'(1 .. 0 => ' '); 229 end Expansion; 230 231 ----------------- 232 -- Full_Switch -- 233 ----------------- 234 235 function Full_Switch return String is 236 begin 237 return CL.Argument (The_Switch.Arg_Num) 238 (The_Switch.First .. The_Switch.Last); 239 end Full_Switch; 240 241 ------------------ 242 -- Get_Argument -- 243 ------------------ 244 245 function Get_Argument (Do_Expansion : Boolean := False) return String is 246 Total : constant Natural := CL.Argument_Count; 247 248 begin 249 if In_Expansion then 250 declare 251 S : constant String := Expansion (Expansion_It); 252 253 begin 254 if S'Length /= 0 then 255 return S; 256 else 257 In_Expansion := False; 258 end if; 259 end; 260 end if; 261 262 if Current_Argument > Total then 263 264 -- If this is the first time this function is called 265 266 if Current_Index = 1 then 267 Current_Argument := 1; 268 while Current_Argument <= CL.Argument_Count 269 and then Section (Current_Argument) /= Current_Section 270 loop 271 Current_Argument := Current_Argument + 1; 272 end loop; 273 else 274 return String'(1 .. 0 => ' '); 275 end if; 276 277 elsif Section (Current_Argument) = 0 then 278 while Current_Argument <= CL.Argument_Count 279 and then Section (Current_Argument) /= Current_Section 280 loop 281 Current_Argument := Current_Argument + 1; 282 end loop; 283 end if; 284 285 Current_Index := 2; 286 287 while Current_Argument <= Total 288 and then Is_Switch (Current_Argument) 289 loop 290 Current_Argument := Current_Argument + 1; 291 end loop; 292 293 if Current_Argument > Total then 294 return String'(1 .. 0 => ' '); 295 end if; 296 297 if Section (Current_Argument) = 0 then 298 return Get_Argument (Do_Expansion); 299 end if; 300 301 Current_Argument := Current_Argument + 1; 302 303 -- Could it be a file name with wild cards to expand? 304 305 if Do_Expansion then 306 declare 307 Arg : String renames CL.Argument (Current_Argument - 1); 308 Index : Positive := Arg'First; 309 310 begin 311 while Index <= Arg'Last loop 312 313 if Arg (Index) = '*' 314 or else Arg (Index) = '?' 315 or else Arg (Index) = '[' 316 then 317 In_Expansion := True; 318 Start_Expansion (Expansion_It, Arg); 319 return Get_Argument (Do_Expansion); 320 end if; 321 322 Index := Index + 1; 323 end loop; 324 end; 325 end if; 326 327 return CL.Argument (Current_Argument - 1); 328 end Get_Argument; 329 330 ------------ 331 -- Getopt -- 332 ------------ 333 334 function Getopt 335 (Switches : String; 336 Concatenate : Boolean := True) return Character 337 is 338 Dummy : Boolean; 339 pragma Unreferenced (Dummy); 340 341 begin 342 -- If we have finished parsing the current command line item (there 343 -- might be multiple switches in a single item), then go to the next 344 -- element 345 346 if Current_Argument > CL.Argument_Count 347 or else (Current_Index > CL.Argument (Current_Argument)'Last 348 and then not Goto_Next_Argument_In_Section) 349 then 350 return ASCII.NUL; 351 end if; 352 353 -- If we are on a new item, test if this might be a switch 354 355 if Current_Index = 1 then 356 if CL.Argument (Current_Argument)(1) /= Switch_Character then 357 if Switches (Switches'First) = '*' then 358 Set_Parameter (The_Switch, 359 Arg_Num => Current_Argument, 360 First => 1, 361 Last => CL.Argument (Current_Argument)'Last); 362 Is_Switch (Current_Argument) := True; 363 Dummy := Goto_Next_Argument_In_Section; 364 return '*'; 365 end if; 366 367 if Stop_At_First then 368 Current_Argument := Positive'Last; 369 return ASCII.NUL; 370 371 elsif not Goto_Next_Argument_In_Section then 372 return ASCII.NUL; 373 374 else 375 return Getopt (Switches); 376 end if; 377 end if; 378 379 Current_Index := 2; 380 Is_Switch (Current_Argument) := True; 381 end if; 382 383 declare 384 Arg : String renames CL.Argument (Current_Argument); 385 Index_Switches : Natural := 0; 386 Max_Length : Natural := 0; 387 Index : Natural := Switches'First; 388 Length : Natural := 1; 389 End_Index : Natural; 390 391 begin 392 while Index <= Switches'Last loop 393 394 -- Search the length of the parameter at this position in Switches 395 396 Length := Index; 397 while Length <= Switches'Last 398 and then Switches (Length) /= ' ' 399 loop 400 Length := Length + 1; 401 end loop; 402 403 if (Switches (Length - 1) = ':' or else 404 Switches (Length - 1) = '=' or else 405 Switches (Length - 1) = '?' or else 406 Switches (Length - 1) = '!') 407 and then Length > Index + 1 408 then 409 Length := Length - 1; 410 end if; 411 412 -- If it is the one we searched, it may be a candidate 413 414 if Current_Index + Length - 1 - Index <= Arg'Last 415 and then 416 Switches (Index .. Length - 1) = 417 Arg (Current_Index .. Current_Index + Length - 1 - Index) 418 and then Length - Index > Max_Length 419 then 420 Index_Switches := Index; 421 Max_Length := Length - Index; 422 end if; 423 424 -- Look for the next switch in Switches 425 426 while Index <= Switches'Last 427 and then Switches (Index) /= ' ' loop 428 Index := Index + 1; 429 end loop; 430 431 Index := Index + 1; 432 end loop; 433 434 End_Index := Current_Index + Max_Length - 1; 435 436 -- If switch is not accepted, skip it, unless we had '*' in Switches 437 438 if Index_Switches = 0 then 439 if Switches (Switches'First) = '*' then 440 Set_Parameter (The_Switch, 441 Arg_Num => Current_Argument, 442 First => 1, 443 Last => CL.Argument (Current_Argument)'Last); 444 Is_Switch (Current_Argument) := True; 445 Dummy := Goto_Next_Argument_In_Section; 446 return '*'; 447 end if; 448 449 -- Depending on the value of Concatenate, the full switch is 450 -- a single character (True) or the rest of the argument (False). 451 452 if Concatenate then 453 End_Index := Current_Index; 454 else 455 End_Index := Arg'Last; 456 end if; 457 458 Set_Parameter (The_Switch, 459 Arg_Num => Current_Argument, 460 First => Current_Index, 461 Last => End_Index); 462 Current_Index := End_Index + 1; 463 raise Invalid_Switch; 464 end if; 465 466 Set_Parameter (The_Switch, 467 Arg_Num => Current_Argument, 468 First => Current_Index, 469 Last => End_Index); 470 471 -- Case of switch needs an argument 472 473 if Index_Switches + Max_Length <= Switches'Last then 474 475 case Switches (Index_Switches + Max_Length) is 476 477 when ':' => 478 479 if End_Index < Arg'Last then 480 Set_Parameter (The_Parameter, 481 Arg_Num => Current_Argument, 482 First => End_Index + 1, 483 Last => Arg'Last); 484 Dummy := Goto_Next_Argument_In_Section; 485 486 elsif Section (Current_Argument + 1) /= 0 then 487 Set_Parameter 488 (The_Parameter, 489 Arg_Num => Current_Argument + 1, 490 First => 1, 491 Last => CL.Argument (Current_Argument + 1)'Last); 492 Current_Argument := Current_Argument + 1; 493 Is_Switch (Current_Argument) := True; 494 Dummy := Goto_Next_Argument_In_Section; 495 496 else 497 Current_Index := End_Index + 1; 498 raise Invalid_Parameter; 499 end if; 500 501 when '=' => 502 503 -- If the switch is of the form <switch>=xxx 504 505 if End_Index < Arg'Last then 506 507 if Arg (End_Index + 1) = '=' 508 and then End_Index + 1 < Arg'Last 509 then 510 Set_Parameter (The_Parameter, 511 Arg_Num => Current_Argument, 512 First => End_Index + 2, 513 Last => Arg'Last); 514 Dummy := Goto_Next_Argument_In_Section; 515 516 else 517 Current_Index := End_Index + 1; 518 raise Invalid_Parameter; 519 end if; 520 521 -- If the switch is of the form <switch> xxx 522 523 elsif Section (Current_Argument + 1) /= 0 then 524 Set_Parameter 525 (The_Parameter, 526 Arg_Num => Current_Argument + 1, 527 First => 1, 528 Last => CL.Argument (Current_Argument + 1)'Last); 529 Current_Argument := Current_Argument + 1; 530 Is_Switch (Current_Argument) := True; 531 Dummy := Goto_Next_Argument_In_Section; 532 533 else 534 Current_Index := End_Index + 1; 535 raise Invalid_Parameter; 536 end if; 537 538 when '!' => 539 540 if End_Index < Arg'Last then 541 Set_Parameter (The_Parameter, 542 Arg_Num => Current_Argument, 543 First => End_Index + 1, 544 Last => Arg'Last); 545 Dummy := Goto_Next_Argument_In_Section; 546 547 else 548 Current_Index := End_Index + 1; 549 raise Invalid_Parameter; 550 end if; 551 552 when '?' => 553 554 if End_Index < Arg'Last then 555 Set_Parameter (The_Parameter, 556 Arg_Num => Current_Argument, 557 First => End_Index + 1, 558 Last => Arg'Last); 559 560 else 561 Set_Parameter (The_Parameter, 562 Arg_Num => Current_Argument, 563 First => 2, 564 Last => 1); 565 end if; 566 Dummy := Goto_Next_Argument_In_Section; 567 568 when others => 569 if Concatenate or else End_Index = Arg'Last then 570 Current_Index := End_Index + 1; 571 572 else 573 -- If Concatenate is False and the full argument is not 574 -- recognized as a switch, this is an invalid switch. 575 576 Set_Parameter (The_Switch, 577 Arg_Num => Current_Argument, 578 First => Current_Index, 579 Last => Arg'Last); 580 Current_Index := Arg'Last + 1; 581 raise Invalid_Switch; 582 end if; 583 end case; 584 585 elsif Concatenate or else End_Index = Arg'Last then 586 Current_Index := End_Index + 1; 587 588 else 589 -- If Concatenate is False and the full argument is not 590 -- recognized as a switch, this is an invalid switch. 591 592 Set_Parameter (The_Switch, 593 Arg_Num => Current_Argument, 594 First => Current_Index, 595 Last => Arg'Last); 596 Current_Index := Arg'Last + 1; 597 raise Invalid_Switch; 598 end if; 599 600 return Switches (Index_Switches); 601 end; 602 end Getopt; 603 604 ----------------------------------- 605 -- Goto_Next_Argument_In_Section -- 606 ----------------------------------- 607 608 function Goto_Next_Argument_In_Section return Boolean is 609 begin 610 Current_Index := 1; 611 Current_Argument := Current_Argument + 1; 612 613 if Section (Current_Argument) = 0 then 614 loop 615 if Current_Argument > CL.Argument_Count then 616 return False; 617 end if; 618 619 Current_Argument := Current_Argument + 1; 620 exit when Section (Current_Argument) = Current_Section; 621 end loop; 622 end if; 623 return True; 624 end Goto_Next_Argument_In_Section; 625 626 ------------------ 627 -- Goto_Section -- 628 ------------------ 629 630 procedure Goto_Section (Name : String := "") is 631 Index : Integer := 1; 632 633 begin 634 In_Expansion := False; 635 636 if Name = "" then 637 Current_Argument := 1; 638 Current_Index := 1; 639 Current_Section := 1; 640 return; 641 end if; 642 643 while Index <= CL.Argument_Count loop 644 645 if Section (Index) = 0 646 and then CL.Argument (Index) = Switch_Character & Name 647 then 648 Current_Argument := Index + 1; 649 Current_Index := 1; 650 651 if Current_Argument <= CL.Argument_Count then 652 Current_Section := Section (Current_Argument); 653 end if; 654 return; 655 end if; 656 657 Index := Index + 1; 658 end loop; 659 660 Current_Argument := Positive'Last; 661 Current_Index := 2; -- so that Get_Argument returns nothing 662 end Goto_Section; 663 664 ---------------------------- 665 -- Initialize_Option_Scan -- 666 ---------------------------- 667 668 procedure Initialize_Option_Scan 669 (Switch_Char : Character := '-'; 670 Stop_At_First_Non_Switch : Boolean := False; 671 Section_Delimiters : String := "") 672 is 673 Section_Num : Section_Number := 1; 674 Section_Index : Integer := Section_Delimiters'First; 675 Last : Integer; 676 Delimiter_Found : Boolean; 677 678 begin 679 Current_Argument := 0; 680 Current_Index := 0; 681 In_Expansion := False; 682 Switch_Character := Switch_Char; 683 Stop_At_First := Stop_At_First_Non_Switch; 684 685 -- If we are using sections, we have to preprocess the command line 686 -- to delimit them. A section can be repeated, so we just give each 687 -- item on the command line a section number 688 689 while Section_Index <= Section_Delimiters'Last loop 690 691 Last := Section_Index; 692 while Last <= Section_Delimiters'Last 693 and then Section_Delimiters (Last) /= ' ' 694 loop 695 Last := Last + 1; 696 end loop; 697 698 Delimiter_Found := False; 699 Section_Num := Section_Num + 1; 700 701 for Index in 1 .. CL.Argument_Count loop 702 if CL.Argument (Index)(1) = Switch_Character 703 and then 704 CL.Argument (Index) = Switch_Character & 705 Section_Delimiters 706 (Section_Index .. Last - 1) 707 then 708 Section (Index) := 0; 709 Delimiter_Found := True; 710 711 elsif Section (Index) = 0 then 712 Delimiter_Found := False; 713 714 elsif Delimiter_Found then 715 Section (Index) := Section_Num; 716 end if; 717 end loop; 718 719 Section_Index := Last + 1; 720 while Section_Index <= Section_Delimiters'Last 721 and then Section_Delimiters (Section_Index) = ' ' 722 loop 723 Section_Index := Section_Index + 1; 724 end loop; 725 end loop; 726 727 Delimiter_Found := Goto_Next_Argument_In_Section; 728 end Initialize_Option_Scan; 729 730 --------------- 731 -- Parameter -- 732 --------------- 733 734 function Parameter return String is 735 begin 736 if The_Parameter.First > The_Parameter.Last then 737 return String'(1 .. 0 => ' '); 738 else 739 return CL.Argument (The_Parameter.Arg_Num) 740 (The_Parameter.First .. The_Parameter.Last); 741 end if; 742 end Parameter; 743 744 ------------------- 745 -- Set_Parameter -- 746 ------------------- 747 748 procedure Set_Parameter 749 (Variable : out Parameter_Type; 750 Arg_Num : Positive; 751 First : Positive; 752 Last : Positive) 753 is 754 begin 755 Variable.Arg_Num := Arg_Num; 756 Variable.First := First; 757 Variable.Last := Last; 758 end Set_Parameter; 759 760 --------------------- 761 -- Start_Expansion -- 762 --------------------- 763 764 procedure Start_Expansion 765 (Iterator : out Expansion_Iterator; 766 Pattern : String; 767 Directory : String := ""; 768 Basic_Regexp : Boolean := True) 769 is 770 Directory_Separator : Character; 771 pragma Import (C, Directory_Separator, "__gnat_dir_separator"); 772 First : Positive := Pattern'First; 773 774 Pat : String := Pattern; 775 776 begin 777 Canonical_Case_File_Name (Pat); 778 Iterator.Current_Depth := 1; 779 780 -- If Directory is unspecified, use the current directory ("./" or ".\") 781 782 if Directory = "" then 783 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator; 784 Iterator.Start := 3; 785 786 else 787 Iterator.Dir_Name (1 .. Directory'Length) := Directory; 788 Iterator.Start := Directory'Length + 1; 789 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length)); 790 791 -- Make sure that the last character is a directory separator 792 793 if Directory (Directory'Last) /= Directory_Separator then 794 Iterator.Dir_Name (Iterator.Start) := Directory_Separator; 795 Iterator.Start := Iterator.Start + 1; 796 end if; 797 end if; 798 799 Iterator.Levels (1).Name_Last := Iterator.Start - 1; 800 801 -- Open the initial Directory, at depth 1 802 803 GNAT.Directory_Operations.Open 804 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1)); 805 806 -- If in the current directory and the pattern starts with "./" or ".\", 807 -- drop the "./" or ".\" from the pattern. 808 809 if Directory = "" and then Pat'Length > 2 810 and then Pat (Pat'First) = '.' 811 and then Pat (Pat'First + 1) = Directory_Separator 812 then 813 First := Pat'First + 2; 814 end if; 815 816 Iterator.Regexp := 817 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True); 818 819 Iterator.Maximum_Depth := 1; 820 821 -- Maximum_Depth is equal to 1 plus the number of directory separators 822 -- in the pattern. 823 824 for Index in First .. Pat'Last loop 825 if Pat (Index) = Directory_Separator then 826 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1; 827 exit when Iterator.Maximum_Depth = Max_Depth; 828 end if; 829 end loop; 830 831 end Start_Expansion; 832 833begin 834 Section (CL.Argument_Count + 1) := 0; 835end GNAT.Command_Line; 836