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-2011, 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 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada.Calendar; use Ada.Calendar;
33 with Ada.Calendar.Formatting; use Ada.Calendar.Formatting;
34 with Ada.Directories.Validity; use Ada.Directories.Validity;
35 with Ada.Strings.Maps;
36 with Ada.Strings.Fixed;
37 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
38 with Ada.Unchecked_Conversion;
39 with Ada.Unchecked_Deallocation;
40 with Ada.Characters.Handling; use Ada.Characters.Handling;
42 with System.CRTL; use System.CRTL;
43 with System.OS_Constants; use System.OS_Constants;
44 with System.OS_Lib; use System.OS_Lib;
45 with System.Regexp; use System.Regexp;
46 with System.File_IO; use System.File_IO;
47 with System; use System;
49 package body Ada.Directories is
51 Filename_Max : constant Integer := 1024;
52 -- 1024 is the value of FILENAME_MAX in stdio.h
54 type Dir_Type_Value is new Address;
55 -- This is the low-level address directory structure as returned by the C
58 No_Dir : constant Dir_Type_Value := Dir_Type_Value (Null_Address);
60 Dir_Separator : constant Character;
61 pragma Import (C, Dir_Separator, "__gnat_dir_separator");
62 -- Running system default directory separator
64 Dir_Seps : constant Ada.Strings.Maps.Character_Set :=
65 Ada.Strings.Maps.To_Set ("/\");
66 -- UNIX and DOS style directory separators
69 pragma Import (C, Max_Path, "__gnat_max_path_len");
70 -- The maximum length of a path
72 type Search_Data is record
73 Is_Valid : Boolean := False;
74 Name : Unbounded_String;
77 Dir : Dir_Type_Value := No_Dir;
78 Entry_Fetched : Boolean := False;
79 Dir_Entry : Directory_Entry_Type;
81 -- The current state of a search
83 Empty_String : constant String := (1 .. 0 => ASCII.NUL);
84 -- Empty string, returned by function Extension when there is no extension
86 procedure Free is new Ada.Unchecked_Deallocation (Search_Data, Search_Ptr);
88 procedure Close (Dir : Dir_Type_Value);
90 function File_Exists (Name : String) return Boolean;
91 -- Returns True if the named file exists
93 procedure Fetch_Next_Entry (Search : Search_Type);
94 -- Get the next entry in a directory, setting Entry_Fetched if successful
95 -- or resetting Is_Valid if not.
101 function Base_Name (Name : String) return String is
102 Simple : constant String := Simple_Name (Name);
103 -- Simple'First is guaranteed to be 1
106 -- Look for the last dot in the file name and return the part of the
107 -- file name preceding this last dot. If the first dot is the first
108 -- character of the file name, the base name is the empty string.
110 for Pos in reverse Simple'Range loop
111 if Simple (Pos) = '.' then
112 return Simple (1 .. Pos - 1);
116 -- If there is no dot, return the complete file name
125 procedure Close (Dir : Dir_Type_Value) is
127 pragma Warnings (Off, Discard);
129 function closedir (directory : DIRs) return Integer;
130 pragma Import (C, closedir, "__gnat_closedir");
133 Discard := closedir (DIRs (Dir));
141 (Containing_Directory : String := "";
143 Extension : String := "") return String
145 Result : String (1 .. Containing_Directory'Length +
146 Name'Length + Extension'Length + 2);
150 -- First, deal with the invalid cases
152 if Containing_Directory /= ""
153 and then not Is_Valid_Path_Name (Containing_Directory)
155 raise Name_Error with
156 "invalid directory path name """ & Containing_Directory & '"';
159 Extension'Length = 0 and then (not Is_Valid_Simple_Name (Name))
161 raise Name_Error with
162 "invalid simple name """ & Name & '"';
164 elsif Extension'Length /= 0
165 and then not Is_Valid_Simple_Name (Name & '.' & Extension)
167 raise Name_Error with
168 "invalid file name """ & Name & '.' & Extension & '"';
170 -- This is not an invalid case so build the path name
173 Last := Containing_Directory'Length;
174 Result (1 .. Last) := Containing_Directory;
176 -- Add a directory separator if needed
178 if Last /= 0 and then Result (Last) /= Dir_Separator then
180 Result (Last) := Dir_Separator;
185 Result (Last + 1 .. Last + Name'Length) := Name;
186 Last := Last + Name'Length;
188 -- If extension was specified, add dot followed by this extension
190 if Extension'Length /= 0 then
192 Result (Last) := '.';
193 Result (Last + 1 .. Last + Extension'Length) := Extension;
194 Last := Last + Extension'Length;
197 return Result (1 .. Last);
201 --------------------------
202 -- Containing_Directory --
203 --------------------------
205 function Containing_Directory (Name : String) return String is
207 -- First, the invalid case
209 if not Is_Valid_Path_Name (Name) then
210 raise Name_Error with "invalid path name """ & Name & '"';
214 -- We need to resolve links because of A.16(47), since we must not
215 -- return alternative names for files.
217 Norm : constant String := Normalize_Pathname (Name);
218 Last_DS : constant Natural :=
220 (Name, Dir_Seps, Going => Strings.Backward);
225 -- There is no directory separator, returns current working
228 return Current_Directory;
230 -- If Name indicates a root directory, raise Use_Error, because
231 -- it has no containing directory.
240 and then Norm (Norm'Last - 1 .. Norm'Last) = ":\"
241 and then (Norm (Norm'First) in 'a' .. 'z'
242 or else Norm (Norm'First) in 'A' .. 'Z'))))
245 "directory """ & Name & """ has no containing directory";
249 Last : Positive := Last_DS - Name'First + 1;
250 Result : String (1 .. Last);
253 Result := Name (Name'First .. Last_DS);
255 -- Remove any trailing directory separator, except as the
256 -- first character or the first character following a drive
257 -- number on Windows.
263 Result (Last) /= Directory_Separator;
267 and then Result (2) = ':'
269 (Result (1) in 'A' .. 'Z'
271 Result (1) in 'a' .. 'z');
276 -- Special case of current directory, identified by "."
278 if Last = 1 and then Result (1) = '.' then
279 return Current_Directory;
281 -- Special case of "..": the current directory may be a root
284 elsif Last = 2 and then Result (1 .. 2) = ".." then
285 return Containing_Directory (Current_Directory);
288 return Result (1 .. Last);
294 end Containing_Directory;
301 (Source_Name : String;
302 Target_Name : String;
306 Mode : Copy_Mode := Overwrite;
307 Preserve : Attribute := None;
310 -- First, the invalid cases
312 if not Is_Valid_Path_Name (Source_Name) then
313 raise Name_Error with
314 "invalid source path name """ & Source_Name & '"';
316 elsif not Is_Valid_Path_Name (Target_Name) then
317 raise Name_Error with
318 "invalid target path name """ & Target_Name & '"';
320 elsif not Is_Regular_File (Source_Name) then
321 raise Name_Error with '"' & Source_Name & """ is not a file";
323 elsif Is_Directory (Target_Name) then
324 raise Use_Error with "target """ & Target_Name & """ is a directory";
327 if Form'Length > 0 then
329 Formstr : String (1 .. Form'Length + 1);
333 -- Acquire form string, setting required NUL terminator
335 Formstr (1 .. Form'Length) := Form;
336 Formstr (Formstr'Last) := ASCII.NUL;
338 -- Convert form string to lower case
340 for J in Formstr'Range loop
341 if Formstr (J) in 'A' .. 'Z' then
343 Character'Val (Character'Pos (Formstr (J)) + 32);
349 Form_Parameter (Formstr, "mode", V1, V2);
354 elsif Formstr (V1 .. V2) = "copy" then
357 elsif Formstr (V1 .. V2) = "overwrite" then
360 elsif Formstr (V1 .. V2) = "append" then
364 raise Use_Error with "invalid Form";
367 Form_Parameter (Formstr, "preserve", V1, V2);
372 elsif Formstr (V1 .. V2) = "timestamps" then
373 Preserve := Time_Stamps;
375 elsif Formstr (V1 .. V2) = "all_attributes" then
378 elsif Formstr (V1 .. V2) = "no_attributes" then
382 raise Use_Error with "invalid Form";
387 -- Do actual copy using System.OS_Lib.Copy_File
389 Copy_File (Source_Name, Target_Name, Success, Mode, Preserve);
392 raise Use_Error with "copy of """ & Source_Name & """ failed";
397 ----------------------
398 -- Create_Directory --
399 ----------------------
401 procedure Create_Directory
402 (New_Directory : String;
405 pragma Unreferenced (Form);
407 C_Dir_Name : constant String := New_Directory & ASCII.NUL;
409 function mkdir (Dir_Name : String) return Integer;
410 pragma Import (C, mkdir, "__gnat_mkdir");
413 -- First, the invalid case
415 if not Is_Valid_Path_Name (New_Directory) then
416 raise Name_Error with
417 "invalid new directory path name """ & New_Directory & '"';
420 if mkdir (C_Dir_Name) /= 0 then
422 "creation of new directory """ & New_Directory & """ failed";
425 end Create_Directory;
431 procedure Create_Path
432 (New_Directory : String;
435 pragma Unreferenced (Form);
437 New_Dir : String (1 .. New_Directory'Length + 1);
438 Last : Positive := 1;
441 -- First, the invalid case
443 if not Is_Valid_Path_Name (New_Directory) then
444 raise Name_Error with
445 "invalid new directory path name """ & New_Directory & '"';
448 -- Build New_Dir with a directory separator at the end, so that the
449 -- complete path will be found in the loop below.
451 New_Dir (1 .. New_Directory'Length) := New_Directory;
452 New_Dir (New_Dir'Last) := Directory_Separator;
454 -- Create, if necessary, each directory in the path
456 for J in 2 .. New_Dir'Last loop
458 -- Look for the end of an intermediate directory
460 if New_Dir (J) /= Dir_Separator and then
465 -- We have found a new intermediate directory each time we find
466 -- a first directory separator.
468 elsif New_Dir (J - 1) /= Dir_Separator and then
469 New_Dir (J - 1) /= '/'
472 -- No need to create the directory if it already exists
474 if Is_Directory (New_Dir (1 .. Last)) then
477 -- It is an error if a file with such a name already exists
479 elsif Is_Regular_File (New_Dir (1 .. Last)) then
481 "file """ & New_Dir (1 .. Last) & """ already exists";
484 Create_Directory (New_Directory => New_Dir (1 .. Last));
491 -----------------------
492 -- Current_Directory --
493 -----------------------
495 function Current_Directory return String is
496 Path_Len : Natural := Max_Path;
497 Buffer : String (1 .. 1 + Max_Path + 1);
499 procedure Local_Get_Current_Dir (Dir : Address; Length : Address);
500 pragma Import (C, Local_Get_Current_Dir, "__gnat_get_current_dir");
503 Local_Get_Current_Dir (Buffer'Address, Path_Len'Address);
506 -- We need to resolve links because of A.16(47), since we must not
507 -- return alternative names for files
508 Cur : constant String := Normalize_Pathname (Buffer (1 .. Path_Len));
511 if Cur'Length > 1 and then Cur (Cur'Last) = Dir_Separator then
512 return Cur (1 .. Cur'Last - 1);
517 end Current_Directory;
519 ----------------------
520 -- Delete_Directory --
521 ----------------------
523 procedure Delete_Directory (Directory : String) is
525 -- First, the invalid cases
527 if not Is_Valid_Path_Name (Directory) then
528 raise Name_Error with
529 "invalid directory path name """ & Directory & '"';
531 elsif not Is_Directory (Directory) then
532 raise Name_Error with '"' & Directory & """ not a directory";
536 C_Dir_Name : constant String := Directory & ASCII.NUL;
539 if rmdir (C_Dir_Name) /= 0 then
541 "deletion of directory """ & Directory & """ failed";
545 end Delete_Directory;
551 procedure Delete_File (Name : String) is
555 -- First, the invalid cases
557 if not Is_Valid_Path_Name (Name) then
558 raise Name_Error with "invalid path name """ & Name & '"';
560 elsif not Is_Regular_File (Name) then
561 raise Name_Error with "file """ & Name & """ does not exist";
564 -- Do actual deletion using System.OS_Lib.Delete_File
566 Delete_File (Name, Success);
569 raise Use_Error with "file """ & Name & """ could not be deleted";
578 procedure Delete_Tree (Directory : String) is
579 Current_Dir : constant String := Current_Directory;
580 Search : Search_Type;
581 Dir_Ent : Directory_Entry_Type;
583 -- First, the invalid cases
585 if not Is_Valid_Path_Name (Directory) then
586 raise Name_Error with
587 "invalid directory path name """ & Directory & '"';
589 elsif not Is_Directory (Directory) then
590 raise Name_Error with '"' & Directory & """ not a directory";
593 Set_Directory (Directory);
594 Start_Search (Search, Directory => ".", Pattern => "");
596 while More_Entries (Search) loop
597 Get_Next_Entry (Search, Dir_Ent);
600 File_Name : constant String := Simple_Name (Dir_Ent);
603 if OS_Lib.Is_Directory (File_Name) then
604 if File_Name /= "." and then File_Name /= ".." then
605 Delete_Tree (File_Name);
609 Delete_File (File_Name);
614 Set_Directory (Current_Dir);
618 C_Dir_Name : constant String := Directory & ASCII.NUL;
621 if rmdir (C_Dir_Name) /= 0 then
623 "directory tree rooted at """ &
624 Directory & """ could not be deleted";
634 function Exists (Name : String) return Boolean is
636 -- First, the invalid case
638 if not Is_Valid_Path_Name (Name) then
639 raise Name_Error with "invalid path name """ & Name & '"';
642 -- The implementation is in File_Exists
644 return File_Exists (Name);
652 function Extension (Name : String) return String is
654 -- First, the invalid case
656 if not Is_Valid_Path_Name (Name) then
657 raise Name_Error with "invalid path name """ & Name & '"';
660 -- Look for first dot that is not followed by a directory separator
662 for Pos in reverse Name'Range loop
664 -- If a directory separator is found before a dot, there is no
667 if Name (Pos) = Dir_Separator then
670 elsif Name (Pos) = '.' then
672 -- We found a dot, build the return value with lower bound 1
675 subtype Result_Type is String (1 .. Name'Last - Pos);
677 return Result_Type (Name (Pos + 1 .. Name'Last));
682 -- No dot were found, there is no extension
688 ----------------------
689 -- Fetch_Next_Entry --
690 ----------------------
692 procedure Fetch_Next_Entry (Search : Search_Type) is
693 Name : String (1 .. 255);
696 Kind : File_Kind := Ordinary_File;
697 -- Initialized to avoid a compilation warning
699 Filename_Addr : Address;
700 Filename_Len : aliased Integer;
702 Buffer : array (0 .. Filename_Max + 12) of Character;
703 -- 12 is the size of the dirent structure (see dirent.h), without the
704 -- field for the filename.
706 function readdir_gnat
707 (Directory : Address;
709 Last : not null access Integer) return Address;
710 pragma Import (C, readdir_gnat, "__gnat_readdir");
713 -- Search.Value.Is_Valid is always True when Fetch_Next_Entry is called
718 (Address (Search.Value.Dir),
720 Filename_Len'Access);
722 -- If no matching entry is found, set Is_Valid to False
724 if Filename_Addr = Null_Address then
725 Search.Value.Is_Valid := False;
730 subtype Path_String is String (1 .. Filename_Len);
731 type Path_String_Access is access Path_String;
733 function Address_To_Access is new
734 Ada.Unchecked_Conversion
736 Target => Path_String_Access);
738 Path_Access : constant Path_String_Access :=
739 Address_To_Access (Filename_Addr);
742 Last := Filename_Len;
743 Name (1 .. Last) := Path_Access.all;
746 -- Check if the entry matches the pattern
748 if Match (Name (1 .. Last), Search.Value.Pattern) then
750 Full_Name : constant String :=
753 (Search.Value.Name), Name (1 .. Last));
754 Found : Boolean := False;
757 if File_Exists (Full_Name) then
759 -- Now check if the file kind matches the filter
761 if Is_Regular_File (Full_Name) then
762 if Search.Value.Filter (Ordinary_File) then
763 Kind := Ordinary_File;
767 elsif Is_Directory (Full_Name) then
768 if Search.Value.Filter (Directory) then
773 elsif Search.Value.Filter (Special_File) then
774 Kind := Special_File;
778 -- If it does, update Search and return
781 Search.Value.Entry_Fetched := True;
782 Search.Value.Dir_Entry :=
784 Simple => To_Unbounded_String (Name (1 .. Last)),
785 Full => To_Unbounded_String (Full_Name),
793 end Fetch_Next_Entry;
799 function File_Exists (Name : String) return Boolean is
800 function C_File_Exists (A : Address) return Integer;
801 pragma Import (C, C_File_Exists, "__gnat_file_exists");
803 C_Name : String (1 .. Name'Length + 1);
806 C_Name (1 .. Name'Length) := Name;
807 C_Name (C_Name'Last) := ASCII.NUL;
808 return C_File_Exists (C_Name (1)'Address) = 1;
815 procedure Finalize (Search : in out Search_Type) is
817 if Search.Value /= null then
819 -- Close the directory, if one is open
821 if Search.Value.Dir /= No_Dir then
822 Close (Search.Value.Dir);
833 function Full_Name (Name : String) return String is
835 -- First, the invalid case
837 if not Is_Valid_Path_Name (Name) then
838 raise Name_Error with "invalid path name """ & Name & '"';
841 -- Build the return value with lower bound 1
843 -- Use System.OS_Lib.Normalize_Pathname
846 -- We need to resolve links because of A.16(47), since we must not
847 -- return alternative names for files.
849 Value : constant String := Normalize_Pathname (Name);
850 subtype Result is String (1 .. Value'Length);
853 return Result (Value);
858 function Full_Name (Directory_Entry : Directory_Entry_Type) return String is
860 -- First, the invalid case
862 if not Directory_Entry.Is_Valid then
863 raise Status_Error with "invalid directory entry";
866 -- The value to return has already been computed
868 return To_String (Directory_Entry.Full);
876 procedure Get_Next_Entry
877 (Search : in out Search_Type;
878 Directory_Entry : out Directory_Entry_Type)
881 -- First, the invalid case
883 if Search.Value = null or else not Search.Value.Is_Valid then
884 raise Status_Error with "invalid search";
887 -- Fetch the next entry, if needed
889 if not Search.Value.Entry_Fetched then
890 Fetch_Next_Entry (Search);
893 -- It is an error if no valid entry is found
895 if not Search.Value.Is_Valid then
896 raise Status_Error with "no next entry";
899 -- Reset Entry_Fetched and return the entry
901 Search.Value.Entry_Fetched := False;
902 Directory_Entry := Search.Value.Dir_Entry;
910 function Kind (Name : String) return File_Kind is
912 -- First, the invalid case
914 if not File_Exists (Name) then
915 raise Name_Error with "file """ & Name & """ does not exist";
917 elsif Is_Regular_File (Name) then
918 return Ordinary_File;
920 elsif Is_Directory (Name) then
928 function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind is
930 -- First, the invalid case
932 if not Directory_Entry.Is_Valid then
933 raise Status_Error with "invalid directory entry";
936 -- The value to return has already be computed
938 return Directory_Entry.Kind;
942 -----------------------
943 -- Modification_Time --
944 -----------------------
946 function Modification_Time (Name : String) return Time is
952 Minute : Minute_Type;
953 Second : Second_Type;
957 -- First, the invalid cases
959 if not (Is_Regular_File (Name) or else Is_Directory (Name)) then
960 raise Name_Error with '"' & Name & """ not a file or directory";
963 Date := File_Time_Stamp (Name);
965 -- Break down the time stamp into its constituents relative to GMT.
966 -- This version of Split does not recognize leap seconds or buffer
967 -- space for time zone processing.
969 GM_Split (Date, Year, Month, Day, Hour, Minute, Second);
971 -- On OpenVMS, the resulting time value must be in the local time
972 -- zone. Ada.Calendar.Time_Of is exactly what we need. Note that
973 -- in both cases, the sub seconds are set to zero (0.0) because the
974 -- time stamp does not store them in its value.
979 (Year, Month, Day, Seconds_Of (Hour, Minute, Second, 0.0));
981 -- On Unix and Windows, the result must be in GMT. Ada.Calendar.
982 -- Formatting.Time_Of with default time zone of zero (0) is the
983 -- routine of choice.
986 Result := Time_Of (Year, Month, Day, Hour, Minute, Second, 0.0);
991 end Modification_Time;
993 function Modification_Time
994 (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time
997 -- First, the invalid case
999 if not Directory_Entry.Is_Valid then
1000 raise Status_Error with "invalid directory entry";
1003 -- The value to return has already be computed
1005 return Modification_Time (To_String (Directory_Entry.Full));
1007 end Modification_Time;
1013 function More_Entries (Search : Search_Type) return Boolean is
1015 if Search.Value = null then
1018 elsif Search.Value.Is_Valid then
1020 -- Fetch the next entry, if needed
1022 if not Search.Value.Entry_Fetched then
1023 Fetch_Next_Entry (Search);
1027 return Search.Value.Is_Valid;
1034 procedure Rename (Old_Name, New_Name : String) is
1038 -- First, the invalid cases
1040 if not Is_Valid_Path_Name (Old_Name) then
1041 raise Name_Error with "invalid old path name """ & Old_Name & '"';
1043 elsif not Is_Valid_Path_Name (New_Name) then
1044 raise Name_Error with "invalid new path name """ & New_Name & '"';
1046 elsif not Is_Regular_File (Old_Name)
1047 and then not Is_Directory (Old_Name)
1049 raise Name_Error with "old file """ & Old_Name & """ does not exist";
1051 elsif Is_Regular_File (New_Name) or else Is_Directory (New_Name) then
1052 raise Use_Error with
1053 "new name """ & New_Name
1054 & """ designates a file that already exists";
1057 -- Do actual rename using System.OS_Lib.Rename_File
1059 Rename_File (Old_Name, New_Name, Success);
1063 -- AI05-0231-1: Name_Error should be raised in case a directory
1064 -- component of New_Name does not exist (as in New_Name =>
1065 -- "/no-such-dir/new-filename"). ENOENT indicates that. ENOENT
1066 -- also indicate that the Old_Name does not exist, but we already
1067 -- checked for that above. All other errors are Use_Error.
1069 if Errno = ENOENT then
1070 raise Name_Error with
1071 "file """ & Containing_Directory (New_Name) & """ not found";
1074 raise Use_Error with
1075 "file """ & Old_Name & """ could not be renamed";
1086 (Directory : String;
1088 Filter : Filter_Type := (others => True);
1089 Process : not null access procedure
1090 (Directory_Entry : Directory_Entry_Type))
1093 Directory_Entry : Directory_Entry_Type;
1096 Start_Search (Srch, Directory, Pattern, Filter);
1098 while More_Entries (Srch) loop
1099 Get_Next_Entry (Srch, Directory_Entry);
1100 Process (Directory_Entry);
1110 procedure Set_Directory (Directory : String) is
1111 C_Dir_Name : constant String := Directory & ASCII.NUL;
1113 if not Is_Valid_Path_Name (Directory) then
1114 raise Name_Error with
1115 "invalid directory path name & """ & Directory & '"';
1117 elsif not Is_Directory (Directory) then
1118 raise Name_Error with
1119 "directory """ & Directory & """ does not exist";
1121 elsif chdir (C_Dir_Name) /= 0 then
1122 raise Name_Error with
1123 "could not set to designated directory """ & Directory & '"';
1131 function Simple_Name (Name : String) return String is
1133 function Simple_Name_Internal (Path : String) return String;
1134 -- This function does the job
1136 --------------------------
1137 -- Simple_Name_Internal --
1138 --------------------------
1140 function Simple_Name_Internal (Path : String) return String is
1141 Cut_Start : Natural :=
1143 (Path, Dir_Seps, Going => Strings.Backward);
1147 -- Cut_Start pointS to the first simple name character
1149 Cut_Start := (if Cut_Start = 0 then Path'First else Cut_Start + 1);
1151 -- Cut_End point to the last simple name character
1153 Cut_End := Path'Last;
1155 Check_For_Standard_Dirs : declare
1156 BN : constant String := Path (Cut_Start .. Cut_End);
1158 Has_Drive_Letter : constant Boolean :=
1159 OS_Lib.Path_Separator /= ':';
1160 -- If Path separator is not ':' then we are on a DOS based OS
1161 -- where this character is used as a drive letter separator.
1164 if BN = "." or else BN = ".." then
1167 elsif Has_Drive_Letter
1168 and then BN'Length > 2
1169 and then Characters.Handling.Is_Letter (BN (BN'First))
1170 and then BN (BN'First + 1) = ':'
1172 -- We have a DOS drive letter prefix, remove it
1174 return BN (BN'First + 2 .. BN'Last);
1179 end Check_For_Standard_Dirs;
1180 end Simple_Name_Internal;
1182 -- Start of processing for Simple_Name
1185 -- First, the invalid case
1187 if not Is_Valid_Path_Name (Name) then
1188 raise Name_Error with "invalid path name """ & Name & '"';
1191 -- Build the value to return with lower bound 1
1194 Value : constant String := Simple_Name_Internal (Name);
1195 subtype Result is String (1 .. Value'Length);
1197 return Result (Value);
1202 function Simple_Name
1203 (Directory_Entry : Directory_Entry_Type) return String is
1205 -- First, the invalid case
1207 if not Directory_Entry.Is_Valid then
1208 raise Status_Error with "invalid directory entry";
1211 -- The value to return has already be computed
1213 return To_String (Directory_Entry.Simple);
1221 function Size (Name : String) return File_Size is
1222 C_Name : String (1 .. Name'Length + 1);
1224 function C_Size (Name : Address) return Long_Integer;
1225 pragma Import (C, C_Size, "__gnat_named_file_length");
1228 -- First, the invalid case
1230 if not Is_Regular_File (Name) then
1231 raise Name_Error with "file """ & Name & """ does not exist";
1234 C_Name (1 .. Name'Length) := Name;
1235 C_Name (C_Name'Last) := ASCII.NUL;
1236 return File_Size (C_Size (C_Name'Address));
1240 function Size (Directory_Entry : Directory_Entry_Type) return File_Size is
1242 -- First, the invalid case
1244 if not Directory_Entry.Is_Valid then
1245 raise Status_Error with "invalid directory entry";
1248 -- The value to return has already be computed
1250 return Size (To_String (Directory_Entry.Full));
1258 procedure Start_Search
1259 (Search : in out Search_Type;
1262 Filter : Filter_Type := (others => True))
1264 function opendir (file_name : String) return DIRs;
1265 pragma Import (C, opendir, "__gnat_opendir");
1267 C_File_Name : constant String := Directory & ASCII.NUL;
1269 Dir : Dir_Type_Value;
1272 -- First, the invalid case Name_Error
1274 if not Is_Directory (Directory) then
1275 raise Name_Error with
1276 "unknown directory """ & Simple_Name (Directory) & '"';
1279 -- Check the pattern
1285 Case_Sensitive => Is_Path_Name_Case_Sensitive);
1287 when Error_In_Regexp =>
1288 Free (Search.Value);
1289 raise Name_Error with "invalid pattern """ & Pattern & '"';
1292 Dir := Dir_Type_Value (opendir (C_File_Name));
1294 if Dir = No_Dir then
1295 raise Use_Error with
1296 "unreadable directory """ & Simple_Name (Directory) & '"';
1299 -- If needed, finalize Search
1303 -- Allocate the default data
1305 Search.Value := new Search_Data;
1307 -- Initialize some Search components
1309 Search.Value.Filter := Filter;
1310 Search.Value.Name := To_Unbounded_String (Full_Name (Directory));
1311 Search.Value.Pattern := Pat;
1312 Search.Value.Dir := Dir;
1313 Search.Value.Is_Valid := True;
1316 end Ada.Directories;