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

http://github.com/tybor/Liberty · Specman e · 355 lines · 281 code · 31 blank · 43 comment · 27 complexity · 1ebb5ecff124ed5fc1a7d8b55860ab96 MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. class POSIX_PATH_NAME
  5. -- Operating system path name, POSIX notation
  6. inherit
  7. UNIXISH_PATH_NAME
  8. rename
  9. path as to_string
  10. export {ANY}
  11. to_string
  12. redefine copy, is_equal
  13. end
  14. create {ANY}
  15. make_empty, make_root, make_current, make_from_string
  16. feature {ANY} -- Creation
  17. make_empty
  18. do
  19. if to_string = Void then
  20. to_string := ""
  21. else
  22. to_string.clear_count
  23. end
  24. end
  25. make_root
  26. do
  27. make_empty
  28. to_string.extend('/')
  29. ensure then
  30. to_string.is_equal(once "/")
  31. end
  32. make_current
  33. do
  34. make_empty
  35. to_string.extend('.')
  36. ensure then
  37. to_string.is_equal(once ".")
  38. end
  39. make_from_string (s: STRING)
  40. do
  41. if to_string = Void then
  42. to_string := s.twin
  43. elseif to_string /= s then
  44. to_string.copy(s)
  45. end
  46. end
  47. feature {ANY} -- Constants
  48. extension_separator: CHARACTER '.'
  49. directory_separator: CHARACTER '/'
  50. up_directory: STRING ".."
  51. this_directory: STRING "."
  52. feature {ANY} -- Access
  53. drive_specification: STRING
  54. do
  55. ensure then
  56. Result = Void
  57. end
  58. count: INTEGER
  59. local
  60. p: INTEGER; sep: BOOLEAN
  61. do
  62. from
  63. p := 1
  64. sep := True
  65. until
  66. p > to_string.count
  67. loop
  68. if not sep and to_string.item(p) = '/' then
  69. sep := True
  70. elseif sep and to_string.item(p) /= '/' then
  71. sep := False
  72. Result := Result + 1
  73. end
  74. p := p + 1
  75. end
  76. if sep and Result > 0 then
  77. -- trailing /
  78. Result := Result + 1
  79. end
  80. end
  81. last: STRING
  82. local
  83. p: INTEGER
  84. do
  85. p := to_string.last_index_of(directory_separator) + 1
  86. -- check to_string.is_valid_index(p) end
  87. Result := to_string.substring(p, to_string.upper)
  88. ensure then
  89. to_string.has_suffix(Result)
  90. end
  91. extension: STRING
  92. local
  93. p: INTEGER -- the position of the eventual (last) separator (usually a point)
  94. as_string: like to_string
  95. do
  96. -- A naive implementation, requiring an hidden allocation could be
  97. -- Result := last; Result := Result.right(Result.last_index_of(extension_separator))
  98. -- beside requiring "right"
  99. Result := once ""
  100. as_string := to_string -- let's cache the complete path as a string in the eventuality that an heir of POSIX_PATH_NAME redefine to_string into a computed feature (a function)
  101. p := as_string.last_index_of(extension_separator)
  102. if p > as_string.lower then
  103. if p > as_string.last_index_of(directory_separator) then
  104. Result := as_string.substring(p, as_string.upper)
  105. else
  106. check
  107. not last.has(extension_separator) -- Current is like "xxx.d/we"
  108. end
  109. end
  110. else
  111. -- path should be like "./without_extension" or like "../asd/./qwerty"
  112. check
  113. Result.is_empty
  114. end
  115. end
  116. end
  117. is_absolute: BOOLEAN
  118. do
  119. Result := not to_string.is_empty and then to_string.first = '/'
  120. ensure
  121. definition: Result = (not to_string.is_empty and then to_string.first = '/')
  122. end
  123. is_normalized: BOOLEAN
  124. local
  125. elem: STRING; scan: STRING
  126. do
  127. elem := once "path_element"
  128. scan := once ""
  129. scan.copy(to_string)
  130. -- Check for emptiness or too many slashes
  131. Result := not scan.is_empty and then not scan.has_prefix(once "///")
  132. -- zero, one or two slashes allowed by POSIX
  133. -- Remove initial slashes
  134. from
  135. until
  136. scan.is_empty or else scan.first /= '/'
  137. loop
  138. scan.remove_head(1)
  139. end
  140. -- Check for trailing slashes, double slashes
  141. Result := Result and then (scan.is_empty or else scan.last /= '/') and then not scan.has_substring(double_slash)
  142. -- Remove initial sequences of ".."
  143. if not is_absolute and Result then
  144. from
  145. until
  146. scan.is_empty or else not scan.has_prefix(up_directory)
  147. loop
  148. if scan.count >= 3 and then scan.item(3) = '/' then
  149. scan.remove_head(3)
  150. else
  151. scan.remove_head(2)
  152. end
  153. end
  154. else
  155. Result := Result and then not scan.has_prefix(once "../") and then not scan.is_equal(up_directory)
  156. end
  157. -- Make sure that there is no '..' remaining
  158. Result := Result and then not scan.has_substring(once "/../") and then not scan.has_suffix(once "/..")
  159. -- Make sure that there is no '.' remaining except alones
  160. Result := Result and then not scan.has_substring(once "/./") and then not scan.has_suffix(once "/.") and then not scan.has_prefix(once "./")
  161. Result := Result and then (is_absolute implies not scan.is_equal(this_directory))
  162. ensure
  163. Result implies not to_string.has_substring(once "/./")
  164. Result implies not to_string.has_suffix(once "/.")
  165. Result implies not to_string.is_empty
  166. Result implies to_string.last /= '/' or else to_string.is_equal(once "/") or else to_string.is_equal(once "//")
  167. end
  168. is_valid_path (path: STRING): BOOLEAN
  169. do
  170. --|*** Not nearly strict enough <FM-24/03/2003>
  171. Result := not path.is_empty
  172. end
  173. feature {ANY} -- Operations
  174. to_absolute
  175. local
  176. bd: BASIC_DIRECTORY
  177. do
  178. if not is_absolute then
  179. tmp.copy(Current)
  180. to_string := bd.current_working_directory.out.twin
  181. join(tmp)
  182. tmp.to_string.make_empty
  183. end
  184. normalize
  185. end
  186. normalize
  187. do
  188. tmp.copy(Current)
  189. make_from_path_name(tmp)
  190. if to_string.is_empty then
  191. make_current
  192. end
  193. end
  194. normalize_case
  195. do
  196. -- POSIX filesystem is case-sensitive
  197. -- There are no secondary separators
  198. ensure
  199. to_string.is_equal(old to_string.twin)
  200. end
  201. remove_last
  202. local
  203. p: INTEGER
  204. do
  205. -- Find last separator
  206. p := to_string.reverse_index_of('/', to_string.count)
  207. -- Remove all trailing slashes, leaving one if it is root
  208. from
  209. until
  210. p <= 1 or else to_string.item(p) /= '/'
  211. loop
  212. p := p - 1
  213. end
  214. to_string.keep_head(p)
  215. ensure then
  216. (old to_string.twin).has_prefix(to_string)
  217. end
  218. add_last (elem: STRING)
  219. do
  220. if not is_empty then
  221. to_string.extend_unless('/')
  222. end
  223. to_string.append(elem)
  224. end
  225. expand_user
  226. local
  227. user_home: STRING; sys: SYSTEM; p: INTEGER
  228. do
  229. if not is_empty and then to_string.first = '~' then
  230. if to_string.count = 1 or else to_string.has_prefix(once "~/") then
  231. user_home := sys.get_environment_variable(once "HOME")
  232. else
  233. -- FIXME: Expansion of some other user's home: not done yet
  234. not_yet_implemented
  235. end
  236. if user_home /= Void then
  237. p := to_string.first_index_of('/')
  238. if p = 0 then
  239. p := to_string.count + 1
  240. end
  241. to_string.remove_head(p - 1)
  242. to_string.prepend(user_home)
  243. end
  244. end
  245. end
  246. expand_shellouts
  247. do
  248. not_yet_implemented
  249. end
  250. feature {ANY} -- Copying, comparison
  251. copy (other: like Current)
  252. do
  253. if Current /= other then
  254. if to_string = Void then
  255. to_string := other.to_string.twin
  256. else
  257. to_string.copy(other.to_string)
  258. end
  259. end
  260. end
  261. is_equal (other: like Current): BOOLEAN
  262. do
  263. Result := to_string.is_equal(other.to_string)
  264. end
  265. feature {PATH_JOINER}
  266. start_join (drive: STRING; absoluteness: INTEGER)
  267. do
  268. inspect absoluteness
  269. when 0 then
  270. when 1 then
  271. make_root
  272. else
  273. make_from_string(double_slash)
  274. end
  275. end
  276. feature {} -- Auxiliary constants/onces
  277. tmp: POSIX_PATH_NAME
  278. once
  279. create Result.make_empty
  280. end
  281. double_slash: STRING "//"
  282. start_join_to (other: PATH_JOINER): INTEGER
  283. local
  284. slash_count: INTEGER
  285. do
  286. from
  287. Result := to_string.lower
  288. until
  289. Result > to_string.upper or else not is_separator(to_string.item(Result))
  290. loop
  291. Result := Result + 1
  292. end
  293. slash_count := Result - to_string.lower
  294. if slash_count > 2 then
  295. slash_count := 1
  296. end
  297. if Result <= to_string.upper then
  298. other.start_join(drive_specification, slash_count)
  299. elseif slash_count > 0 then
  300. other.start_join(drive_specification, slash_count)
  301. other.end_join
  302. end
  303. end
  304. end -- class POSIX_PATH_NAME
  305. --
  306. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  307. --
  308. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  309. -- of this software and associated documentation files (the "Software"), to deal
  310. -- in the Software without restriction, including without limitation the rights
  311. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  312. -- copies of the Software, and to permit persons to whom the Software is
  313. -- furnished to do so, subject to the following conditions:
  314. --
  315. -- The above copyright notice and this permission notice shall be included in
  316. -- all copies or substantial portions of the Software.
  317. --
  318. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  319. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  320. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  321. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  322. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  323. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  324. -- THE SOFTWARE.