1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . D I R E C T O R I E S --
9 -- Copyright (C) 2004-2007, Free Software Foundation, Inc. --
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, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
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. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Ada.Calendar; use Ada.Calendar;
35 with Ada.Calendar.Formatting; use Ada.Calendar.Formatting;
36 with Ada.Directories.Validity; use Ada.Directories.Validity;
37 with Ada.Strings.Maps;
38 with Ada.Strings.Fixed;
39 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
40 with Ada.Unchecked_Conversion;
41 with Ada.Unchecked_Deallocation;
42 with Ada.Characters.Handling; use Ada.Characters.Handling;
44 with System.CRTL; use System.CRTL;
45 with System.OS_Lib; use System.OS_Lib;
46 with System.Regexp; use System.Regexp;
50 package body Ada.Directories is
52 Filename_Max : constant Integer := 1024;
53 -- 1024 is the value of FILENAME_MAX in stdio.h
55 type Dir_Type_Value is new System.Address;
56 -- This is the low-level address directory structure as returned by the C
59 No_Dir : constant Dir_Type_Value := Dir_Type_Value (System.Null_Address);
61 Dir_Separator : constant Character;
62 pragma Import (C, Dir_Separator, "__gnat_dir_separator");
63 -- Running system default directory separator
65 Dir_Seps : constant Ada.Strings.Maps.Character_Set :=
66 Ada.Strings.Maps.To_Set ("/\");
67 -- UNIX and DOS style directory separators
70 pragma Import (C, Max_Path, "__gnat_max_path_len");
71 -- The maximum length of a path
73 type Search_Data is record
74 Is_Valid : Boolean := False;
75 Name : Ada.Strings.Unbounded.Unbounded_String;
78 Dir : Dir_Type_Value := No_Dir;
79 Entry_Fetched : Boolean := False;
80 Dir_Entry : Directory_Entry_Type;
82 -- The current state of a search
84 Empty_String : constant String := (1 .. 0 => ASCII.NUL);
85 -- Empty string, returned by function Extension when there is no extension
87 procedure Free is new Ada.Unchecked_Deallocation (Search_Data, Search_Ptr);
89 procedure Close (Dir : Dir_Type_Value);
91 function File_Exists (Name : String) return Boolean;
92 -- Returns True if the named file exists
94 procedure Fetch_Next_Entry (Search : Search_Type);
95 -- Get the next entry in a directory, setting Entry_Fetched if successful
96 -- or resetting Is_Valid if not.
98 procedure To_Lower_If_Case_Insensitive (S : in out String);
99 -- Put S in lower case if file and path names are case-insensitive
105 function Base_Name (Name : String) return String is
106 Simple : String := Simple_Name (Name);
107 -- Simple'First is guaranteed to be 1
110 To_Lower_If_Case_Insensitive (Simple);
112 -- Look for the last dot in the file name and return the part of the
113 -- file name preceding this last dot. If the first dot is the first
114 -- character of the file name, the base name is the empty string.
116 for Pos in reverse Simple'Range loop
117 if Simple (Pos) = '.' then
118 return Simple (1 .. Pos - 1);
122 -- If there is no dot, return the complete file name
131 procedure Close (Dir : Dir_Type_Value) is
133 pragma Warnings (Off, Discard);
135 function closedir (directory : DIRs) return Integer;
136 pragma Import (C, closedir, "__gnat_closedir");
139 Discard := closedir (DIRs (Dir));
147 (Containing_Directory : String := "";
149 Extension : String := "") return String
151 Result : String (1 .. Containing_Directory'Length +
152 Name'Length + Extension'Length + 2);
156 -- First, deal with the invalid cases
158 if not Is_Valid_Path_Name (Containing_Directory) then
162 Extension'Length = 0 and then (not Is_Valid_Simple_Name (Name))
166 elsif Extension'Length /= 0
167 and then not Is_Valid_Simple_Name (Name & '.' & Extension)
171 -- This is not an invalid case so build the path name
174 Last := Containing_Directory'Length;
175 Result (1 .. Last) := Containing_Directory;
177 -- Add a directory separator if needed
179 if Result (Last) /= Dir_Separator then
181 Result (Last) := Dir_Separator;
186 Result (Last + 1 .. Last + Name'Length) := Name;
187 Last := Last + Name'Length;
189 -- If extension was specified, add dot followed by this extension
191 if Extension'Length /= 0 then
193 Result (Last) := '.';
194 Result (Last + 1 .. Last + Extension'Length) := Extension;
195 Last := Last + Extension'Length;
198 To_Lower_If_Case_Insensitive (Result (1 .. Last));
199 return Result (1 .. Last);
203 --------------------------
204 -- Containing_Directory --
205 --------------------------
207 function Containing_Directory (Name : String) return String is
209 -- First, the invalid case
211 if not Is_Valid_Path_Name (Name) then
216 Norm : constant String := Normalize_Pathname (Name);
217 Last_DS : constant Natural :=
219 (Name, Dir_Seps, Going => Strings.Backward);
224 -- There is no directory separator, returns current working
227 return Current_Directory;
229 -- If Name indicates a root directory, raise Use_Error, because
230 -- it has no containing directory.
239 and then Norm (Norm'Last - 1 .. Norm'Last) = ":\"
240 and then (Norm (Norm'First) in 'a' .. 'z'
241 or else Norm (Norm'First) in 'A' .. 'Z'))))
247 Last : Positive := Last_DS - Name'First + 1;
248 Result : String (1 .. Last);
251 Result := Name (Name'First .. Last_DS);
253 -- Remove any trailing directory separator, except as the
254 -- first character or the first character following a drive
255 -- number on Windows.
261 Result (Last) /= Directory_Separator;
265 and then Result (2) = ':'
267 (Result (1) in 'A' .. 'Z'
269 Result (1) in 'a' .. 'z');
274 -- Special case of current directory, identified by "."
276 if Last = 1 and then Result (1) = '.' then
277 return Current_Directory;
279 -- Special case of "..": the current directory may be a root
282 elsif Last = 2 and then Result (1 .. 2) = ".." then
283 return Containing_Directory (Current_Directory);
286 To_Lower_If_Case_Insensitive (Result (1 .. Last));
287 return Result (1 .. Last);
293 end Containing_Directory;
300 (Source_Name : String;
301 Target_Name : String;
304 pragma Unreferenced (Form);
308 -- First, the invalid cases
310 if not Is_Valid_Path_Name (Source_Name)
311 or else not Is_Valid_Path_Name (Target_Name)
312 or else not Is_Regular_File (Source_Name)
316 elsif Is_Directory (Target_Name) then
320 -- The implementation uses System.OS_Lib.Copy_File, with parameters
321 -- suitable for all platforms.
323 Copy_File (Source_Name, Target_Name, Success, Overwrite, None);
331 ----------------------
332 -- Create_Directory --
333 ----------------------
335 procedure Create_Directory
336 (New_Directory : String;
339 pragma Unreferenced (Form);
341 C_Dir_Name : constant String := New_Directory & ASCII.NUL;
343 function mkdir (Dir_Name : String) return Integer;
344 pragma Import (C, mkdir, "__gnat_mkdir");
347 -- First, the invalid case
349 if not Is_Valid_Path_Name (New_Directory) then
353 if mkdir (C_Dir_Name) /= 0 then
357 end Create_Directory;
363 procedure Create_Path
364 (New_Directory : String;
367 pragma Unreferenced (Form);
369 New_Dir : String (1 .. New_Directory'Length + 1);
370 Last : Positive := 1;
373 -- First, the invalid case
375 if not Is_Valid_Path_Name (New_Directory) then
379 -- Build New_Dir with a directory separator at the end, so that the
380 -- complete path will be found in the loop below.
382 New_Dir (1 .. New_Directory'Length) := New_Directory;
383 New_Dir (New_Dir'Last) := Directory_Separator;
385 -- Create, if necessary, each directory in the path
387 for J in 2 .. New_Dir'Last loop
389 -- Look for the end of an intermediate directory
391 if New_Dir (J) /= Dir_Separator then
394 -- We have found a new intermediate directory each time we find
395 -- a first directory separator.
397 elsif New_Dir (J - 1) /= Dir_Separator then
399 -- No need to create the directory if it already exists
401 if Is_Directory (New_Dir (1 .. Last)) then
404 -- It is an error if a file with such a name already exists
406 elsif Is_Regular_File (New_Dir (1 .. Last)) then
410 Create_Directory (New_Directory => New_Dir (1 .. Last));
417 -----------------------
418 -- Current_Directory --
419 -----------------------
421 function Current_Directory return String is
422 Path_Len : Natural := Max_Path;
423 Buffer : String (1 .. 1 + Max_Path + 1);
425 procedure Local_Get_Current_Dir
426 (Dir : System.Address;
427 Length : System.Address);
428 pragma Import (C, Local_Get_Current_Dir, "__gnat_get_current_dir");
431 Local_Get_Current_Dir (Buffer'Address, Path_Len'Address);
434 Cur : String := Normalize_Pathname (Buffer (1 .. Path_Len));
437 To_Lower_If_Case_Insensitive (Cur);
439 if Cur'Length > 1 and then Cur (Cur'Last) = Dir_Separator then
440 return Cur (1 .. Cur'Last - 1);
445 end Current_Directory;
447 ----------------------
448 -- Delete_Directory --
449 ----------------------
451 procedure Delete_Directory (Directory : String) is
453 -- First, the invalid cases
455 if not Is_Valid_Path_Name (Directory) then
458 elsif not Is_Directory (Directory) then
463 C_Dir_Name : constant String := Directory & ASCII.NUL;
467 if System.OS_Lib.Is_Directory (Directory) then
472 end Delete_Directory;
478 procedure Delete_File (Name : String) is
482 -- First, the invalid cases
484 if not Is_Valid_Path_Name (Name) then
487 elsif not Is_Regular_File (Name) then
491 -- The implementation uses System.OS_Lib.Delete_File
493 Delete_File (Name, Success);
505 procedure Delete_Tree (Directory : String) is
506 Current_Dir : constant String := Current_Directory;
507 Search : Search_Type;
508 Dir_Ent : Directory_Entry_Type;
510 -- First, the invalid cases
512 if not Is_Valid_Path_Name (Directory) then
515 elsif not Is_Directory (Directory) then
519 Set_Directory (Directory);
520 Start_Search (Search, Directory => ".", Pattern => "");
522 while More_Entries (Search) loop
523 Get_Next_Entry (Search, Dir_Ent);
526 File_Name : constant String := Simple_Name (Dir_Ent);
529 if System.OS_Lib.Is_Directory (File_Name) then
530 if File_Name /= "." and then File_Name /= ".." then
531 Delete_Tree (File_Name);
535 Delete_File (File_Name);
540 Set_Directory (Current_Dir);
544 C_Dir_Name : constant String := Directory & ASCII.NUL;
549 if System.OS_Lib.Is_Directory (Directory) then
560 function Exists (Name : String) return Boolean is
562 -- First, the invalid case
564 if not Is_Valid_Path_Name (Name) then
568 -- The implementation is in File_Exists
570 return File_Exists (Name);
578 function Extension (Name : String) return String is
580 -- First, the invalid case
582 if not Is_Valid_Path_Name (Name) then
586 -- Look for first dot that is not followed by a directory separator
588 for Pos in reverse Name'Range loop
590 -- If a directory separator is found before a dot, there is no
593 if Name (Pos) = Dir_Separator then
596 elsif Name (Pos) = '.' then
598 -- We found a dot, build the return value with lower bound 1
601 subtype Result_Type is String (1 .. Name'Last - Pos);
603 return Result_Type (Name (Pos + 1 .. Name'Last));
608 -- No dot were found, there is no extension
614 ----------------------
615 -- Fetch_Next_Entry --
616 ----------------------
618 procedure Fetch_Next_Entry (Search : Search_Type) is
619 Name : String (1 .. 255);
622 Kind : File_Kind := Ordinary_File;
623 -- Initialized to avoid a compilation warning
625 Filename_Addr : System.Address;
626 Filename_Len : aliased Integer;
628 Buffer : array (0 .. Filename_Max + 12) of Character;
629 -- 12 is the size of the dirent structure (see dirent.h), without the
630 -- field for the filename.
632 function readdir_gnat
633 (Directory : System.Address;
634 Buffer : System.Address;
635 Last : not null access Integer) return System.Address;
636 pragma Import (C, readdir_gnat, "__gnat_readdir");
641 -- Search.Value.Is_Valid is always True when Fetch_Next_Entry is called
646 (System.Address (Search.Value.Dir),
648 Filename_Len'Access);
650 -- If no matching entry is found, set Is_Valid to False
652 if Filename_Addr = System.Null_Address then
653 Search.Value.Is_Valid := False;
658 subtype Path_String is String (1 .. Filename_Len);
659 type Path_String_Access is access Path_String;
661 function Address_To_Access is new
662 Ada.Unchecked_Conversion
664 Target => Path_String_Access);
666 Path_Access : constant Path_String_Access :=
667 Address_To_Access (Filename_Addr);
670 Last := Filename_Len;
671 Name (1 .. Last) := Path_Access.all;
674 -- Check if the entry matches the pattern
676 if Match (Name (1 .. Last), Search.Value.Pattern) then
678 Full_Name : constant String :=
681 (Search.Value.Name), Name (1 .. Last));
682 Found : Boolean := False;
685 if File_Exists (Full_Name) then
687 -- Now check if the file kind matches the filter
689 if Is_Regular_File (Full_Name) then
690 if Search.Value.Filter (Ordinary_File) then
691 Kind := Ordinary_File;
695 elsif Is_Directory (Full_Name) then
696 if Search.Value.Filter (Directory) then
701 elsif Search.Value.Filter (Special_File) then
702 Kind := Special_File;
706 -- If it does, update Search and return
709 Search.Value.Entry_Fetched := True;
710 Search.Value.Dir_Entry :=
712 Simple => To_Unbounded_String (Name (1 .. Last)),
713 Full => To_Unbounded_String (Full_Name),
721 end Fetch_Next_Entry;
727 function File_Exists (Name : String) return Boolean is
728 function C_File_Exists (A : System.Address) return Integer;
729 pragma Import (C, C_File_Exists, "__gnat_file_exists");
731 C_Name : String (1 .. Name'Length + 1);
734 C_Name (1 .. Name'Length) := Name;
735 C_Name (C_Name'Last) := ASCII.NUL;
736 return C_File_Exists (C_Name (1)'Address) = 1;
743 procedure Finalize (Search : in out Search_Type) is
745 if Search.Value /= null then
747 -- Close the directory, if one is open
749 if Search.Value.Dir /= No_Dir then
750 Close (Search.Value.Dir);
761 function Full_Name (Name : String) return String is
763 -- First, the invalid case
765 if not Is_Valid_Path_Name (Name) then
769 -- Build the return value with lower bound 1
771 -- Use System.OS_Lib.Normalize_Pathname
774 Value : String := Normalize_Pathname (Name);
775 subtype Result is String (1 .. Value'Length);
777 To_Lower_If_Case_Insensitive (Value);
778 return Result (Value);
783 function Full_Name (Directory_Entry : Directory_Entry_Type) return String is
785 -- First, the invalid case
787 if not Directory_Entry.Is_Valid then
791 -- The value to return has already been computed
793 return To_String (Directory_Entry.Full);
801 procedure Get_Next_Entry
802 (Search : in out Search_Type;
803 Directory_Entry : out Directory_Entry_Type)
806 -- First, the invalid case
808 if Search.Value = null or else not Search.Value.Is_Valid then
812 -- Fetch the next entry, if needed
814 if not Search.Value.Entry_Fetched then
815 Fetch_Next_Entry (Search);
818 -- It is an error if no valid entry is found
820 if not Search.Value.Is_Valid then
824 -- Reset Entry_Fatched and return the entry
826 Search.Value.Entry_Fetched := False;
827 Directory_Entry := Search.Value.Dir_Entry;
835 function Kind (Name : String) return File_Kind is
837 -- First, the invalid case
839 if not File_Exists (Name) then
842 elsif Is_Regular_File (Name) then
843 return Ordinary_File;
845 elsif Is_Directory (Name) then
853 function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind is
855 -- First, the invalid case
857 if not Directory_Entry.Is_Valid then
861 -- The value to return has already be computed
863 return Directory_Entry.Kind;
867 -----------------------
868 -- Modification_Time --
869 -----------------------
871 function Modification_Time (Name : String) return Time is
877 Minute : Minute_Type;
878 Second : Second_Type;
882 -- First, the invalid cases
884 if not (Is_Regular_File (Name) or else Is_Directory (Name)) then
888 Date := File_Time_Stamp (Name);
890 -- Break down the time stamp into its constituents relative to GMT.
891 -- This version of Split does not recognize leap seconds or buffer
892 -- space for time zone processing.
894 GM_Split (Date, Year, Month, Day, Hour, Minute, Second);
896 -- On OpenVMS, the resulting time value must be in the local time
897 -- zone. Ada.Calendar.Time_Of is exactly what we need. Note that
898 -- in both cases, the sub seconds are set to zero (0.0) because the
899 -- time stamp does not store them in its value.
904 (Year, Month, Day, Seconds_Of (Hour, Minute, Second, 0.0));
906 -- On Unix and Windows, the result must be in GMT. Ada.Calendar.
907 -- Formatting.Time_Of with default time zone of zero (0) is the
908 -- routine of choice.
911 Result := Time_Of (Year, Month, Day, Hour, Minute, Second, 0.0);
916 end Modification_Time;
918 function Modification_Time
919 (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time
922 -- First, the invalid case
924 if not Directory_Entry.Is_Valid then
928 -- The value to return has already be computed
930 return Modification_Time (To_String (Directory_Entry.Full));
932 end Modification_Time;
938 function More_Entries (Search : Search_Type) return Boolean is
940 if Search.Value = null then
943 elsif Search.Value.Is_Valid then
945 -- Fetch the next entry, if needed
947 if not Search.Value.Entry_Fetched then
948 Fetch_Next_Entry (Search);
952 return Search.Value.Is_Valid;
959 procedure Rename (Old_Name, New_Name : String) is
963 -- First, the invalid cases
965 if not Is_Valid_Path_Name (Old_Name)
966 or else not Is_Valid_Path_Name (New_Name)
967 or else (not Is_Regular_File (Old_Name)
968 and then not Is_Directory (Old_Name))
972 elsif Is_Regular_File (New_Name) or Is_Directory (New_Name) then
976 -- The implementation uses System.OS_Lib.Rename_File
978 Rename_File (Old_Name, New_Name, Success);
993 Filter : Filter_Type := (others => True);
994 Process : not null access procedure
995 (Directory_Entry : Directory_Entry_Type))
998 Directory_Entry : Directory_Entry_Type;
1001 Start_Search (Srch, Directory, Pattern, Filter);
1003 while More_Entries (Srch) loop
1004 Get_Next_Entry (Srch, Directory_Entry);
1005 Process (Directory_Entry);
1015 procedure Set_Directory (Directory : String) is
1016 C_Dir_Name : constant String := Directory & ASCII.NUL;
1018 function chdir (Dir_Name : String) return Integer;
1019 pragma Import (C, chdir, "chdir");
1022 if chdir (C_Dir_Name) /= 0 then
1031 function Simple_Name (Name : String) return String is
1033 function Simple_Name_CI (Path : String) return String;
1034 -- This function does the job. The difference between Simple_Name_CI
1035 -- and Simple_Name (the parent function) is that the former is case
1036 -- sensitive, while the latter is not. Path and Suffix are adjusted
1037 -- appropriately before calling Simple_Name_CI under platforms where
1038 -- the file system is not case sensitive.
1040 --------------------
1041 -- Simple_Name_CI --
1042 --------------------
1044 function Simple_Name_CI (Path : String) return String is
1045 Cut_Start : Natural :=
1047 (Path, Dir_Seps, Going => Strings.Backward);
1051 -- Cut_Start point to the first simple name character
1053 if Cut_Start = 0 then
1054 Cut_Start := Path'First;
1057 Cut_Start := Cut_Start + 1;
1060 -- Cut_End point to the last simple name character
1062 Cut_End := Path'Last;
1064 Check_For_Standard_Dirs : declare
1065 Offset : constant Integer := Path'First - Name'First;
1066 BN : constant String :=
1067 Name (Cut_Start - Offset .. Cut_End - Offset);
1068 -- Here we use Simple_Name.Name to keep the original casing
1070 Has_Drive_Letter : constant Boolean :=
1071 System.OS_Lib.Path_Separator /= ':';
1072 -- If Path separator is not ':' then we are on a DOS based OS
1073 -- where this character is used as a drive letter separator.
1076 if BN = "." or else BN = ".." then
1079 elsif Has_Drive_Letter
1080 and then BN'Length > 2
1081 and then Characters.Handling.Is_Letter (BN (BN'First))
1082 and then BN (BN'First + 1) = ':'
1084 -- We have a DOS drive letter prefix, remove it
1086 return BN (BN'First + 2 .. BN'Last);
1091 end Check_For_Standard_Dirs;
1094 -- Start of processing for Simple_Name
1097 -- First, the invalid case
1099 if not Is_Valid_Path_Name (Name) then
1103 -- Build the value to return with lower bound 1
1105 if Is_Path_Name_Case_Sensitive then
1107 Value : constant String := Simple_Name_CI (Name);
1108 subtype Result is String (1 .. Value'Length);
1110 return Result (Value);
1115 Value : constant String :=
1116 Simple_Name_CI (Characters.Handling.To_Lower (Name));
1117 subtype Result is String (1 .. Value'Length);
1119 return Result (Value);
1125 function Simple_Name
1126 (Directory_Entry : Directory_Entry_Type) return String
1129 -- First, the invalid case
1131 if not Directory_Entry.Is_Valid then
1135 -- The value to return has already be computed
1137 return To_String (Directory_Entry.Simple);
1145 function Size (Name : String) return File_Size is
1146 C_Name : String (1 .. Name'Length + 1);
1148 function C_Size (Name : System.Address) return Long_Integer;
1149 pragma Import (C, C_Size, "__gnat_named_file_length");
1152 -- First, the invalid case
1154 if not Is_Regular_File (Name) then
1158 C_Name (1 .. Name'Length) := Name;
1159 C_Name (C_Name'Last) := ASCII.NUL;
1160 return File_Size (C_Size (C_Name'Address));
1164 function Size (Directory_Entry : Directory_Entry_Type) return File_Size is
1166 -- First, the invalid case
1168 if not Directory_Entry.Is_Valid then
1172 -- The value to return has already be computed
1174 return Size (To_String (Directory_Entry.Full));
1182 procedure Start_Search
1183 (Search : in out Search_Type;
1186 Filter : Filter_Type := (others => True))
1188 function opendir (file_name : String) return DIRs;
1189 pragma Import (C, opendir, "__gnat_opendir");
1191 C_File_Name : constant String := Directory & ASCII.NUL;
1194 -- First, the invalid case
1196 if not Is_Directory (Directory) then
1200 -- If needed, finalize Search
1204 -- Allocate the default data
1206 Search.Value := new Search_Data;
1209 -- Check the pattern
1211 Search.Value.Pattern := Compile (Pattern, Glob => True);
1214 when Error_In_Regexp =>
1215 Free (Search.Value);
1219 -- Initialize some Search components
1221 Search.Value.Filter := Filter;
1222 Search.Value.Name := To_Unbounded_String (Full_Name (Directory));
1223 Search.Value.Dir := Dir_Type_Value (opendir (C_File_Name));
1224 Search.Value.Is_Valid := True;
1227 ----------------------------------
1228 -- To_Lower_If_Case_Insensitive --
1229 ----------------------------------
1231 procedure To_Lower_If_Case_Insensitive (S : in out String) is
1233 if not Is_Path_Name_Case_Sensitive then
1234 for J in S'Range loop
1235 S (J) := To_Lower (S (J));
1238 end To_Lower_If_Case_Insensitive;
1240 end Ada.Directories;