OSDN Git Service

Regenerate gcc/configure.
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-dirope.adb
index 4755584..294aa70 100644 (file)
@@ -6,9 +6,7 @@
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---                            $Revision$
---                                                                          --
---            Copyright (C) 1998-2001 Ada Core Technologies, Inc.           --
+--                     Copyright (C) 1998-2009, AdaCore                     --
 --                                                                          --
 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
@@ -18,8 +16,8 @@
 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
 -- for  more details.  You should have  received  a copy of the GNU General --
 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
--- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
--- MA 02111-1307, USA.                                                      --
+-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
+-- Boston, MA 02110-1301, USA.                                              --
 --                                                                          --
 -- As a special exception,  if other files  instantiate  generics from this --
 -- unit, or you link  this unit with other files  to produce an executable, --
 -- however invalidate  any other reasons why  the executable file  might be --
 -- covered by the  GNU Public License.                                      --
 --                                                                          --
--- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com).   --
+-- GNAT was originally developed  by the GNAT team at  New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc.      --
 --                                                                          --
 ------------------------------------------------------------------------------
 
 with Ada.Characters.Handling;
 with Ada.Strings.Fixed;
-with Ada.Strings.Maps;
-with Unchecked_Deallocation;
-with Unchecked_Conversion;
-with System;  use System;
+
+with Ada.Unchecked_Deallocation;
+with Ada.Unchecked_Conversion;
+
+with System;      use System;
+with System.CRTL; use System.CRTL;
 
 with GNAT.OS_Lib;
 
@@ -45,12 +46,15 @@ package body GNAT.Directory_Operations is
 
    use Ada;
 
-   type Dir_Type_Value is new System.Address;
-   --  This is the low-level address directory structure as returned by the C
-   --  opendir routine.
+   Filename_Max : constant Integer := 1024;
+   --  1024 is the value of FILENAME_MAX in stdio.h
 
    procedure Free is new
-     Unchecked_Deallocation (Dir_Type_Value, Dir_Type);
+     Ada.Unchecked_Deallocation (Dir_Type_Value, Dir_Type);
+
+   On_Windows : constant Boolean := GNAT.OS_Lib.Directory_Separator = '\';
+   --  An indication that we are on Windows. Used in Get_Current_Dir, to
+   --  deal with drive letters in the beginning of absolute paths.
 
    ---------------
    -- Base_Name --
@@ -58,8 +62,7 @@ package body GNAT.Directory_Operations is
 
    function Base_Name
      (Path   : Path_Name;
-      Suffix : String    := "")
-      return   String
+      Suffix : String := "") return String
    is
       function Get_File_Names_Case_Sensitive return Integer;
       pragma Import
@@ -71,8 +74,7 @@ package body GNAT.Directory_Operations is
 
       function Basename
         (Path   : Path_Name;
-         Suffix : String    := "")
-         return String;
+         Suffix : String := "") return String;
       --  This function does the job. The only difference between Basename
       --  and Base_Name (the parent function) is that the former is case
       --  sensitive, while the latter is not. Path and Suffix are adjusted
@@ -85,8 +87,7 @@ package body GNAT.Directory_Operations is
 
       function Basename
         (Path   : Path_Name;
-         Suffix : String    := "")
-         return   String
+         Suffix : String    := "") return String
       is
          Cut_Start : Natural :=
                        Strings.Fixed.Index
@@ -96,18 +97,13 @@ package body GNAT.Directory_Operations is
       begin
          --  Cut_Start point to the first basename character
 
