/src/lib/io/filesystem/path_name.e

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