1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- ADA.DIRECTORIES.HIERARCHICAL_FILE_NAMES --
9 -- Copyright (C) 2004-2024, 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 -- In particular, you can freely distribute your programs built with the --
23 -- GNAT Pro compiler, including any required library run-time units, using --
24 -- any licensing terms of your choosing. See the AdaCore Software License --
25 -- for full details. --
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
.Characters
.Handling
; use Ada
.Characters
.Handling
;
33 with Ada
.Directories
.Validity
; use Ada
.Directories
.Validity
;
34 with Ada
.Strings
.Fixed
; use Ada
.Strings
.Fixed
;
35 with System
; use System
;
37 package body Ada
.Directories
.Hierarchical_File_Names
is
39 Dir_Separator
: constant Character;
40 pragma Import
(C
, Dir_Separator
, "__gnat_dir_separator");
41 -- Running system default directory separator
47 function Equivalent_File_Names
51 -- Perform an OS-independent comparison between two file paths
53 function Is_Absolute_Path
(Name
: String) return Boolean;
54 -- Returns True if Name is an absolute path name, i.e. it designates a
55 -- file or directory absolutely rather than relative to another directory.
57 ---------------------------
58 -- Equivalent_File_Names --
59 ---------------------------
61 function Equivalent_File_Names
67 -- Check the validity of the input paths
69 if not Is_Valid_Path_Name
(Left
)
70 or else not Is_Valid_Path_Name
(Right
)
75 -- Normalize the paths by removing any trailing directory separators and
76 -- perform the comparison.
79 Normal_Left
: constant String :=
80 (if Index
(Left
, Dir_Separator
& "", Strings
.Backward
) = Left
'Last
81 and then not Is_Root_Directory_Name
(Left
)
83 Left
(Left
'First .. Left
'Last - 1)
87 Normal_Right
: constant String :=
88 (if Index
(Right
, Dir_Separator
& "", Strings
.Backward
) = Right
'Last
89 and then not Is_Root_Directory_Name
(Right
)
91 Right
(Right
'First .. Right
'Last - 1)
95 -- Within Windows we assume case insensitivity
98 return Normal_Left
= Normal_Right
;
101 -- Otherwise do a straight comparison
103 return To_Lower
(Normal_Left
) = To_Lower
(Normal_Right
);
105 end Equivalent_File_Names
;
107 ----------------------
108 -- Is_Absolute_Path --
109 ----------------------
111 function Is_Absolute_Path
(Name
: String) return Boolean is
112 function Is_Absolute_Path
114 Length
: Integer) return Integer;
115 pragma Import
(C
, Is_Absolute_Path
, "__gnat_is_absolute_path");
117 return Is_Absolute_Path
(Name
'Address, Name
'Length) /= 0;
118 end Is_Absolute_Path
;
124 function Is_Simple_Name
(Name
: String) return Boolean is
126 -- Verify the file path name is valid and that it is not a root
128 if not Is_Valid_Path_Name
(Name
)
129 or else Is_Root_Directory_Name
(Name
)
134 -- Check for the special paths "." and "..", which are considered simple
136 if Is_Parent_Directory_Name
(Name
)
137 or else Is_Current_Directory_Name
(Name
)
142 -- Perform a comparison with the calculated simple path name
144 return Equivalent_File_Names
(Simple_Name
(Name
), Name
);
147 ----------------------------
148 -- Is_Root_Directory_Name --
149 ----------------------------
151 function Is_Root_Directory_Name
(Name
: String) return Boolean is
153 -- Check if the path name is a root directory by looking for a slash in
154 -- the general case, and a drive letter in the case of Windows.
163 and then Name (Name'Last - 1) = ':'
164 and then Name (Name'Last) in '/' | '\'
165 and then (Name (Name'First) in 'a' .. 'z'
167 Name (Name'First) in 'A' .. 'Z'))
170 and then Name (Name'Last) = ':'
171 and then (Name (Name'First) in 'a' .. 'z'
173 Name (Name'First) in 'A' .. 'Z'))));
174 end Is_Root_Directory_Name;
176 ------------------------------
177 -- Is_Parent_Directory_Name --
178 ------------------------------
180 function Is_Parent_Directory_Name (Name : String) return Boolean is
183 end Is_Parent_Directory_Name;
185 -------------------------------
186 -- Is_Current_Directory_Name --
187 -------------------------------
189 function Is_Current_Directory_Name (Name : String) return Boolean is
192 end Is_Current_Directory_Name;
198 function Is_Full_Name (Name : String) return Boolean is
200 return Equivalent_File_Names (Full_Name (Name), Name);
203 ----------------------
204 -- Is_Relative_Name --
205 ----------------------
207 function Is_Relative_Name (Name : String) return Boolean is
209 return not Is_Absolute_Path (Name)
210 and then Is_Valid_Path_Name (Name);
211 end Is_Relative_Name;
213 -----------------------
214 -- Initial_Directory --
215 -----------------------
217 function Initial_Directory (Name : String) return String is
218 Start : constant Integer := Index (Name, Dir_Separator & "");
222 if not Is_Valid_Path_Name (Name) then
223 raise Name_Error with "invalid path name
""" & Name & '"';
226 -- When there is no starting directory separator or the path name is a
227 -- root directory then the path name is already simple - so return it.
229 if Is_Root_Directory_Name (Name) or else Start = 0 then
233 -- When the initial directory of the path name is a root directory then
234 -- the starting directory separator is part of the result so we must
235 -- return it in the slice.
237 if Is_Root_Directory_Name (Name (Name'First .. Start)) then
238 return Name (Name'First .. Start);
241 -- Otherwise we grab a slice up to the starting directory separator
243 return Name (Name'First .. Start - 1);
244 end Initial_Directory;
250 function Relative_Name (Name : String) return String is
252 -- We cannot derive a relative name if Name does not exist
254 if not Is_Relative_Name (Name)
255 and then not Is_Valid_Path_Name (Name)
257 raise Name_Error with "invalid relative path name """ & Name & '"';
260 -- Name only has a single part and thus cannot be made relative
262 if Is_Simple_Name (Name)
263 or else Is_Root_Directory_Name (Name)
265 raise Name_Error with
266 "relative path name
""" & Name & """ is composed
of a single part
";
269 -- Trim the input according to the initial directory and maintain proper
270 -- directory separation due to the fact that root directories may
271 -- contain separators.
274 Init_Dir : constant String := Initial_Directory (Name);
276 if Init_Dir (Init_Dir'Last) = Dir_Separator then
277 return Name (Name'First + Init_Dir'Length .. Name'Last);
280 return Name (Name'First + Init_Dir'Length + 1 .. Name'Last);
289 (Directory : String := "";
290 Relative_Name : String;
291 Extension : String := "") return String
293 -- Append a directory separator if none is present
295 Separated_Dir : constant String :=
296 (if Directory = "" then ""
297 elsif Directory (Directory'Last) = Dir_Separator then Directory
298 else Directory & Dir_Separator);
300 -- Check that relative name is valid
302 if not Is_Relative_Name (Relative_Name) then
303 raise Name_Error with
304 "invalid relative path name
""" & Relative_Name & '"';
307 -- Check that directory is valid
309 if Separated_Dir /= ""
310 and then not Is_Valid_Path_Name (Separated_Dir & Relative_Name)
312 raise Name_Error with
313 "invalid path composition """ & Separated_Dir & Relative_Name & '"';
316 -- Check that the extension is valid
319 and then not Is_Valid_Path_Name
320 (Separated_Dir & Relative_Name & Extension)
322 raise Name_Error with
323 "invalid path composition
"""
324 & Separated_Dir & Relative_Name & Extension & '"';
327 -- Concatenate the result
329 return Separated_Dir & Relative_Name & Extension;
332 end Ada.Directories.Hierarchical_File_Names;