/src/lib/io/filesystem/path_name.e
Specman e | 415 lines | 290 code | 41 blank | 84 comment | 9 complexity | d569fad87d579f0d4ffd8d5d5feb96ff MD5 | raw file
1-- This file is part of a Liberty Eiffel library. 2-- See the full copyright at the end. 3-- 4deferred class PATH_NAME 5 -- Operating system path name 6 7inherit 8 PATH_JOINER 9 10feature {ANY} -- Creation/ Initialization 11 make_empty 12 -- Make a 'null' path 13 deferred 14 ensure 15 is_empty 16 end 17 18 make_root 19 -- Path to root directory (in current drive) 20 deferred 21 ensure 22 is_absolute 23 end 24 25 make_current 26 -- Path to current directory (relative). See also `to_absolute' 27 -- if you need the absolute current working directory 28 deferred 29 ensure 30 not is_absolute 31 end 32 33 make_from_string (s: STRING) 34 require 35 s /= Void 36 s.is_empty or else is_valid_path(s) 37 deferred 38 ensure 39 to_string.is_equal(s) 40 (old to_string /= s) implies to_string /= s 41 end 42 43 make_from_path_name (pn: PATH_NAME) 44 require 45 pn /= Void 46 do 47 make_empty 48 pn.join_to(Current) 49 end 50 51feature {ANY} -- Constants 52 extension_separator: CHARACTER 53 -- Character used to separate filenames from extensions 54 deferred 55 end 56 57 directory_separator: CHARACTER 58 -- Character used to separate directories 59 -- This character is forbidden in filenames 60 deferred 61 end 62 63feature {ANY} -- Access 64 to_string: STRING 65 -- String representation 66 deferred 67 ensure 68 Result /= Void 69 Result.is_empty or else is_valid_path(Result) 70 end 71 72 drive_specification: STRING 73 -- Drive specified by the current path, Void if none 74 deferred 75 ensure 76 Result /= Void implies to_string.has_prefix(Result) 77 end 78 79 count: INTEGER 80 -- Number of elements in_path 81 deferred 82 ensure 83 Result >= 0 84 end 85 86 is_empty: BOOLEAN 87 -- Path is null. Note that you can have a null absolute path 88 -- (i.e., root) or a null relative path (current directory) 89 do 90 Result := count = 0 91 ensure 92 Result = (count = 0) 93 end 94 95 last: STRING 96 -- Last component (also known as "basename") 97 require 98 not is_empty 99 deferred 100 ensure 101 Result /= Void 102 is_valid_file_name(Result) 103 end 104 105 extension: STRING 106 -- Path extension (may be empty) 107 deferred 108 ensure 109 is_suffix: to_string.has_suffix(Result) 110 is_extension: not Result.is_empty implies Result.first = extension_separator 111 is_minimal: Result.occurrences(extension_separator) <= 1 112 not Result.has(directory_separator) 113 is_empty implies Result.is_empty 114 end 115 116 is_absolute: BOOLEAN 117 -- absolute path? 118 deferred 119 end 120 121 as_absolute: like Current 122 -- Equivalent absolute path 123 do 124 Result := twin 125 Result.to_absolute 126 ensure 127 Result.is_absolute 128 Result.is_normalized 129 end 130 131 is_normalized: BOOLEAN 132 -- Has no redundant separators, or redundant up-references 133 deferred 134 end 135 136 is_valid_path (path: STRING): BOOLEAN 137 -- Does `path' represent a syntactically valid file or 138 -- directory path? The result does not imply that there 139 -- actually a file or directory with that name. This 140 -- operation does not perform any disk access. 141 require 142 path /= Void 143 deferred 144 ensure 145 path.is_equal(old path.twin) 146 end 147 148 is_valid_file_name (name: STRING): BOOLEAN 149 -- Does `path' only contain valid characters for a file? The 150 -- result does not imply that there is actually a file or 151 -- directory with that name. Not the same as `is_valid_path': 152 -- path separators (/ for unix, \ for windows, ...) are 153 -- allowed in paths, but not in file names. This operation 154 -- does not perform any disk access. 155 require 156 name /= Void 157 deferred 158 ensure 159 name.is_equal(old name.twin) 160 end 161 162 is_valid_directory: BOOLEAN 163 -- Does `Current' represent a syntactically valid directory 164 -- path? For many Systems, there may be no syntactical 165 -- difference between file paths and directory paths, in 166 -- that case `is_valid_directory' is always True. 167 deferred 168 ensure 169 Result or is_valid_file 170 end 171 172 is_valid_file: BOOLEAN 173 -- Does `Current' represent a syntactically valid directory 174 -- path? For many Systems, there may be no syntactical 175 -- difference between file paths and directory paths, in 176 -- that case `is_valid_file' is always True. 177 deferred 178 ensure 179 Result or is_valid_directory 180 end 181 182 is_file: BOOLEAN 183 -- Path points to an existing regular file? 184 do 185 Result := (create {FILE_TOOLS}).is_file(to_string) 186 end 187 188 is_directory: BOOLEAN 189 -- Path points to an existing directory? 190 do 191 Result := (create {FILE_TOOLS}).is_directory(to_string) 192 end 193 194 infix "+" (other: like Current): like Current 195 -- Join with `other' using filesystem semantics 196 require 197 other /= Void 198 do 199 Result := twin 200 Result.join(other) 201 ensure 202 -- Result is a fresh instance 203 end 204 205 infix "/" (elem: STRING): like Current 206 -- Path with `elem' inside current 207 require 208 elem /= Void 209 not elem.has(directory_separator) 210 do 211 Result := twin 212 Result.add_last(elem) 213 ensure 214 -- Result is a fresh instance 215 Result.is_empty = (is_empty and elem.is_empty) 216 Result.last.is_equal(elem) 217 end 218 219 short_name: STRING 220 deferred 221 ensure 222 Result /= Void 223 end 224 225feature {ANY} -- Operations 226 to_absolute 227 -- Transform into equivalent absolute path 228 deferred 229 ensure 230 is_absolute 231 is_normalized 232 end 233 234 normalize_case 235 -- Transform into normalized case version (equivalent), with 236 -- standard path separators 237 deferred 238 end 239 240 normalize 241 -- Normalize removing double separators, and up-references 242 deferred 243 ensure 244 is_normalized 245 old is_normalized implies to_string.is_equal(old to_string.twin) 246 end 247 248 remove_last 249 -- Remove last component of path (keep the "dirname") 250 require 251 not is_empty 252 deferred 253 ensure 254 -- (old twin).is_equal (Current / (old last)) 255 -- assertion above commented because of SE bug 256 count = old count - 1 257 end 258 259 go_up 260 -- Go up by one directory 261 deferred 262 end 263 264 add_last (elem: STRING) 265 require 266 elem /= Void 267 not elem.has(directory_separator) 268 deferred 269 ensure 270 last.is_equal(elem) 271 not_reduced: old count <= count 272 may_grow_one: count <= old count + 1 273 end 274 275 join (other: PATH_NAME) 276 -- Join with `other' using filesystem semantics 277 require 278 other /= Void 279 do 280 other.join_to(Current) 281 ensure 282 (old is_normalized) implies is_normalized 283 -- definition: to_string.is_equal (old (Current+other).to_string) 284 -- assertion above is commented out because of SE bug 285 end 286 287 join_to (other: PATH_JOINER) 288 require 289 other /= Void 290 deferred 291 end 292 293 expand_user 294 -- Replace an initial "~" or "~user" by user home directory 295 deferred 296 ensure 297 not (old to_string.twin).has_prefix(once "~") implies to_string.is_equal(old to_string.twin) 298 end 299 300 expand_variables 301 -- Replace substrings of form $name or ${name} with environment 302 -- variable values 303 local 304 p, len: INTEGER; new, varname, subst: STRING; sys: SYSTEM 305 do 306 new := once "" 307 varname := once "" 308 subst := once "" 309 len := to_string.count 310 from 311 p := 1 312 new.clear_count 313 varname.clear_count 314 until 315 p > len 316 loop 317 if to_string.item(p) = '$' then 318 varname.clear_count 319 subst.clear_count 320 subst.extend('$') 321 p := p + 1 322 if p = len then 323 elseif to_string.item(p) = '{' then 324 -- ${var} 325 subst.extend('{') 326 from 327 p := p + 1 328 until 329 p > len or else to_string.item(p) = '}' 330 loop 331 varname.extend(to_string.item(p)) 332 subst.extend(to_string.item(p)) 333 p := p + 1 334 end 335 if p <= len then 336 p := p + 1 -- skip '}' 337 subst.extend('}') 338 else 339 varname.clear_count 340 -- invalid variable 341 end 342 else 343 -- $var 344 from 345 until 346 p > len or else not to_string.item(p).is_letter_or_digit 347 loop 348 varname.extend(to_string.item(p)) 349 subst.extend(to_string.item(p)) 350 p := p + 1 351 end 352 end 353 if not varname.is_empty and then sys.get_environment_variable(varname) /= Void then 354 subst.copy(sys.get_environment_variable(varname)) 355 end 356 new.append(subst) 357 else 358 new.extend(to_string.item(p)) 359 p := p + 1 360 end 361 end 362 to_string.copy(new) 363 ensure 364 not (old to_string.twin).has('$') implies to_string.is_equal(old to_string.twin) 365 end 366 367 expand_shellouts 368 -- Replace substrings of form $(command) with execution of 369 -- shell commands 370 deferred 371 ensure 372 not to_string.has_substring("$(") implies to_string.is_equal(old to_string.twin) 373 end 374 375feature {PATH_JOINER} 376 join_up 377 do 378 go_up 379 end 380 381 end_join 382 do 383 if is_empty and then not is_absolute then 384 make_current 385 end 386 end 387 388feature {} 389 tmp: like Current 390 deferred 391 ensure 392 Result /= Void 393 end 394 395end -- class PATH_NAME 396-- 397-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file. 398-- 399-- Permission is hereby granted, free of charge, to any person obtaining a copy 400-- of this software and associated documentation files (the "Software"), to deal 401-- in the Software without restriction, including without limitation the rights 402-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 403-- copies of the Software, and to permit persons to whom the Software is 404-- furnished to do so, subject to the following conditions: 405-- 406-- The above copyright notice and this permission notice shall be included in 407-- all copies or substantial portions of the Software. 408-- 409-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 410-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 411-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 412-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 413-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 414-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 415-- THE SOFTWARE.