Merge -r 127928:132243 from trunk
[official-gcc.git] / gcc / ada / a-direct.adb
blob5c5c784eb4fb22aa0e63cd463d962760b67d3ab9
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME COMPONENTS --
4 -- --
5 -- A D A . D I R E C T O R I E S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2004-2007, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
28 -- --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
31 -- --
32 ------------------------------------------------------------------------------
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;
48 with System;
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
57 -- opendir routine.
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
69 Max_Path : Integer;
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;
76 Pattern : Regexp;
77 Filter : Filter_Type;
78 Dir : Dir_Type_Value := No_Dir;
79 Entry_Fetched : Boolean := False;
80 Dir_Entry : Directory_Entry_Type;
81 end record;
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
101 ---------------
102 -- Base_Name --
103 ---------------
105 function Base_Name (Name : String) return String is
106 Simple : String := Simple_Name (Name);
107 -- Simple'First is guaranteed to be 1
109 begin
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);
119 end if;
120 end loop;
122 -- If there is no dot, return the complete file name
124 return Simple;
125 end Base_Name;
127 -----------
128 -- Close --
129 -----------
131 procedure Close (Dir : Dir_Type_Value) is
132 Discard : Integer;
133 pragma Warnings (Off, Discard);
135 function closedir (directory : DIRs) return Integer;
136 pragma Import (C, closedir, "__gnat_closedir");
138 begin
139 Discard := closedir (DIRs (Dir));
140 end Close;
142 -------------
143 -- Compose --
144 -------------
146 function Compose
147 (Containing_Directory : String := "";
148 Name : String;
149 Extension : String := "") return String
151 Result : String (1 .. Containing_Directory'Length +
152 Name'Length + Extension'Length + 2);
153 Last : Natural;
155 begin
156 -- First, deal with the invalid cases
158 if Containing_Directory /= ""
159 and then not Is_Valid_Path_Name (Containing_Directory)
160 then
161 raise Name_Error;
163 elsif
164 Extension'Length = 0 and then (not Is_Valid_Simple_Name (Name))
165 then
166 raise Name_Error;
168 elsif Extension'Length /= 0
169 and then not Is_Valid_Simple_Name (Name & '.' & Extension)
170 then
171 raise Name_Error;
173 -- This is not an invalid case so build the path name
175 else
176 Last := Containing_Directory'Length;
177 Result (1 .. Last) := Containing_Directory;
179 -- Add a directory separator if needed
181 if Last /= 0 and then Result (Last) /= Dir_Separator then
182 Last := Last + 1;
183 Result (Last) := Dir_Separator;
184 end if;
186 -- Add the file name
188 Result (Last + 1 .. Last + Name'Length) := Name;
189 Last := Last + Name'Length;
191 -- If extension was specified, add dot followed by this extension
193 if Extension'Length /= 0 then
194 Last := Last + 1;
195 Result (Last) := '.';
196 Result (Last + 1 .. Last + Extension'Length) := Extension;
197 Last := Last + Extension'Length;
198 end if;
200 To_Lower_If_Case_Insensitive (Result (1 .. Last));
201 return Result (1 .. Last);
202 end if;
203 end Compose;
205 --------------------------
206 -- Containing_Directory --
207 --------------------------
209 function Containing_Directory (Name : String) return String is
210 begin
211 -- First, the invalid case
213 if not Is_Valid_Path_Name (Name) then
214 raise Name_Error;
216 else
217 declare
218 Norm : constant String := Normalize_Pathname (Name);
219 Last_DS : constant Natural :=
220 Strings.Fixed.Index
221 (Name, Dir_Seps, Going => Strings.Backward);
223 begin
224 if Last_DS = 0 then
226 -- There is no directory separator, returns current working
227 -- directory.
229 return Current_Directory;
231 -- If Name indicates a root directory, raise Use_Error, because
232 -- it has no containing directory.
234 elsif Norm = "/"
235 or else
236 (Windows
237 and then
238 (Norm = "\"
239 or else
240 (Norm'Length = 3
241 and then Norm (Norm'Last - 1 .. Norm'Last) = ":\"
242 and then (Norm (Norm'First) in 'a' .. 'z'
243 or else Norm (Norm'First) in 'A' .. 'Z'))))
244 then
245 raise Use_Error;
247 else
248 declare
249 Last : Positive := Last_DS - Name'First + 1;
250 Result : String (1 .. Last);
252 begin
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.
259 while Last > 1 loop
260 exit when
261 Result (Last) /= '/'
262 and then
263 Result (Last) /= Directory_Separator;
265 exit when Windows
266 and then Last = 3
267 and then Result (2) = ':'
268 and then
269 (Result (1) in 'A' .. 'Z'
270 or else
271 Result (1) in 'a' .. 'z');
273 Last := Last - 1;
274 end loop;
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
282 -- directory.
284 elsif Last = 2 and then Result (1 .. 2) = ".." then
285 return Containing_Directory (Current_Directory);
287 else
288 To_Lower_If_Case_Insensitive (Result (1 .. Last));
289 return Result (1 .. Last);
290 end if;
291 end;
292 end if;
293 end;
294 end if;
295 end Containing_Directory;
297 ---------------
298 -- Copy_File --
299 ---------------
301 procedure Copy_File
302 (Source_Name : String;
303 Target_Name : String;
304 Form : String := "")
306 pragma Unreferenced (Form);
307 Success : Boolean;
309 begin
310 -- First, the invalid cases
312 if not Is_Valid_Path_Name (Source_Name)
313 or else not Is_Valid_Path_Name (Target_Name)
314 or else not Is_Regular_File (Source_Name)
315 then
316 raise Name_Error;
318 elsif Is_Directory (Target_Name) then
319 raise Use_Error;
321 else
322 -- The implementation uses System.OS_Lib.Copy_File, with parameters
323 -- suitable for all platforms.
325 Copy_File (Source_Name, Target_Name, Success, Overwrite, None);
327 if not Success then
328 raise Use_Error;
329 end if;
330 end if;
331 end Copy_File;
333 ----------------------
334 -- Create_Directory --
335 ----------------------
337 procedure Create_Directory
338 (New_Directory : String;
339 Form : String := "")
341 pragma Unreferenced (Form);
343 C_Dir_Name : constant String := New_Directory & ASCII.NUL;
345 function mkdir (Dir_Name : String) return Integer;
346 pragma Import (C, mkdir, "__gnat_mkdir");
348 begin
349 -- First, the invalid case
351 if not Is_Valid_Path_Name (New_Directory) then
352 raise Name_Error;
354 else
355 if mkdir (C_Dir_Name) /= 0 then
356 raise Use_Error;
357 end if;
358 end if;
359 end Create_Directory;
361 -----------------
362 -- Create_Path --
363 -----------------
365 procedure Create_Path
366 (New_Directory : String;
367 Form : String := "")
369 pragma Unreferenced (Form);
371 New_Dir : String (1 .. New_Directory'Length + 1);
372 Last : Positive := 1;
374 begin
375 -- First, the invalid case
377 if not Is_Valid_Path_Name (New_Directory) then
378 raise Name_Error;
380 else
381 -- Build New_Dir with a directory separator at the end, so that the
382 -- complete path will be found in the loop below.
384 New_Dir (1 .. New_Directory'Length) := New_Directory;
385 New_Dir (New_Dir'Last) := Directory_Separator;
387 -- Create, if necessary, each directory in the path
389 for J in 2 .. New_Dir'Last loop
391 -- Look for the end of an intermediate directory
393 if New_Dir (J) /= Dir_Separator and then
394 New_Dir (J) /= '/'
395 then
396 Last := J;
398 -- We have found a new intermediate directory each time we find
399 -- a first directory separator.
401 elsif New_Dir (J - 1) /= Dir_Separator and then
402 New_Dir (J - 1) /= '/'
403 then
405 -- No need to create the directory if it already exists
407 if Is_Directory (New_Dir (1 .. Last)) then
408 null;
410 -- It is an error if a file with such a name already exists
412 elsif Is_Regular_File (New_Dir (1 .. Last)) then
413 raise Use_Error;
415 else
416 Create_Directory (New_Directory => New_Dir (1 .. Last));
417 end if;
418 end if;
419 end loop;
420 end if;
421 end Create_Path;
423 -----------------------
424 -- Current_Directory --
425 -----------------------
427 function Current_Directory return String is
428 Path_Len : Natural := Max_Path;
429 Buffer : String (1 .. 1 + Max_Path + 1);
431 procedure Local_Get_Current_Dir
432 (Dir : System.Address;
433 Length : System.Address);
434 pragma Import (C, Local_Get_Current_Dir, "__gnat_get_current_dir");
436 begin
437 Local_Get_Current_Dir (Buffer'Address, Path_Len'Address);
439 declare
440 Cur : String := Normalize_Pathname (Buffer (1 .. Path_Len));
442 begin
443 To_Lower_If_Case_Insensitive (Cur);
445 if Cur'Length > 1 and then Cur (Cur'Last) = Dir_Separator then
446 return Cur (1 .. Cur'Last - 1);
447 else
448 return Cur;
449 end if;
450 end;
451 end Current_Directory;
453 ----------------------
454 -- Delete_Directory --
455 ----------------------
457 procedure Delete_Directory (Directory : String) is
458 begin
459 -- First, the invalid cases
461 if not Is_Valid_Path_Name (Directory) then
462 raise Name_Error;
464 elsif not Is_Directory (Directory) then
465 raise Name_Error;
467 else
468 declare
469 C_Dir_Name : constant String := Directory & ASCII.NUL;
470 begin
471 rmdir (C_Dir_Name);
473 if System.OS_Lib.Is_Directory (Directory) then
474 raise Use_Error;
475 end if;
476 end;
477 end if;
478 end Delete_Directory;
480 -----------------
481 -- Delete_File --
482 -----------------
484 procedure Delete_File (Name : String) is
485 Success : Boolean;
487 begin
488 -- First, the invalid cases
490 if not Is_Valid_Path_Name (Name) then
491 raise Name_Error;
493 elsif not Is_Regular_File (Name) then
494 raise Name_Error;
496 else
497 -- The implementation uses System.OS_Lib.Delete_File
499 Delete_File (Name, Success);
501 if not Success then
502 raise Use_Error;
503 end if;
504 end if;
505 end Delete_File;
507 -----------------
508 -- Delete_Tree --
509 -----------------
511 procedure Delete_Tree (Directory : String) is
512 Current_Dir : constant String := Current_Directory;
513 Search : Search_Type;
514 Dir_Ent : Directory_Entry_Type;
515 begin
516 -- First, the invalid cases
518 if not Is_Valid_Path_Name (Directory) then
519 raise Name_Error;
521 elsif not Is_Directory (Directory) then
522 raise Name_Error;
524 else
525 Set_Directory (Directory);
526 Start_Search (Search, Directory => ".", Pattern => "");
528 while More_Entries (Search) loop
529 Get_Next_Entry (Search, Dir_Ent);
531 declare
532 File_Name : constant String := Simple_Name (Dir_Ent);
534 begin
535 if System.OS_Lib.Is_Directory (File_Name) then
536 if File_Name /= "." and then File_Name /= ".." then
537 Delete_Tree (File_Name);
538 end if;
540 else
541 Delete_File (File_Name);
542 end if;
543 end;
544 end loop;
546 Set_Directory (Current_Dir);
547 End_Search (Search);
549 declare
550 C_Dir_Name : constant String := Directory & ASCII.NUL;
552 begin
553 rmdir (C_Dir_Name);
555 if System.OS_Lib.Is_Directory (Directory) then
556 raise Use_Error;
557 end if;
558 end;
559 end if;
560 end Delete_Tree;
562 ------------
563 -- Exists --
564 ------------
566 function Exists (Name : String) return Boolean is
567 begin
568 -- First, the invalid case
570 if not Is_Valid_Path_Name (Name) then
571 raise Name_Error;
573 else
574 -- The implementation is in File_Exists
576 return File_Exists (Name);
577 end if;
578 end Exists;
580 ---------------
581 -- Extension --
582 ---------------
584 function Extension (Name : String) return String is
585 begin
586 -- First, the invalid case
588 if not Is_Valid_Path_Name (Name) then
589 raise Name_Error;
591 else
592 -- Look for first dot that is not followed by a directory separator
594 for Pos in reverse Name'Range loop
596 -- If a directory separator is found before a dot, there is no
597 -- extension.
599 if Name (Pos) = Dir_Separator then
600 return Empty_String;
602 elsif Name (Pos) = '.' then
604 -- We found a dot, build the return value with lower bound 1
606 declare
607 subtype Result_Type is String (1 .. Name'Last - Pos);
608 begin
609 return Result_Type (Name (Pos + 1 .. Name'Last));
610 end;
611 end if;
612 end loop;
614 -- No dot were found, there is no extension
616 return Empty_String;
617 end if;
618 end Extension;
620 ----------------------
621 -- Fetch_Next_Entry --
622 ----------------------
624 procedure Fetch_Next_Entry (Search : Search_Type) is
625 Name : String (1 .. 255);
626 Last : Natural;
628 Kind : File_Kind := Ordinary_File;
629 -- Initialized to avoid a compilation warning
631 Filename_Addr : System.Address;
632 Filename_Len : aliased Integer;
634 Buffer : array (0 .. Filename_Max + 12) of Character;
635 -- 12 is the size of the dirent structure (see dirent.h), without the
636 -- field for the filename.
638 function readdir_gnat
639 (Directory : System.Address;
640 Buffer : System.Address;
641 Last : not null access Integer) return System.Address;
642 pragma Import (C, readdir_gnat, "__gnat_readdir");
644 use System;
646 begin
647 -- Search.Value.Is_Valid is always True when Fetch_Next_Entry is called
649 loop
650 Filename_Addr :=
651 readdir_gnat
652 (System.Address (Search.Value.Dir),
653 Buffer'Address,
654 Filename_Len'Access);
656 -- If no matching entry is found, set Is_Valid to False
658 if Filename_Addr = System.Null_Address then
659 Search.Value.Is_Valid := False;
660 exit;
661 end if;
663 declare
664 subtype Path_String is String (1 .. Filename_Len);
665 type Path_String_Access is access Path_String;
667 function Address_To_Access is new
668 Ada.Unchecked_Conversion
669 (Source => Address,
670 Target => Path_String_Access);
672 Path_Access : constant Path_String_Access :=
673 Address_To_Access (Filename_Addr);
675 begin
676 Last := Filename_Len;
677 Name (1 .. Last) := Path_Access.all;
678 end;
680 -- Check if the entry matches the pattern
682 if Match (Name (1 .. Last), Search.Value.Pattern) then
683 declare
684 Full_Name : constant String :=
685 Compose
686 (To_String
687 (Search.Value.Name), Name (1 .. Last));
688 Found : Boolean := False;
690 begin
691 if File_Exists (Full_Name) then
693 -- Now check if the file kind matches the filter
695 if Is_Regular_File (Full_Name) then
696 if Search.Value.Filter (Ordinary_File) then
697 Kind := Ordinary_File;
698 Found := True;
699 end if;
701 elsif Is_Directory (Full_Name) then
702 if Search.Value.Filter (Directory) then
703 Kind := Directory;
704 Found := True;
705 end if;
707 elsif Search.Value.Filter (Special_File) then
708 Kind := Special_File;
709 Found := True;
710 end if;
712 -- If it does, update Search and return
714 if Found then
715 Search.Value.Entry_Fetched := True;
716 Search.Value.Dir_Entry :=
717 (Is_Valid => True,
718 Simple => To_Unbounded_String (Name (1 .. Last)),
719 Full => To_Unbounded_String (Full_Name),
720 Kind => Kind);
721 exit;
722 end if;
723 end if;
724 end;
725 end if;
726 end loop;
727 end Fetch_Next_Entry;
729 -----------------
730 -- File_Exists --
731 -----------------
733 function File_Exists (Name : String) return Boolean is
734 function C_File_Exists (A : System.Address) return Integer;
735 pragma Import (C, C_File_Exists, "__gnat_file_exists");
737 C_Name : String (1 .. Name'Length + 1);
739 begin
740 C_Name (1 .. Name'Length) := Name;
741 C_Name (C_Name'Last) := ASCII.NUL;
742 return C_File_Exists (C_Name (1)'Address) = 1;
743 end File_Exists;
745 --------------
746 -- Finalize --
747 --------------
749 procedure Finalize (Search : in out Search_Type) is
750 begin
751 if Search.Value /= null then
753 -- Close the directory, if one is open
755 if Search.Value.Dir /= No_Dir then
756 Close (Search.Value.Dir);
757 end if;
759 Free (Search.Value);
760 end if;
761 end Finalize;
763 ---------------
764 -- Full_Name --
765 ---------------
767 function Full_Name (Name : String) return String is
768 begin
769 -- First, the invalid case
771 if not Is_Valid_Path_Name (Name) then
772 raise Name_Error;
774 else
775 -- Build the return value with lower bound 1
777 -- Use System.OS_Lib.Normalize_Pathname
779 declare
780 Value : String := Normalize_Pathname (Name);
781 subtype Result is String (1 .. Value'Length);
782 begin
783 To_Lower_If_Case_Insensitive (Value);
784 return Result (Value);
785 end;
786 end if;
787 end Full_Name;
789 function Full_Name (Directory_Entry : Directory_Entry_Type) return String is
790 begin
791 -- First, the invalid case
793 if not Directory_Entry.Is_Valid then
794 raise Status_Error;
796 else
797 -- The value to return has already been computed
799 return To_String (Directory_Entry.Full);
800 end if;
801 end Full_Name;
803 --------------------
804 -- Get_Next_Entry --
805 --------------------
807 procedure Get_Next_Entry
808 (Search : in out Search_Type;
809 Directory_Entry : out Directory_Entry_Type)
811 begin
812 -- First, the invalid case
814 if Search.Value = null or else not Search.Value.Is_Valid then
815 raise Status_Error;
816 end if;
818 -- Fetch the next entry, if needed
820 if not Search.Value.Entry_Fetched then
821 Fetch_Next_Entry (Search);
822 end if;
824 -- It is an error if no valid entry is found
826 if not Search.Value.Is_Valid then
827 raise Status_Error;
829 else
830 -- Reset Entry_Fatched and return the entry
832 Search.Value.Entry_Fetched := False;
833 Directory_Entry := Search.Value.Dir_Entry;
834 end if;
835 end Get_Next_Entry;
837 ----------
838 -- Kind --
839 ----------
841 function Kind (Name : String) return File_Kind is
842 begin
843 -- First, the invalid case
845 if not File_Exists (Name) then
846 raise Name_Error;
848 elsif Is_Regular_File (Name) then
849 return Ordinary_File;
851 elsif Is_Directory (Name) then
852 return Directory;
854 else
855 return Special_File;
856 end if;
857 end Kind;
859 function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind is
860 begin
861 -- First, the invalid case
863 if not Directory_Entry.Is_Valid then
864 raise Status_Error;
866 else
867 -- The value to return has already be computed
869 return Directory_Entry.Kind;
870 end if;
871 end Kind;
873 -----------------------
874 -- Modification_Time --
875 -----------------------
877 function Modification_Time (Name : String) return Time is
878 Date : OS_Time;
879 Year : Year_Type;
880 Month : Month_Type;
881 Day : Day_Type;
882 Hour : Hour_Type;
883 Minute : Minute_Type;
884 Second : Second_Type;
885 Result : Time;
887 begin
888 -- First, the invalid cases
890 if not (Is_Regular_File (Name) or else Is_Directory (Name)) then
891 raise Name_Error;
893 else
894 Date := File_Time_Stamp (Name);
896 -- Break down the time stamp into its constituents relative to GMT.
897 -- This version of Split does not recognize leap seconds or buffer
898 -- space for time zone processing.
900 GM_Split (Date, Year, Month, Day, Hour, Minute, Second);
902 -- On OpenVMS, the resulting time value must be in the local time
903 -- zone. Ada.Calendar.Time_Of is exactly what we need. Note that
904 -- in both cases, the sub seconds are set to zero (0.0) because the
905 -- time stamp does not store them in its value.
907 if OpenVMS then
908 Result :=
909 Ada.Calendar.Time_Of
910 (Year, Month, Day, Seconds_Of (Hour, Minute, Second, 0.0));
912 -- On Unix and Windows, the result must be in GMT. Ada.Calendar.
913 -- Formatting.Time_Of with default time zone of zero (0) is the
914 -- routine of choice.
916 else
917 Result := Time_Of (Year, Month, Day, Hour, Minute, Second, 0.0);
918 end if;
920 return Result;
921 end if;
922 end Modification_Time;
924 function Modification_Time
925 (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time
927 begin
928 -- First, the invalid case
930 if not Directory_Entry.Is_Valid then
931 raise Status_Error;
933 else
934 -- The value to return has already be computed
936 return Modification_Time (To_String (Directory_Entry.Full));
937 end if;
938 end Modification_Time;
940 ------------------
941 -- More_Entries --
942 ------------------
944 function More_Entries (Search : Search_Type) return Boolean is
945 begin
946 if Search.Value = null then
947 return False;
949 elsif Search.Value.Is_Valid then
951 -- Fetch the next entry, if needed
953 if not Search.Value.Entry_Fetched then
954 Fetch_Next_Entry (Search);
955 end if;
956 end if;
958 return Search.Value.Is_Valid;
959 end More_Entries;
961 ------------
962 -- Rename --
963 ------------
965 procedure Rename (Old_Name, New_Name : String) is
966 Success : Boolean;
968 begin
969 -- First, the invalid cases
971 if not Is_Valid_Path_Name (Old_Name)
972 or else not Is_Valid_Path_Name (New_Name)
973 or else (not Is_Regular_File (Old_Name)
974 and then not Is_Directory (Old_Name))
975 then
976 raise Name_Error;
978 elsif Is_Regular_File (New_Name) or Is_Directory (New_Name) then
979 raise Use_Error;
981 else
982 -- The implementation uses System.OS_Lib.Rename_File
984 Rename_File (Old_Name, New_Name, Success);
986 if not Success then
987 raise Use_Error;
988 end if;
989 end if;
990 end Rename;
992 ------------
993 -- Search --
994 ------------
996 procedure Search
997 (Directory : String;
998 Pattern : String;
999 Filter : Filter_Type := (others => True);
1000 Process : not null access procedure
1001 (Directory_Entry : Directory_Entry_Type))
1003 Srch : Search_Type;
1004 Directory_Entry : Directory_Entry_Type;
1006 begin
1007 Start_Search (Srch, Directory, Pattern, Filter);
1009 while More_Entries (Srch) loop
1010 Get_Next_Entry (Srch, Directory_Entry);
1011 Process (Directory_Entry);
1012 end loop;
1014 End_Search (Srch);
1015 end Search;
1017 -------------------
1018 -- Set_Directory --
1019 -------------------
1021 procedure Set_Directory (Directory : String) is
1022 C_Dir_Name : constant String := Directory & ASCII.NUL;
1024 function chdir (Dir_Name : String) return Integer;
1025 pragma Import (C, chdir, "chdir");
1027 begin
1028 if chdir (C_Dir_Name) /= 0 then
1029 raise Name_Error;
1030 end if;
1031 end Set_Directory;
1033 -----------------
1034 -- Simple_Name --
1035 -----------------
1037 function Simple_Name (Name : String) return String is
1039 function Simple_Name_CI (Path : String) return String;
1040 -- This function does the job. The difference between Simple_Name_CI
1041 -- and Simple_Name (the parent function) is that the former is case
1042 -- sensitive, while the latter is not. Path and Suffix are adjusted
1043 -- appropriately before calling Simple_Name_CI under platforms where
1044 -- the file system is not case sensitive.
1046 --------------------
1047 -- Simple_Name_CI --
1048 --------------------
1050 function Simple_Name_CI (Path : String) return String is
1051 Cut_Start : Natural :=
1052 Strings.Fixed.Index
1053 (Path, Dir_Seps, Going => Strings.Backward);
1054 Cut_End : Natural;
1056 begin
1057 -- Cut_Start point to the first simple name character
1059 if Cut_Start = 0 then
1060 Cut_Start := Path'First;
1062 else
1063 Cut_Start := Cut_Start + 1;
1064 end if;
1066 -- Cut_End point to the last simple name character
1068 Cut_End := Path'Last;
1070 Check_For_Standard_Dirs : declare
1071 Offset : constant Integer := Path'First - Name'First;
1072 BN : constant String :=
1073 Name (Cut_Start - Offset .. Cut_End - Offset);
1074 -- Here we use Simple_Name.Name to keep the original casing
1076 Has_Drive_Letter : constant Boolean :=
1077 System.OS_Lib.Path_Separator /= ':';
1078 -- If Path separator is not ':' then we are on a DOS based OS
1079 -- where this character is used as a drive letter separator.
1081 begin
1082 if BN = "." or else BN = ".." then
1083 return "";
1085 elsif Has_Drive_Letter
1086 and then BN'Length > 2
1087 and then Characters.Handling.Is_Letter (BN (BN'First))
1088 and then BN (BN'First + 1) = ':'
1089 then
1090 -- We have a DOS drive letter prefix, remove it
1092 return BN (BN'First + 2 .. BN'Last);
1094 else
1095 return BN;
1096 end if;
1097 end Check_For_Standard_Dirs;
1098 end Simple_Name_CI;
1100 -- Start of processing for Simple_Name
1102 begin
1103 -- First, the invalid case
1105 if not Is_Valid_Path_Name (Name) then
1106 raise Name_Error;
1108 else
1109 -- Build the value to return with lower bound 1
1111 if Is_Path_Name_Case_Sensitive then
1112 declare
1113 Value : constant String := Simple_Name_CI (Name);
1114 subtype Result is String (1 .. Value'Length);
1115 begin
1116 return Result (Value);
1117 end;
1119 else
1120 declare
1121 Value : constant String :=
1122 Simple_Name_CI (Characters.Handling.To_Lower (Name));
1123 subtype Result is String (1 .. Value'Length);
1124 begin
1125 return Result (Value);
1126 end;
1127 end if;
1128 end if;
1129 end Simple_Name;
1131 function Simple_Name
1132 (Directory_Entry : Directory_Entry_Type) return String
1134 begin
1135 -- First, the invalid case
1137 if not Directory_Entry.Is_Valid then
1138 raise Status_Error;
1140 else
1141 -- The value to return has already be computed
1143 return To_String (Directory_Entry.Simple);
1144 end if;
1145 end Simple_Name;
1147 ----------
1148 -- Size --
1149 ----------
1151 function Size (Name : String) return File_Size is
1152 C_Name : String (1 .. Name'Length + 1);
1154 function C_Size (Name : System.Address) return Long_Integer;
1155 pragma Import (C, C_Size, "__gnat_named_file_length");
1157 begin
1158 -- First, the invalid case
1160 if not Is_Regular_File (Name) then
1161 raise Name_Error;
1163 else
1164 C_Name (1 .. Name'Length) := Name;
1165 C_Name (C_Name'Last) := ASCII.NUL;
1166 return File_Size (C_Size (C_Name'Address));
1167 end if;
1168 end Size;
1170 function Size (Directory_Entry : Directory_Entry_Type) return File_Size is
1171 begin
1172 -- First, the invalid case
1174 if not Directory_Entry.Is_Valid then
1175 raise Status_Error;
1177 else
1178 -- The value to return has already be computed
1180 return Size (To_String (Directory_Entry.Full));
1181 end if;
1182 end Size;
1184 ------------------
1185 -- Start_Search --
1186 ------------------
1188 procedure Start_Search
1189 (Search : in out Search_Type;
1190 Directory : String;
1191 Pattern : String;
1192 Filter : Filter_Type := (others => True))
1194 function opendir (file_name : String) return DIRs;
1195 pragma Import (C, opendir, "__gnat_opendir");
1197 C_File_Name : constant String := Directory & ASCII.NUL;
1199 begin
1200 -- First, the invalid case
1202 if not Is_Directory (Directory) then
1203 raise Name_Error;
1204 end if;
1206 -- If needed, finalize Search
1208 Finalize (Search);
1210 -- Allocate the default data
1212 Search.Value := new Search_Data;
1214 begin
1215 -- Check the pattern
1217 Search.Value.Pattern := Compile (Pattern, Glob => True);
1219 exception
1220 when Error_In_Regexp =>
1221 Free (Search.Value);
1222 raise Name_Error;
1223 end;
1225 -- Initialize some Search components
1227 Search.Value.Filter := Filter;
1228 Search.Value.Name := To_Unbounded_String (Full_Name (Directory));
1229 Search.Value.Dir := Dir_Type_Value (opendir (C_File_Name));
1230 Search.Value.Is_Valid := True;
1231 end Start_Search;
1233 ----------------------------------
1234 -- To_Lower_If_Case_Insensitive --
1235 ----------------------------------
1237 procedure To_Lower_If_Case_Insensitive (S : in out String) is
1238 begin
1239 if not Is_Path_Name_Case_Sensitive then
1240 for J in S'Range loop
1241 S (J) := To_Lower (S (J));
1242 end loop;
1243 end if;
1244 end To_Lower_If_Case_Insensitive;
1246 end Ada.Directories;