-         if Cut_Start = 0 then
-            Cut_Start := Path'First;
+         Cut_Start := (if Cut_Start = 0 then Path'First else Cut_Start + 1);
 
-         else
-            Cut_Start := Cut_Start + 1;
-         end if;
-
-         --  Cut_End point to the last basename character.
+         --  Cut_End point to the last basename character
 
          Cut_End := Path'Last;
 
-         --  If basename ends with Suffix, adjust Cut_End.
+         --  If basename ends with Suffix, adjust Cut_End
 
          if Suffix /= ""
            and then Path (Path'Last - Suffix'Length + 1 .. Cut_End) = Suffix
@@ -121,11 +117,17 @@ package body GNAT.Directory_Operations is
                        Base_Name.Path (Cut_Start - Offset .. Cut_End - Offset);
             --  Here we use Base_Name.Path to keep the original casing
 
+            Has_Drive_Letter : constant Boolean :=
+                                 OS_Lib.Path_Separator /= ':';
+            --  If Path separator is not ':' then we are on a DOS based OS
+            --  where this character is used as a drive letter separator.
+
          begin
             if BN = "." or else BN = ".." then
                return "";
 
-            elsif BN'Length > 2
+            elsif Has_Drive_Letter
+              and then BN'Length > 2
               and then Characters.Handling.Is_Letter (BN (BN'First))
               and then BN (BN'First + 1) = ':'
             then
@@ -139,12 +141,15 @@ package body GNAT.Directory_Operations is
          end Check_For_Standard_Dirs;
       end Basename;
 
-   --  Start processing for Base_Name
+   --  Start of processing for Base_Name
 
    begin
+      if Path'Length <= Suffix'Length then
+         return Path;
+      end if;
+
       if Case_Sensitive_File_Name then
          return Basename (Path, Suffix);
-
       else
          return Basename
            (Characters.Handling.To_Lower (Path),
@@ -157,11 +162,7 @@ package body GNAT.Directory_Operations is
    ----------------
 
    procedure Change_Dir (Dir_Name : Dir_Name_Str) is
-      C_Dir_Name : String := Dir_Name & ASCII.NUL;
-
-      function chdir (Dir_Name : String) return Integer;
-      pragma Import (C, chdir, "chdir");
-
+      C_Dir_Name : constant String := Dir_Name & ASCII.NUL;
    begin
       if chdir (C_Dir_Name) /= 0 then
          raise Directory_Error;
@@ -173,18 +174,18 @@ package body GNAT.Directory_Operations is
    -----------
 
    procedure Close (Dir : in out Dir_Type) is
-
-      function closedir (Directory : System.Address) return Integer;
-      pragma Import (C, closedir, "closedir");
-
       Discard : Integer;
+      pragma Warnings (Off, Discard);
+
+      function closedir (directory : DIRs) return Integer;
+      pragma Import (C, closedir, "__gnat_closedir");
 
    begin
       if not Is_Open (Dir) then
          raise Directory_Error;
       end if;
 
-      Discard := closedir (System.Address (Dir.all));
+      Discard := closedir (DIRs (Dir.all));
       Free (Dir);
    end Close;
 
@@ -213,7 +214,12 @@ package body GNAT.Directory_Operations is
    -- Expand_Path --
    -----------------
 
-   function Expand_Path (Path : Path_Name) return String is
+   function Expand_Path
+     (Path : Path_Name;
+      Mode : Environment_Style := System_Default) return Path_Name
+   is
+      Environment_Variable_Char : Character;
+      pragma Import (C, Environment_Variable_Char, "__gnat_environment_char");
 
       Result      : OS_Lib.String_Access := new String (1 .. 200);
       Result_Last : Natural := 0;
@@ -225,6 +231,9 @@ package body GNAT.Directory_Operations is
       procedure Double_Result_Size;
       --  Reallocate Result, doubling its size
 
+      function Is_Var_Prefix (C : Character) return Boolean;
+      pragma Inline (Is_Var_Prefix);
+
       procedure Read (K : in out Positive);
       --  Update Result while reading current Path starting at position K. If
       --  a variable is found, call Var below.
@@ -263,46 +272,59 @@ package body GNAT.Directory_Operations is
 
       procedure Double_Result_Size is
          New_Result : constant OS_Lib.String_Access :=
-           new String (1 .. 2 * Result'Last);
-
+                        new String (1 .. 2 * Result'Last);
       begin
          New_Result (1 .. Result_Last) := Result (1 .. Result_Last);
          OS_Lib.Free (Result);
          Result := New_Result;
       end Double_Result_Size;
 
+      -------------------
+      -- Is_Var_Prefix --
+      -------------------
+
+      function Is_Var_Prefix (C : Character) return Boolean is
+      begin
+         return (C = Environment_Variable_Char and then Mode = System_Default)
+           or else
+             (C = '$' and then (Mode = UNIX or else Mode = Both))
+           or else
+             (C = '%' and then (Mode = DOS or else Mode = Both));
+      end Is_Var_Prefix;
+
       ----------
       -- Read --
       ----------
 
       procedure Read (K : in out Positive) is
+         P : Character;
+
       begin
          For_All_Characters : loop
-            if Path (K) = '$' then
+            if Is_Var_Prefix (Path (K)) then
+               P := Path (K);
 
                --  Could be a variable
 
                if K < Path'Last then
+                  if Path (K + 1) = P then
 
-                  if Path (K + 1) = '$' then
+                     --  Not a variable after all, this is a double $ or %,
+                     --  just insert one in the result string.
 
-                     --  Not a variable after all, this is a double $, just
-                     --  insert one in the result string.
-
-                     Append ('$');
+                     Append (P);
                      K := K + 1;
 
                   else
                      --  Let's parse the variable
 
-                     K := K + 1;
                      Var (K);
                   end if;
 
                else
-                  --  We have an ending $ sign
+                  --  We have an ending $ or % sign
 
-                  Append ('$');
+                  Append (P);
                end if;
 
             else
@@ -324,27 +346,41 @@ package body GNAT.Directory_Operations is
       ---------
 
       procedure Var (K : in out Positive) is
+         P : constant Character := Path (K);
+         T : Character;
          E : Positive;
 
       begin
-         if Path (K) = '{' then
+         K := K + 1;
 
-            --  Look for closing } (curly bracket).
+         if P = '%' or else Path (K) = '{' then
+
+            --  Set terminator character
+
+            if P = '%' then
+               T := '%';
+            else
+               T := '}';
+               K := K + 1;
+            end if;
+
+            --  Look for terminator character, k point to the first character
+            --  for the variable name.
 
             E := K;
 
             loop
                E := E + 1;
-               exit when Path (E) = '}' or else E = Path'Last;
+               exit when Path (E) = T or else E = Path'Last;
             end loop;
 
-            if Path (E) = '}' then
+            if Path (E) = T then
 
                --  OK found, translate with environment value
 
                declare
                   Env : OS_Lib.String_Access :=
-                          OS_Lib.Getenv (Path (K + 1 .. E - 1));
+                          OS_Lib.Getenv (Path (K .. E - 1));
 
                begin
                   Append (Env.all);
@@ -352,10 +388,15 @@ package body GNAT.Directory_Operations is
                end;
 
             else
-               --  No closing curly bracket, not a variable after all or a
+               --  No terminator character, not a variable after all or a
                --  syntax error, ignore it, insert string as-is.
 
-               Append ('$');
+               Append (P);       --  Add prefix character
+
+               if T = '}' then   --  If we were looking for curly bracket
+                  Append ('{');  --  terminator, add the curly bracket
+               end if;
+
                Append (Path (K .. E));
             end if;
 
@@ -365,24 +406,25 @@ package body GNAT.Directory_Operations is
 
             E := K;
 
-            --  Check that first chartacter is a letter
+            --  Check that first character is a letter
 
             if Characters.Handling.Is_Letter (Path (E)) then
                E := E + 1;
 
                Var_Name : loop
-                  exit Var_Name when E = Path'Last;
+                  exit Var_Name when E > Path'Last;
 
                   if Characters.Handling.Is_Letter (Path (E))
                     or else Characters.Handling.Is_Digit (Path (E))
                   then
                      E := E + 1;
                   else
-                     E := E - 1;
                      exit Var_Name;
                   end if;
                end loop Var_Name;
 
+               E := E - 1;
+
                declare
                   Env : OS_Lib.String_Access := OS_Lib.Getenv (Path (K .. E));
 
@@ -459,16 +501,63 @@ package body GNAT.Directory_Operations is
    end File_Name;
 
    ---------------------
+   -- Format_Pathname --
+   ---------------------
+
+   function Format_Pathname
+     (Path  : Path_Name;
+      Style : Path_Style := System_Default) return String
+   is
+      N_Path       : String   := Path;
+      K            : Positive := N_Path'First;
+      Prev_Dirsep  : Boolean  := False;
+
+   begin
+      if Dir_Separator = '\'
+        and then Path'Length > 1
+        and then Path (K .. K + 1) = "\\"
+      then
+         if Style = UNIX then
+            N_Path (K .. K + 1) := "//";
+         end if;
+
+         K := K + 2;
+      end if;
+
+      for J in K .. Path'Last loop
+         if Strings.Maps.Is_In (Path (J), Dir_Seps) then
+            if not Prev_Dirsep then
+               case Style is
+                  when UNIX           => N_Path (K) := '/';
+                  when DOS            => N_Path (K) := '\';
+                  when System_Default => N_Path (K) := Dir_Separator;
+               end case;
+
+               K := K + 1;
+            end if;
+
+            Prev_Dirsep := True;
+
+         else
+            N_Path (K) := Path (J);
+            K := K + 1;
+            Prev_Dirsep := False;
+         end if;
+      end loop;
+
+      return N_Path (N_Path'First .. K - 1);
+   end Format_Pathname;
+
+   ---------------------
    -- Get_Current_Dir --
    ---------------------
 
    Max_Path : Integer;
-   pragma Import (C, Max_Path, "max_path_len");
+   pragma Import (C, Max_Path, "__gnat_max_path_len");
 
    function Get_Current_Dir return Dir_Name_Str is
       Current_Dir : String (1 .. Max_Path + 1);
       Last        : Natural;
-
    begin
       Get_Current_Dir (Current_Dir, Last);
       return Current_Dir (1 .. Last);
@@ -486,13 +575,19 @@ package body GNAT.Directory_Operations is
    begin
       Local_Get_Current_Dir (Buffer'Address, Path_Len'Address);
 
-      if Dir'Length > Path_Len then
-         Last := Dir'First + Path_Len - 1;
-      else
-         Last := Dir'Last;
-      end if;
+      Last :=
+        (if Dir'Length > Path_Len then Dir'First + Path_Len - 1 else Dir'Last);
 
       Dir (Buffer'First .. Last) := Buffer (Buffer'First .. Last);
+
+      --  By default, the drive letter on Windows is in upper case
+
+      if On_Windows and then Last > Dir'First and then
+        Dir (Dir'First + 1) = ':'
+      then
+         Dir (Dir'First) :=
+           Ada.Characters.Handling.To_Upper (Dir (Dir'First));
+      end if;
    end Get_Current_Dir;
 
    -------------
@@ -510,7 +605,7 @@ package body GNAT.Directory_Operations is
    --------------
 
    procedure Make_Dir (Dir_Name : Dir_Name_Str) is
-      C_Dir_Name : String := Dir_Name & ASCII.NUL;
+      C_Dir_Name : constant String := Dir_Name & ASCII.NUL;
 
       function mkdir (Dir_Name : String) return Integer;
       pragma Import (C, mkdir, "__gnat_mkdir");
@@ -521,46 +616,6 @@ package body GNAT.Directory_Operations is
       end if;
    end Make_Dir;
 
-   ------------------------
-   -- Normalize_Pathname --
-   ------------------------
-
-   function Normalize_Pathname
-     (Path  : Path_Name;
-      Style : Path_Style := System_Default)
-      return  String
-   is
-      N_Path      : String := Path;
-      K           : Positive := N_Path'First;
-      Prev_Dirsep : Boolean := False;
-
-   begin
-      for J in Path'Range loop
-
-         if Strings.Maps.Is_In (Path (J), Dir_Seps) then
-            if not Prev_Dirsep then
-
-               case Style is
-                  when UNIX           => N_Path (K) := '/';
-                  when DOS            => N_Path (K) := '\';
-                  when System_Default => N_Path (K) := Dir_Separator;
-               end case;
-
-               K := K + 1;
-            end if;
-
-            Prev_Dirsep := True;
-
-         else
-            N_Path (K) := Path (J);
-            K := K + 1;
-            Prev_Dirsep := False;
-         end if;
-      end loop;
-
-      return N_Path (N_Path'First .. K - 1);
-   end Normalize_Pathname;
-
    ----------
    -- Open --
    ----------
@@ -569,15 +624,13 @@ package body GNAT.Directory_Operations is
      (Dir      : out Dir_Type;
       Dir_Name : Dir_Name_Str)
    is
-      C_File_Name : String := Dir_Name & ASCII.NUL;
+      function opendir (file_name : String) return DIRs;
+      pragma Import (C, opendir, "__gnat_opendir");
 
-      function opendir
-        (File_Name : String)
-         return      Dir_Type_Value;
-      pragma Import (C, opendir, "opendir");
+      C_File_Name : constant String := Dir_Name & ASCII.NUL;
 
    begin
-      Dir := new Dir_Type_Value'(opendir (C_File_Name));
+      Dir := new Dir_Type_Value'(Dir_Type_Value (opendir (C_File_Name)));
 
       if not Is_Open (Dir) then
          Free (Dir);
@@ -591,56 +644,52 @@ package body GNAT.Directory_Operations is
    ----------
 
    procedure Read
-     (Dir  : in out Dir_Type;
+     (Dir  : Dir_Type;
       Str  : out String;
       Last : out Natural)
    is
       Filename_Addr : Address;
-      Filename_Len  : Integer;
+      Filename_Len  : aliased Integer;
 
-      Buffer : array (0 .. 1024) of Character;
-      --  1024 is the value of FILENAME_MAX in stdio.h
+      Buffer : array (0 .. Filename_Max + 12) of Character;
+      --  12 is the size of the dirent structure (see dirent.h), without the
+      --  field for the filename.
 
       function readdir_gnat
         (Directory : System.Address;
-         Buffer    : System.Address)
-         return      System.Address;
+         Buffer    : System.Address;
+         Last      : not null access Integer) return System.Address;
       pragma Import (C, readdir_gnat, "__gnat_readdir");
 
-      function strlen (S : Address) return Integer;
-      pragma Import (C, strlen, "strlen");
-
    begin
       if not Is_Open (Dir) then
          raise Directory_Error;
       end if;
 
       Filename_Addr :=
-        readdir_gnat (System.Address (Dir.all), Buffer'Address);
+        readdir_gnat
+          (System.Address (Dir.all), Buffer'Address, Filename_Len'Access);
 
       if Filename_Addr = System.Null_Address then
          Last := 0;
          return;
       end if;
 
-      Filename_Len  := strlen (Filename_Addr);
-
-      if Str'Length > Filename_Len then
-         Last := Str'First + Filename_Len - 1;
-      else
-         Last := Str'Last;
-      end if;
+      Last :=
+        (if Str'Length > Filename_Len then Str'First + Filename_Len - 1
+         else Str'Last);
 
       declare
          subtype Path_String is String (1 .. Filename_Len);
          type    Path_String_Access is access Path_String;
 
          function Address_To_Access is new
-           Unchecked_Conversion
+           Ada.Unchecked_Conversion
              (Source => Address,
               Target => Path_String_Access);
 
-         Path_Access : Path_String_Access := Address_To_Access (Filename_Addr);
+         Path_Access : constant Path_String_Access :=
+                         Address_To_Access (Filename_Addr);
 
       begin
          for J in Str'First .. Last loop
@@ -654,11 +703,9 @@ package body GNAT.Directory_Operations is
    -------------------------
 
    function Read_Is_Thread_Safe return Boolean is
-
       function readdir_is_thread_safe return Integer;
       pragma Import
         (C, readdir_is_thread_safe, "__gnat_readdir_is_thread_safe");
-
    begin
       return (readdir_is_thread_safe /= 0);
    end Read_Is_Thread_Safe;
@@ -667,14 +714,74 @@ package body GNAT.Directory_Operations is
    -- Remove_Dir --
    ----------------
 
-   procedure Remove_Dir (Dir_Name : Dir_Name_Str) is
-      C_Dir_Name : String := Dir_Name & ASCII.NUL;
-
-      procedure rmdir (Dir_Name : String);
-      pragma Import (C, rmdir, "rmdir");
+   procedure Remove_Dir
+     (Dir_Name  : Dir_Name_Str;
+      Recursive : Boolean := False)
+   is
+      C_Dir_Name  : constant String := Dir_Name & ASCII.NUL;
+      Current_Dir : constant Dir_Name_Str := Get_Current_Dir;
+      Last        : Integer;
+      Str         : String (1 .. Filename_Max);
+      Success     : Boolean;
+      Working_Dir : Dir_Type;
 
    begin
-      rmdir (C_Dir_Name);
+      --  Remove the directory only if it is empty
+
+      if not Recursive then
+         if rmdir (C_Dir_Name) /= 0 then
+            raise Directory_Error;
+         end if;
+
+      --  Remove directory and all files and directories that it may contain
+
+      else
+         --  Substantial comments needed. See RH for revision 1.50 ???
+
+         begin
+            Change_Dir (Dir_Name);
+            Open (Working_Dir, ".");
+
+            loop
+               Read (Working_Dir, Str, Last);
+               exit when Last = 0;
+
+               if GNAT.OS_Lib.Is_Directory (Str (1 .. Last)) then
+                  if Str (1 .. Last) /= "."
+                       and then
+                     Str (1 .. Last) /= ".."
+                  then
+                     Remove_Dir (Str (1 .. Last), True);
+                  end if;
+
+               else
+                  GNAT.OS_Lib.Delete_File (Str (1 .. Last), Success);
+
+                  if not Success then
+                     Change_Dir (Current_Dir);
+                     raise Directory_Error;
+                  end if;
+               end if;
+            end loop;
+
+            Change_Dir (Current_Dir);
+            Close (Working_Dir);
+            Remove_Dir (Dir_Name);
+
+         exception
+            when others =>
+
+               --  An exception occurred. We must make sure the current working
+               --  directory is unchanged.
+
+               Change_Dir (Current_Dir);
+
+               --  What if the Change_Dir raises an exception itself, shouldn't
+               --  that be protected? ???
+
+               raise;
+         end;
+      end if;
    end Remove_Dir;
 
 end GNAT.Directory_Operations;