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-2008, 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 Containing_Directory /= ""
159 and then not Is_Valid_Path_Name (Containing_Directory)
161 raise Name_Error with
162 "invalid directory path name """ & Containing_Directory & '"';
165 Extension'Length = 0 and then (not Is_Valid_Simple_Name (Name))
167 raise Name_Error with
168 "invalid simple name """ & Name & '"';
170 elsif Extension'Length /= 0
171 and then not Is_Valid_Simple_Name (Name & '.' & Extension)
173 raise Name_Error with
174 "invalid file name """ & Name & '.' & Extension & '"';
176 -- This is not an invalid case so build the path name
179 Last := Containing_Directory'Length;
180 Result (1 .. Last) := Containing_Directory;
182 -- Add a directory separator if needed
184 if Last /= 0 and then Result (Last) /= Dir_Separator then
186 Result (Last) := Dir_Separator;
191 Result (Last + 1 .. Last + Name'Length) := Name;
192 Last := Last + Name'Length;
194 -- If extension was specified, add dot followed by this extension
196 if Extension'Length /= 0 then
198 Result (Last) := '.';
199 Result (Last + 1 .. Last + Extension'Length) := Extension;
200 Last := Last + Extension'Length;
203 To_Lower_If_Case_Insensitive (Result (1 .. Last));
204 return Result (1 .. Last);
208 --------------------------
209 -- Containing_Directory --
210 --------------------------
212 function Containing_Directory (Name : String) return String is
214 -- First, the invalid case
216 if not Is_Valid_Path_Name (Name) then
217 raise Name_Error with "invalid path name """ & Name & '"';
221 Norm : constant String := Normalize_Pathname (Name);
222 Last_DS : constant Natural :=
224 (Name, Dir_Seps, Going => Strings.Backward);
229 -- There is no directory separator, returns current working
232 return Current_Directory;
234 -- If Name indicates a root directory, raise Use_Error, because
235 -- it has no containing directory.
244 and then Norm (Norm'Last - 1 .. Norm'Last) = ":\"
245 and then (Norm (Norm'First) in 'a' .. 'z'
246 or else Norm (Norm'First) in 'A' .. 'Z'))))
249 "directory """ & Name & """ has no containing directory";
253 Last : Positive := Last_DS - Name'First + 1;
254 Result : String (1 .. Last);
257 Result := Name (Name'First .. Last_DS);
259 -- Remove any trailing directory separator, except as the
260 -- first character or the first character following a drive
261 -- number on Windows.
267 Result (Last) /= Directory_Separator;
271 and then Result (2) = ':'
273 (Result (1) in 'A' .. 'Z'
275 Result (1) in 'a' .. 'z');
280 -- Special case of current directory, identified by "."
282 if Last = 1 and then Result (1) = '.' then
283 return Current_Directory;
285 -- Special case of "..": the current directory may be a root
288 elsif Last = 2 and then Result (1 .. 2) = ".." then
289 return Containing_Directory (Current_Directory);
292 To_Lower_If_Case_Insensitive (Result (1 .. Last));
293 return Result (1 .. Last);
299 end Containing_Directory;
306 (Source_Name : String;
307 Target_Name : String;
310 pragma Unreferenced (Form);
314 -- First, the invalid cases
316 if not Is_Valid_Path_Name (Source_Name) then
317 raise Name_Error with
318 "invalid source path name """ & Source_Name & '"';
320 elsif not Is_Valid_Path_Name (Target_Name) then
321 raise Name_Error with
322 "invalid target path name """ & Target_Name & '"';
324 elsif not Is_Regular_File (Source_Name) then
325 raise Name_Error with '"' & Source_Name & """ is not a file";
327 elsif Is_Directory (Target_Name) then
328 raise Use_Error with "target """ & Target_Name & """ is a directory";
331 -- The implementation uses System.OS_Lib.Copy_File, with parameters
332 -- suitable for all platforms.
334 Copy_File (Source_Name, Target_Name, Success, Overwrite, None);
337 raise Use_Error with "copy of """ & Source_Name & """ failed";
342 ----------------------
343 -- Create_Directory --
344 ----------------------
346 procedure Create_Directory
347 (New_Directory : String;
350 pragma Unreferenced (Form);
352 C_Dir_Name : constant String := New_Directory & ASCII.NUL;
354 function mkdir (Dir_Name : String) return Integer;
355 pragma Import (C, mkdir, "__gnat_mkdir");
358 -- First, the invalid case
360 if not Is_Valid_Path_Name (New_Directory) then
361 raise Name_Error with
362 "invalid new directory path name """ & New_Directory & '"';
365 if mkdir (C_Dir_Name) /= 0 then
367 "creation of new directory """ & New_Directory & """ failed";
370 end Create_Directory;
376 procedure Create_Path
377 (New_Directory : String;
380 pragma Unreferenced (Form);
382 New_Dir : String (1 .. New_Directory'Length + 1);
383 Last : Positive := 1;
386 -- First, the invalid case
388 if not Is_Valid_Path_Name (New_Directory) then
389 raise Name_Error with
390 "invalid new directory path name """ & New_Directory & '"';
393 -- Build New_Dir with a directory separator at the end, so that the
394 -- complete path will be found in the loop below.
396 New_Dir (1 .. New_Directory'Length) := New_Directory;
397 New_Dir (New_Dir'Last) := Directory_Separator;
399 -- Create, if necessary, each directory in the path
401 for J in 2 .. New_Dir'Last loop
403 -- Look for the end of an intermediate directory
405 if New_Dir (J) /= Dir_Separator and then
410 -- We have found a new intermediate directory each time we find
411 -- a first directory separator.
413 elsif New_Dir (J - 1) /= Dir_Separator and then
414 New_Dir (J - 1) /= '/'
417 -- No need to create the directory if it already exists
419 if Is_Directory (New_Dir (1 .. Last)) then
422 -- It is an error if a file with such a name already exists
424 elsif Is_Regular_File (New_Dir (1 .. Last)) then
426 "file """ & New_Dir (1 .. Last) & """ already exists";
429 Create_Directory (New_Directory => New_Dir (1 .. Last));
436 -----------------------
437 -- Current_Directory --
438 -----------------------
440 function Current_Directory return String is
441 Path_Len : Natural := Max_Path;
442 Buffer : String (1 .. 1 + Max_Path + 1);
444 procedure Local_Get_Current_Dir
445 (Dir : System.Address;
446 Length : System.Address);
447 pragma Import (C, Local_Get_Current_Dir, "__gnat_get_current_dir");
450 Local_Get_Current_Dir (Buffer'Address, Path_Len'Address);
453 Cur : String := Normalize_Pathname (Buffer (1 .. Path_Len));
456 To_Lower_If_Case_Insensitive (Cur);
458 if Cur'Length > 1 and then Cur (Cur'Last) = Dir_Separator then
459 return Cur (1 .. Cur'Last - 1);
464 end Current_Directory;
466 ----------------------
467 -- Delete_Directory --
468 ----------------------
470 procedure Delete_Directory (Directory : String) is
472 -- First, the invalid cases
474 if not Is_Valid_Path_Name (Directory) then
475 raise Name_Error with
476 "invalid directory path name """ & Directory & '"';
478 elsif not Is_Directory (Directory) then
479 raise Name_Error with '"' & Directory & """ not a directory";
483 C_Dir_Name : constant String := Directory & ASCII.NUL;
488 if System.OS_Lib.Is_Directory (Directory) then
490 "deletion of directory """ & Directory & """ failed";
494 end Delete_Directory;
500 procedure Delete_File (Name : String) is
504 -- First, the invalid cases
506 if not Is_Valid_Path_Name (Name) then
507 raise Name_Error with "invalid path name """ & Name & '"';
509 elsif not Is_Regular_File (Name) then
510 raise Name_Error with "file """ & Name & """ does not exist";
513 -- The implementation uses System.OS_Lib.Delete_File
515 Delete_File (Name, Success);
518 raise Use_Error with "file """ & Name & """ could not be deleted";
527 procedure Delete_Tree (Directory : String) is
528 Current_Dir : constant String := Current_Directory;
529 Search : Search_Type;
530 Dir_Ent : Directory_Entry_Type;
532 -- First, the invalid cases
534 if not Is_Valid_Path_Name (Directory) then
535 raise Name_Error with
536 "invalid directory path name """ & Directory & '"';
538 elsif not Is_Directory (Directory) then
539 raise Name_Error with '"' & Directory & """ not a directory";
542 Set_Directory (Directory);
543 Start_Search (Search, Directory => ".", Pattern => "");
545 while More_Entries (Search) loop
546 Get_Next_Entry (Search, Dir_Ent);
549 File_Name : constant String := Simple_Name (Dir_Ent);
552 if System.OS_Lib.Is_Directory (File_Name) then
553 if File_Name /= "." and then File_Name /= ".." then
554 Delete_Tree (File_Name);
558 Delete_File (File_Name);
563 Set_Directory (Current_Dir);
567 C_Dir_Name : constant String := Directory & ASCII.NUL;
572 if System.OS_Lib.Is_Directory (Directory) then
574 "directory tree rooted at """ &
575 Directory & """ could not be deleted";
585 function Exists (Name : String) return Boolean is
587 -- First, the invalid case
589 if not Is_Valid_Path_Name (Name) then
590 raise Name_Error with "invalid path name """ & Name & '"';
593 -- The implementation is in File_Exists
595 return File_Exists (Name);
603 function Extension (Name : String) return String is
605 -- First, the invalid case
607 if not Is_Valid_Path_Name (Name) then
608 raise Name_Error with "invalid path name """ & Name & '"';
611 -- Look for first dot that is not followed by a directory separator
613 for Pos in reverse Name'Range loop
615 -- If a directory separator is found before a dot, there is no
618 if Name (Pos) = Dir_Separator then
621 elsif Name (Pos) = '.' then
623 -- We found a dot, build the return value with lower bound 1
626 subtype Result_Type is String (1 .. Name'Last - Pos);
628 return Result_Type (Name (Pos + 1 .. Name'Last));
633 -- No dot were found, there is no extension
639 ----------------------
640 -- Fetch_Next_Entry --
641 ----------------------
643 procedure Fetch_Next_Entry (Search : Search_Type) is
644 Name : String (1 .. 255);
647 Kind : File_Kind := Ordinary_File;
648 -- Initialized to avoid a compilation warning
650 Filename_Addr : System.Address;
651 Filename_Len : aliased Integer;
653 Buffer : array (0 .. Filename_Max + 12) of Character;
654 -- 12 is the size of the dirent structure (see dirent.h), without the
655 -- field for the filename.
657 function readdir_gnat
658 (Directory : System.Address;
659 Buffer : System.Address;
660 Last : not null access Integer) return System.Address;
661 pragma Import (C, readdir_gnat, "__gnat_readdir");
666 -- Search.Value.Is_Valid is always True when Fetch_Next_Entry is called
671 (System.Address (Search.Value.Dir),
673 Filename_Len'Access);
675 -- If no matching entry is found, set Is_Valid to False
677 if Filename_Addr = System.Null_Address then
678 Search.Value.Is_Valid := False;
683 subtype Path_String is String (1 .. Filename_Len);
684 type Path_String_Access is access Path_String;
686 function Address_To_Access is new
687 Ada.Unchecked_Conversion
689 Target => Path_String_Access);
691 Path_Access : constant Path_String_Access :=
692 Address_To_Access (Filename_Addr);
695 Last := Filename_Len;
696 Name (1 .. Last) := Path_Access.all;
699 -- Check if the entry matches the pattern
701 if Match (Name (1 .. Last), Search.Value.Pattern) then
703 Full_Name : constant String :=
706 (Search.Value.Name), Name (1 .. Last));
707 Found : Boolean := False;
710 if File_Exists (Full_Name) then
712 -- Now check if the file kind matches the filter
714 if Is_Regular_File (Full_Name) then
715 if Search.Value.Filter (Ordinary_File) then
716 Kind := Ordinary_File;
720 elsif Is_Directory (Full_Name) then
721 if Search.Value.Filter (Directory) then
726 elsif Search.Value.Filter (Special_File) then
727 Kind := Special_File;
731 -- If it does, update Search and return
734 Search.Value.Entry_Fetched := True;
735 Search.Value.Dir_Entry :=
737 Simple => To_Unbounded_String (Name (1 .. Last)),
738 Full => To_Unbounded_String (Full_Name),
746 end Fetch_Next_Entry;
752 function File_Exists (Name : String) return Boolean is
753 function C_File_Exists (A : System.Address) return Integer;
754 pragma Import (C, C_File_Exists, "__gnat_file_exists");
756 C_Name : String (1 .. Name'Length + 1);
759 C_Name (1 .. Name'Length) := Name;
760 C_Name (C_Name'Last) := ASCII.NUL;
761 return C_File_Exists (C_Name (1)'Address) = 1;
768 procedure Finalize (Search : in out Search_Type) is
770 if Search.Value /= null then
772 -- Close the directory, if one is open
774 if Search.Value.Dir /= No_Dir then
775 Close (Search.Value.Dir);
786 function Full_Name (Name : String) return String is
788 -- First, the invalid case
790 if not Is_Valid_Path_Name (Name) then
791 raise Name_Error with "invalid path name """ & Name & '"';
794 -- Build the return value with lower bound 1
796 -- Use System.OS_Lib.Normalize_Pathname
799 Value : String := Normalize_Pathname (Name);
800 subtype Result is String (1 .. Value'Length);
802 To_Lower_If_Case_Insensitive (Value);
803 return Result (Value);
808 function Full_Name (Directory_Entry : Directory_Entry_Type) return String is
810 -- First, the invalid case
812 if not Directory_Entry.Is_Valid then
813 raise Status_Error with "invalid directory entry";
816 -- The value to return has already been computed
818 return To_String (Directory_Entry.Full);
826 procedure Get_Next_Entry
827 (Search : in out Search_Type;
828 Directory_Entry : out Directory_Entry_Type)
831 -- First, the invalid case
833 if Search.Value = null or else not Search.Value.Is_Valid then
834 raise Status_Error with "invalid search";
837 -- Fetch the next entry, if needed
839 if not Search.Value.Entry_Fetched then
840 Fetch_Next_Entry (Search);
843 -- It is an error if no valid entry is found
845 if not Search.Value.Is_Valid then
846 raise Status_Error with "no next entry";
849 -- Reset Entry_Fetched and return the entry
851 Search.Value.Entry_Fetched := False;
852 Directory_Entry := Search.Value.Dir_Entry;
860 function Kind (Name : String) return File_Kind is
862 -- First, the invalid case
864 if not File_Exists (Name) then
865 raise Name_Error with "file """ & Name & """ does not exist";
867 elsif Is_Regular_File (Name) then
868 return Ordinary_File;
870 elsif Is_Directory (Name) then
878 function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind is
880 -- First, the invalid case
882 if not Directory_Entry.Is_Valid then
883 raise Status_Error with "invalid directory entry";
886 -- The value to return has already be computed
888 return Directory_Entry.Kind;
892 -----------------------
893 -- Modification_Time --
894 -----------------------
896 function Modification_Time (Name : String) return Time is
902 Minute : Minute_Type;
903 Second : Second_Type;
907 -- First, the invalid cases
909 if not (Is_Regular_File (Name) or else Is_Directory (Name)) then
910 raise Name_Error with '"' & Name & """ not a file or directory";
913 Date := File_Time_Stamp (Name);
915 -- Break down the time stamp into its constituents relative to GMT.
916 -- This version of Split does not recognize leap seconds or buffer
917 -- space for time zone processing.
919 GM_Split (Date, Year, Month, Day, Hour, Minute, Second);
921 -- On OpenVMS, the resulting time value must be in the local time
922 -- zone. Ada.Calendar.Time_Of is exactly what we need. Note that
923 -- in both cases, the sub seconds are set to zero (0.0) because the
924 -- time stamp does not store them in its value.
929 (Year, Month, Day, Seconds_Of (Hour, Minute, Second, 0.0));
931 -- On Unix and Windows, the result must be in GMT. Ada.Calendar.
932 -- Formatting.Time_Of with default time zone of zero (0) is the
933 -- routine of choice.
936 Result := Time_Of (Year, Month, Day, Hour, Minute, Second, 0.0);
941 end Modification_Time;
943 function Modification_Time
944 (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time
947 -- First, the invalid case
949 if not Directory_Entry.Is_Valid then
950 raise Status_Error with "invalid directory entry";
953 -- The value to return has already be computed
955 return Modification_Time (To_String (Directory_Entry.Full));
957 end Modification_Time;
963 function More_Entries (Search : Search_Type) return Boolean is
965 if Search.Value = null then
968 elsif Search.Value.Is_Valid then
970 -- Fetch the next entry, if needed
972 if not Search.Value.Entry_Fetched then
973 Fetch_Next_Entry (Search);
977 return Search.Value.Is_Valid;
984 procedure Rename (Old_Name, New_Name : String) is
988 -- First, the invalid cases
990 if not Is_Valid_Path_Name (Old_Name) then
991 raise Name_Error with "invalid old path name """ & Old_Name & '"';
993 elsif not Is_Valid_Path_Name (New_Name) then
994 raise Name_Error with "invalid new path name """ & New_Name & '"';
996 elsif not Is_Regular_File (Old_Name)
997 and then not Is_Directory (Old_Name)
999 raise Name_Error with "old file """ & Old_Name & """ does not exist";
1001 elsif Is_Regular_File (New_Name) or Is_Directory (New_Name) then
1002 raise Use_Error with
1003 "new name """ & New_Name
1004 & """ designates a file that already exists";
1007 -- The implementation uses System.OS_Lib.Rename_File
1009 Rename_File (Old_Name, New_Name, Success);
1012 raise Use_Error with
1013 "file """ & Old_Name & """ could not be renamed";
1023 (Directory : String;
1025 Filter : Filter_Type := (others => True);
1026 Process : not null access procedure
1027 (Directory_Entry : Directory_Entry_Type))
1030 Directory_Entry : Directory_Entry_Type;
1033 Start_Search (Srch, Directory, Pattern, Filter);
1035 while More_Entries (Srch) loop
1036 Get_Next_Entry (Srch, Directory_Entry);
1037 Process (Directory_Entry);
1047 procedure Set_Directory (Directory : String) is
1048 C_Dir_Name : constant String := Directory & ASCII.NUL;
1050 function chdir (Dir_Name : String) return Integer;
1051 pragma Import (C, chdir, "chdir");
1054 if not Is_Valid_Path_Name (Directory) then
1055 raise Name_Error with
1056 "invalid directory path name & """ & Directory & '"';
1058 elsif not Is_Directory (Directory) then
1059 raise Name_Error with
1060 "directory """ & Directory & """ does not exist";
1062 elsif chdir (C_Dir_Name) /= 0 then
1063 raise Name_Error with
1064 "could not set to designated directory """ & Directory & '"';
1072 function Simple_Name (Name : String) return String is
1074 function Simple_Name_CI (Path : String) return String;
1075 -- This function does the job. The difference between Simple_Name_CI
1076 -- and Simple_Name (the parent function) is that the former is case
1077 -- sensitive, while the latter is not. Path and Suffix are adjusted
1078 -- appropriately before calling Simple_Name_CI under platforms where
1079 -- the file system is not case sensitive.
1081 --------------------
1082 -- Simple_Name_CI --
1083 --------------------
1085 function Simple_Name_CI (Path : String) return String is
1086 Cut_Start : Natural :=
1088 (Path, Dir_Seps, Going => Strings.Backward);
1092 -- Cut_Start point to the first simple name character
1094 if Cut_Start = 0 then
1095 Cut_Start := Path'First;
1098 Cut_Start := Cut_Start + 1;
1101 -- Cut_End point to the last simple name character
1103 Cut_End := Path'Last;
1105 Check_For_Standard_Dirs : declare
1106 Offset : constant Integer := Path'First - Name'First;
1107 BN : constant String :=
1108 Name (Cut_Start - Offset .. Cut_End - Offset);
1109 -- Here we use Simple_Name.Name to keep the original casing
1111 Has_Drive_Letter : constant Boolean :=
1112 System.OS_Lib.Path_Separator /= ':';
1113 -- If Path separator is not ':' then we are on a DOS based OS
1114 -- where this character is used as a drive letter separator.
1117 if BN = "." or else BN = ".." then
1120 elsif Has_Drive_Letter
1121 and then BN'Length > 2
1122 and then Characters.Handling.Is_Letter (BN (BN'First))
1123 and then BN (BN'First + 1) = ':'
1125 -- We have a DOS drive letter prefix, remove it
1127 return BN (BN'First + 2 .. BN'Last);
1132 end Check_For_Standard_Dirs;
1135 -- Start of processing for Simple_Name
1138 -- First, the invalid case
1140 if not Is_Valid_Path_Name (Name) then
1141 raise Name_Error with "invalid path name """ & Name & '"';
1144 -- Build the value to return with lower bound 1
1146 if Is_Path_Name_Case_Sensitive then
1148 Value : constant String := Simple_Name_CI (Name);
1149 subtype Result is String (1 .. Value'Length);
1151 return Result (Value);
1156 Value : constant String :=
1157 Simple_Name_CI (Characters.Handling.To_Lower (Name));
1158 subtype Result is String (1 .. Value'Length);
1160 return Result (Value);
1166 function Simple_Name
1167 (Directory_Entry : Directory_Entry_Type) return String
1170 -- First, the invalid case
1172 if not Directory_Entry.Is_Valid then
1173 raise Status_Error with "invalid directory entry";
1176 -- The value to return has already be computed
1178 return To_String (Directory_Entry.Simple);
1186 function Size (Name : String) return File_Size is
1187 C_Name : String (1 .. Name'Length + 1);
1189 function C_Size (Name : System.Address) return Long_Integer;
1190 pragma Import (C, C_Size, "__gnat_named_file_length");
1193 -- First, the invalid case
1195 if not Is_Regular_File (Name) then
1196 raise Name_Error with "file """ & Name & """ does not exist";
1199 C_Name (1 .. Name'Length) := Name;
1200 C_Name (C_Name'Last) := ASCII.NUL;
1201 return File_Size (C_Size (C_Name'Address));
1205 function Size (Directory_Entry : Directory_Entry_Type) return File_Size is
1207 -- First, the invalid case
1209 if not Directory_Entry.Is_Valid then
1210 raise Status_Error with "invalid directory entry";
1213 -- The value to return has already be computed
1215 return Size (To_String (Directory_Entry.Full));
1223 procedure Start_Search
1224 (Search : in out Search_Type;
1227 Filter : Filter_Type := (others => True))
1229 function opendir (file_name : String) return DIRs;
1230 pragma Import (C, opendir, "__gnat_opendir");
1232 C_File_Name : constant String := Directory & ASCII.NUL;
1234 Dir : Dir_Type_Value;
1237 -- First, the invalid case Name_Error
1239 if not Is_Directory (Directory) then
1240 raise Name_Error with
1241 "unknown directory """ & Simple_Name (Directory) & '"';
1244 -- Check the pattern
1247 Pat := Compile (Pattern, Glob => True);
1249 when Error_In_Regexp =>
1250 Free (Search.Value);
1251 raise Name_Error with "invalid pattern """ & Pattern & '"';
1254 Dir := Dir_Type_Value (opendir (C_File_Name));
1256 if Dir = No_Dir then
1257 raise Use_Error with
1258 "unreadable directory """ & Simple_Name (Directory) & '"';
1261 -- If needed, finalize Search
1265 -- Allocate the default data
1267 Search.Value := new Search_Data;
1269 -- Initialize some Search components
1271 Search.Value.Filter := Filter;
1272 Search.Value.Name := To_Unbounded_String (Full_Name (Directory));
1273 Search.Value.Pattern := Pat;
1274 Search.Value.Dir := Dir;
1275 Search.Value.Is_Valid := True;
1278 ----------------------------------
1279 -- To_Lower_If_Case_Insensitive --
1280 ----------------------------------
1282 procedure To_Lower_If_Case_Insensitive (S : in out String) is
1284 if not Is_Path_Name_Case_Sensitive then
1285 for J in S'Range loop
1286 S (J) := To_Lower (S (J));
1289 end To_Lower_If_Case_Insensitive;
1291 end Ada.Directories;