/src/lib/io/low_level/basic_directory.e

http://github.com/tybor/Liberty · Specman e · 711 lines · 539 code · 45 blank · 127 comment · 38 complexity · 53777f334ba48cd9e4bde263b54d844b MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. expanded class BASIC_DIRECTORY
  5. --
  6. -- Very low-level basic tools for file-system directory handling and file path manipulation. This class
  7. -- intended to be platform independent as much as possible. In order to remove from the client side the
  8. -- burden of file path computation, this class tries to compute automatically the system file notation
  9. -- using argument(s) of some of the very first call(s). As soon as the system notation has been properly
  10. -- detected, the result is internally memorized for all objects of type BASIC_DIRECTORY in a common private
  11. -- buffer. Besides the low-level nature of operations one can found in this class, all file path
  12. -- manipulations are done in a smart way (except when the system file path notation has not been detected
  13. -- automatically, which is quite uncommon). As an example, even if the directory separator is internally
  14. -- detected, this information is _intentionally_ kept private to avoid low-level manipulation from the
  15. -- client side. Finally, this class is expanded in order to avoid as much as possible memory allocations.
  16. --
  17. -- Also consider high level facade class DIRECTORY if you don't want to deal directly with low level
  18. -- directory streams.
  19. --
  20. feature {}
  21. directory_stream: POINTER
  22. -- This pointer memorize the current directory stream being scanned (used to compute `is_connected').
  23. current_entry: POINTER
  24. -- When `is_connected', memorize the current entry in the current `directory_stream'.
  25. feature {ANY} -- State of `Current' basic directory stream:
  26. is_connected: BOOLEAN
  27. -- Is `Current' connected to some directory stream ?
  28. do
  29. Result := directory_stream.is_not_null
  30. end
  31. end_of_input: BOOLEAN
  32. -- Is end of input reached ?
  33. require
  34. is_connected
  35. do
  36. Result := current_entry.is_null
  37. end
  38. feature {ANY} -- Connect and disconnect:
  39. connect_to (directory_path: ABSTRACT_STRING)
  40. -- Try to connect `Current' to some existing `directory_path'. After this call, the client
  41. -- supposed to use `is_connected' to check that the stream is ready to be used.
  42. require
  43. not is_connected
  44. not directory_path.is_empty
  45. common_buffer_protection: last_entry /= directory_path
  46. local
  47. path_pointer: POINTER
  48. do
  49. path_pointer := directory_path.to_external
  50. directory_stream := directory_open(path_pointer)
  51. current_entry := directory_stream
  52. last_entry.clear_count
  53. ensure
  54. is_connected implies not end_of_input
  55. end
  56. connect_with (some_path: ABSTRACT_STRING)
  57. -- Try to connect `Current' to some directory using `some_path' which may be either an existing
  58. -- directory path or some arbitrary file path name. When `some_path' is the path of some readable
  59. -- existing directory, this directory is opened and the effect of `connect_with' is equivalent to
  60. -- `connect_to'. When `some_path' is not an existing readable directory path, `connect_with' tries to
  61. -- open the directory which may contains `some_path' viewed as a file path name. After this call, the
  62. -- client is supposed to use `is_connected' to check that the stream is ready to be used and the
  63. -- `last_entry' buffer to know about the corresponding opened directory path. Whatever the result,
  64. -- `some_path' is left unchanged.
  65. require
  66. not is_connected
  67. not some_path.is_empty
  68. common_buffer_protection: last_entry /= some_path
  69. local
  70. p: POINTER
  71. do
  72. connect_to(some_path)
  73. if is_connected then
  74. last_entry.copy(some_path.out)
  75. else
  76. compute_parent_directory_of(some_path)
  77. if last_entry.count > 0 then
  78. p := last_entry.to_external
  79. directory_stream := directory_open(p)
  80. current_entry := directory_stream
  81. if directory_stream.is_null then
  82. last_entry.clear_count
  83. end
  84. else
  85. last_entry.clear_count
  86. end
  87. end
  88. ensure
  89. is_connected implies not end_of_input
  90. end
  91. connect_to_current_working_directory
  92. -- Try to connect `Current' to the current working directory. After this call, the client is supposed
  93. -- to use `is_connected' to check that the stream is ready to be used and the `last_entry' buffer to
  94. -- know about the name of the current working directory.
  95. require
  96. not is_connected
  97. local
  98. path: POINTER
  99. do
  100. path := directory_current_working_directory
  101. if path.is_not_null then
  102. last_entry.from_external_copy(path)
  103. directory_stream := directory_open(path)
  104. current_entry := directory_stream
  105. if directory_stream.is_null then
  106. last_entry.clear_count
  107. else
  108. if not system_notation_detected then
  109. set_notation_using(last_entry)
  110. end
  111. system_notation.to_directory_path(last_entry)
  112. end
  113. else
  114. last_entry.clear_count
  115. end
  116. ensure
  117. is_connected implies not end_of_input
  118. end
  119. disconnect
  120. -- Do not forget to call this feature when you have finished with some previously opened directory
  121. -- stream.
  122. require
  123. is_connected
  124. local
  125. null: POINTER
  126. do
  127. if directory_close(directory_stream) then
  128. directory_stream := null
  129. current_entry := null
  130. end
  131. ensure
  132. not is_connected
  133. end
  134. feature {ANY} -- Scanning:
  135. last_entry: STRING
  136. -- Unique global buffer (once object) to get the last information computed by many routines of this
  137. -- class: `read_entry', `connect_with' `connect_to_current_working_directory',
  138. -- `compute_parent_directory_of', ...
  139. once
  140. create Result.make(256)
  141. end
  142. read_entry
  143. -- Read the next entry name and update `last_entry' and `end_of_input' accordingly.
  144. require
  145. is_connected
  146. not end_of_input
  147. local
  148. name: POINTER
  149. do
  150. current_entry := directory_read_entry(directory_stream)
  151. if current_entry.is_not_null then
  152. name := directory_get_entry_name(current_entry)
  153. last_entry.from_external_copy(name)
  154. end
  155. end
  156. feature {ANY} -- File path handling tools:
  157. compute_parent_directory_of (some_path: ABSTRACT_STRING)
  158. -- Using `some_path' (which may be either a file path or a directory path) tries to compute in the
  159. -- `last_entry' buffer the parent directory of `some_path'. When `some_path' is a path with no parent
  160. -- directory, the `last_entry' buffer `is_empty' after this call. This operation does not perform any
  161. -- disk access.
  162. require
  163. not some_path.is_empty
  164. common_buffer_protection: last_entry /= some_path
  165. do
  166. if system_notation /= Void then
  167. last_entry.copy(some_path.out)
  168. system_notation.to_parent_directory(last_entry)
  169. else
  170. set_notation_using(some_path)
  171. if system_notation /= Void then
  172. last_entry.copy(some_path.out)
  173. system_notation.to_parent_directory(last_entry)
  174. else
  175. last_entry.clear_count
  176. end
  177. end
  178. end
  179. compute_subdirectory_with (parent_path, entry_name: ABSTRACT_STRING)
  180. -- Try to compute in the `last_entry' buffer the new subdirectory path obtained when trying to
  181. -- concatenate smartly `parent_path' with some `entry_name'. When this fails the `last_entry' buffer
  182. -- `is_empty' after this call. This operation does not perform any disk access. Whatever the result,
  183. -- `parent_path' and `entry_name' are left unchanged.
  184. require
  185. not parent_path.is_empty
  186. not entry_name.is_empty
  187. common_buffer_protection1: last_entry /= parent_path
  188. common_buffer_protection2: last_entry /= entry_name
  189. do
  190. if system_notation /= Void then
  191. last_entry.copy(parent_path.out)
  192. system_notation.to_subdirectory_with(last_entry, entry_name.out)
  193. else
  194. set_notation_using(parent_path.out)
  195. if system_notation /= Void then
  196. last_entry.copy(parent_path.out)
  197. system_notation.to_subdirectory_with(last_entry, entry_name.out)
  198. else
  199. last_entry.clear_count
  200. end
  201. end
  202. end
  203. compute_file_path_with (parent_path, file_name: ABSTRACT_STRING)
  204. -- Try to compute in the `last_entry' buffer the new file path obtained when trying to concatenate
  205. -- smartly `parent_path' with some `file_name'. When this fails the `last_entry' buffer `is_empty'
  206. -- after this call. This operation does not perform any disk access. Whatever the result,
  207. -- `parent_path' and `file_name' are left unchanged.
  208. require
  209. not parent_path.is_empty
  210. not file_name.is_empty
  211. common_buffer_protection1: last_entry /= parent_path
  212. common_buffer_protection2: last_entry /= file_name
  213. do
  214. if system_notation /= Void then
  215. last_entry.copy(parent_path.out)
  216. system_notation.to_file_path_with(last_entry, file_name.out)
  217. else
  218. set_notation_using(parent_path)
  219. if system_notation /= Void then
  220. last_entry.copy(parent_path.out)
  221. system_notation.to_file_path_with(last_entry, file_name.out)
  222. else
  223. last_entry.clear_count
  224. end
  225. end
  226. end
  227. compute_absolute_file_path_with (path: ABSTRACT_STRING)
  228. -- Try to compute an absolute path equivalent to `path' and store it in `last_entry'. When this fails
  229. -- the `last_entry' buffer `is_empty' after this call. This operation does not perform any disk
  230. -- access. Whatever the result, `path' is left unchanged.
  231. require
  232. valid_path(path)
  233. common_buffer_protection: last_entry /= path
  234. do
  235. if system_notation /= Void then
  236. last_entry.copy(current_working_directory.out)
  237. system_notation.to_absolute_path_in(last_entry, path.out)
  238. else
  239. set_notation_using(path)
  240. if system_notation /= Void then
  241. last_entry.copy(current_working_directory.out)
  242. system_notation.to_absolute_path_in(last_entry, path.out)
  243. else
  244. last_entry.clear_count
  245. end
  246. end
  247. ensure
  248. last_entry.is_empty or else system_notation.is_absolute_path(last_entry.out)
  249. end
  250. compute_short_name_of (path: ABSTRACT_STRING)
  251. -- Try to find the short name of the file or directory given by its `path' and store it in
  252. -- `last_entry'. When this fails the `last_entry' buffer `is_empty' after the call. This operation
  253. -- does not perform any disk access. Whatever the result, `path' is left unchanged.
  254. do
  255. if system_notation /= Void then
  256. last_entry.copy(current_working_directory.out)
  257. system_notation.to_short_name_in(last_entry, path.out)
  258. else
  259. set_notation_using(path)
  260. if system_notation /= Void then
  261. last_entry.copy(current_working_directory.out)
  262. system_notation.to_short_name_in(last_entry, path.out)
  263. else
  264. last_entry.clear_count
  265. end
  266. end
  267. end
  268. valid_path (path: ABSTRACT_STRING): BOOLEAN
  269. -- Is the syntax of `path' valid for the system notation?
  270. do
  271. if system_notation_detected then
  272. Result := system_notation.is_valid_path(path.out)
  273. else
  274. Result := not path.is_empty
  275. end
  276. ensure
  277. system_notation_detected implies Result = system_notation.is_valid_path(path.out)
  278. not system_notation_detected implies Result = not path.is_empty
  279. end
  280. change_current_working_directory (directory_path: ABSTRACT_STRING)
  281. -- Try to change the current working directory using some `directory_path'. When the operation
  282. -- possible, the `last_entry' buffer is updated with the new current working directory path,
  283. -- otherwise, when the modification is not possible the `last_entry' buffer `is_empty' after this
  284. -- call. Whatever the result, `directory_path' is left unchanged.
  285. require
  286. not is_connected
  287. common_buffer_protection1: last_entry /= directory_path
  288. local
  289. p: POINTER
  290. do
  291. p := directory_path.to_external
  292. if directory_chdir(p) then
  293. connect_to_current_working_directory
  294. if is_connected then
  295. disconnect
  296. check
  297. not last_entry.is_empty
  298. end
  299. else
  300. last_entry.clear_count
  301. end
  302. else
  303. last_entry.clear_count
  304. end
  305. ensure
  306. not is_connected
  307. end
  308. current_working_directory: FIXED_STRING
  309. -- The current working directory. Always returns the same once STRING.
  310. local
  311. path: POINTER; cwd: STRING
  312. do
  313. path := directory_current_working_directory
  314. if path.is_not_null then
  315. cwd := once ""
  316. cwd.from_external_copy(path)
  317. if not system_notation_detected then
  318. set_notation_using(cwd)
  319. end
  320. system_notation.to_directory_path(cwd)
  321. Result := cwd.intern
  322. end
  323. end
  324. ensure_system_notation
  325. local
  326. dummy: BOOLEAN
  327. once
  328. dummy := require_system_notation
  329. ensure
  330. system_notation /= Void
  331. end
  332. require_system_notation: BOOLEAN
  333. -- Same as `ensure_system_notation', useful for contracts
  334. once
  335. Result := current_working_directory /= Void
  336. ensure
  337. system_notation /= Void
  338. Result
  339. end
  340. feature {ANY} -- Disk modification:
  341. create_new_directory (directory_path: ABSTRACT_STRING): BOOLEAN
  342. -- Try to create a new directory using the `directory_path' name.
  343. -- Returns True on success.
  344. require
  345. not is_connected
  346. local
  347. p: POINTER
  348. do
  349. p := directory_path.to_external
  350. Result := directory_mkdir(p)
  351. ensure
  352. not is_connected
  353. end
  354. remove_directory (directory_path: ABSTRACT_STRING): BOOLEAN
  355. -- Try to remove directory `directory_path' which must be empty.
  356. -- Returns True on success.
  357. require
  358. not is_connected
  359. local
  360. p: POINTER
  361. do
  362. p := directory_path.to_external
  363. Result := directory_rmdir(p)
  364. ensure
  365. not is_connected
  366. end
  367. remove_files_of (directory_path: ABSTRACT_STRING)
  368. -- Try to remove all files (not subdirectories) of directory specified by `directory_path'.
  369. require
  370. not is_connected
  371. local
  372. ft: FILE_TOOLS
  373. do
  374. connect_to(directory_path)
  375. if is_connected then
  376. from
  377. read_entry
  378. until
  379. end_of_input
  380. loop
  381. tmp_path.copy(last_entry)
  382. compute_file_path_with(directory_path, tmp_path)
  383. tmp_path.copy(last_entry)
  384. if ft.is_file(tmp_path) then
  385. ft.delete(tmp_path)
  386. end
  387. read_entry
  388. end
  389. disconnect
  390. end
  391. ensure
  392. not is_connected
  393. end
  394. remove_recursively (directory_path: ABSTRACT_STRING): BOOLEAN
  395. -- Try to remove all files and all subdirectories of directory specified by `directory_path'.
  396. require
  397. not is_connected
  398. local
  399. ft: FILE_TOOLS
  400. do
  401. connect_to(directory_path)
  402. Result := is_connected
  403. if Result then
  404. from
  405. read_entry
  406. until
  407. end_of_input
  408. loop
  409. if not system_notation.is_current_directory(last_entry) and then not system_notation.is_parent_directory(last_entry) then
  410. tmp_path.copy(last_entry)
  411. compute_file_path_with(directory_path, tmp_path)
  412. tmp_path.copy(last_entry)
  413. if ft.is_directory(tmp_path) then
  414. disconnect
  415. Result := Result and remove_recursively(tmp_path.twin)
  416. connect_to(directory_path)
  417. else
  418. ft.delete(tmp_path)
  419. end
  420. end
  421. read_entry
  422. end
  423. disconnect
  424. end
  425. Result := Result and remove_directory(directory_path)
  426. ensure
  427. not is_connected
  428. end
  429. feature {ANY} -- Miscellaneous:
  430. is_case_sensitive: BOOLEAN
  431. local
  432. bd: like Current
  433. do
  434. if system_notation = Void then
  435. -- Try to create system_notation
  436. bd.connect_to_current_working_directory
  437. if bd.is_connected then
  438. if not last_entry.is_empty then
  439. set_notation_using(last_entry)
  440. if system_notation /= Void then
  441. Result := system_notation.is_case_sensitive
  442. end
  443. end
  444. bd.disconnect
  445. end
  446. else
  447. Result := system_notation.is_case_sensitive
  448. end
  449. end
  450. system_notation: DIRECTORY_NOTATION
  451. do
  452. Result := system_notation_buffer.item
  453. end
  454. feature {DIRECTORY_NOTATION_HANDLER}
  455. system_notation_buffer: REFERENCE[DIRECTORY_NOTATION]
  456. -- Unique common buffer to memorize the system path notation.
  457. once
  458. create Result
  459. end
  460. system_notation_detected: BOOLEAN
  461. do
  462. Result := system_notation /= Void
  463. end
  464. unix_notation: BOOLEAN
  465. -- The Unix like file path notation looks like:
  466. -- /LibertyEiffel/sys/system.se
  467. do
  468. Result := {UNIX_DIRECTORY_NOTATION} ?:= system_notation
  469. end
  470. windows_notation: BOOLEAN
  471. -- The Windows like file path notation looks like:
  472. -- C:\LibertyEiffel\sys\system.se
  473. do
  474. Result := {WINDOWS_DIRECTORY_NOTATION} ?:= system_notation
  475. end
  476. cygwin_notation: BOOLEAN
  477. -- The Cygwin like file path notation looks like:
  478. -- //C/LibertyEiffel/sys/system.se
  479. do
  480. Result := {CYGWIN_DIRECTORY_NOTATION} ?:= system_notation
  481. end
  482. openvms_notation: BOOLEAN
  483. -- The VMS file path notation looks like:
  484. -- DISK:[LibertyEiffel.sys]system.se
  485. -- The current working directory notation is:
  486. -- DISK:[]
  487. -- The equivalent of Unix .. is :
  488. -- [-]
  489. -- The equivalent of Unix ../.. is :
  490. -- [-.-]
  491. --
  492. do
  493. Result := {OPENVMS_DIRECTORY_NOTATION} ?:= system_notation
  494. end
  495. set_notation_using (some_path: ABSTRACT_STRING)
  496. -- Try to detect automatically the file system notation.
  497. require
  498. not some_path.is_empty
  499. not system_notation_detected
  500. do
  501. inspect
  502. some_path.first
  503. when '/', '.', '~' then
  504. if some_path.count >= 4 and then some_path.item(2) = '/' and then some_path.item(4) = '/' then
  505. system_notation_buffer.set_item(create {CYGWIN_DIRECTORY_NOTATION})
  506. else
  507. system_notation_buffer.set_item(create {UNIX_DIRECTORY_NOTATION})
  508. end
  509. when '\' then
  510. system_notation_buffer.set_item(create {WINDOWS_DIRECTORY_NOTATION})
  511. when '[' then
  512. system_notation_buffer.set_item(create {OPENVMS_DIRECTORY_NOTATION})
  513. when 'a' .. 'z', 'A' .. 'Z' then
  514. if some_path.count >= 2 then
  515. inspect
  516. some_path.item(2)
  517. when ':' then
  518. if some_path.count = 2 then
  519. system_notation_buffer.set_item(create {WINDOWS_DIRECTORY_NOTATION})
  520. elseif some_path.has('\') then
  521. system_notation_buffer.set_item(create {WINDOWS_DIRECTORY_NOTATION})
  522. elseif some_path.has('/') then
  523. system_notation_buffer.set_item(create {CYGWIN_DIRECTORY_NOTATION})
  524. end
  525. when 'a' .. 'z', 'A' .. 'Z' then
  526. if some_path.has('[') then
  527. system_notation_buffer.set_item(create {OPENVMS_DIRECTORY_NOTATION})
  528. elseif some_path.has(':') then
  529. if some_path.has('[') then
  530. system_notation_buffer.set_item(create {OPENVMS_DIRECTORY_NOTATION})
  531. end
  532. elseif some_path.has('/') then
  533. system_notation_buffer.set_item(create {UNIX_DIRECTORY_NOTATION})
  534. elseif some_path.has('\') then
  535. --|*** This looks weird <FM-23/03/2005>
  536. system_notation_buffer.set_item(create {UNIX_DIRECTORY_NOTATION})
  537. end
  538. else
  539. end
  540. end
  541. else
  542. end
  543. end
  544. reset_notation_using (some_path: ABSTRACT_STRING)
  545. -- Try to detect automatically the file system notation.
  546. do
  547. system_notation_buffer.set_item(Void)
  548. set_notation_using(some_path)
  549. end
  550. reset_notation
  551. do
  552. reset_notation_using(current_working_directory)
  553. end
  554. feature {}
  555. tmp_path: STRING
  556. once
  557. create Result.make(256)
  558. end
  559. directory_open (path_pointer: POINTER): POINTER
  560. -- Try to open some existing directory using `path'. When `Result' `is_not_null', the directory
  561. -- correctly opened and `Result' is a valid handle for this directory. Using `Result', one can then
  562. -- scan the content of the directory using function `basic_directory_read_entry' and
  563. -- `basic_directory_get_entry_name'. Finally, a `is_not_null' directory must be closed using function
  564. -- `basic_directory_close'.
  565. require
  566. path_pointer.is_not_null
  567. external "plug_in"
  568. alias "{
  569. location: "${sys}/plugins/io"
  570. module_name: "directory"
  571. feature_name: "directory_open"
  572. }"
  573. end
  574. directory_read_entry (dirstream: POINTER): POINTER
  575. -- Read an return a new entry using the directory handle `dirstream' obtained with function
  576. -- `basic_directory_open'. When there is no more entry, the `Result' becomes `is_null'.
  577. require
  578. dirstream.is_not_null
  579. external "plug_in"
  580. alias "{
  581. location: "${sys}/plugins/io"
  582. module_name: "directory"
  583. feature_name: "directory_read_entry"
  584. }"
  585. end
  586. directory_get_entry_name (entry: POINTER): POINTER
  587. -- Read an return a new entry using the directory handle `dirstream' obtained with function
  588. -- `basic_directory_open'. When there is no more entry, the `Result' becomes `is_null'.
  589. require
  590. entry.is_not_null
  591. external "plug_in"
  592. alias "{
  593. location: "${sys}/plugins/io"
  594. module_name: "directory"
  595. feature_name: "directory_get_entry_name"
  596. }"
  597. end
  598. directory_close (dirstream: POINTER): BOOLEAN
  599. -- Try to close some opened `dirstream' directory. A True result indicates that the directory
  600. -- correctly closed.
  601. require
  602. dirstream.is_not_null
  603. external "plug_in"
  604. alias "{
  605. location: "${sys}/plugins/io"
  606. module_name: "directory"
  607. feature_name: "directory_close"
  608. }"
  609. end
  610. directory_current_working_directory: POINTER
  611. -- Try to get the current working directory path.
  612. external "plug_in"
  613. alias "{
  614. location: "${sys}/plugins/io"
  615. module_name: "directory"
  616. feature_name: "directory_current_working_directory"
  617. }"
  618. end
  619. directory_chdir (destination: POINTER): BOOLEAN
  620. -- Try to change the current working directory using `destination'.
  621. external "plug_in"
  622. alias "{
  623. location: "${sys}/plugins/io"
  624. module_name: "directory"
  625. feature_name: "directory_chdir"
  626. }"
  627. end
  628. directory_mkdir (directory_path: POINTER): BOOLEAN
  629. -- Try to create a new directory using `directory_path'.
  630. external "plug_in"
  631. alias "{
  632. location: "${sys}/plugins/io"
  633. module_name: "directory"
  634. feature_name: "directory_mkdir"
  635. }"
  636. end
  637. directory_rmdir (directory_path: POINTER): BOOLEAN
  638. -- Try to remove `directory_path'.
  639. external "plug_in"
  640. alias "{
  641. location: "${sys}/plugins/io"
  642. module_name: "directory"
  643. feature_name: "directory_rmdir"
  644. }"
  645. end
  646. end -- class BASIC_DIRECTORY
  647. --
  648. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  649. --
  650. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  651. -- of this software and associated documentation files (the "Software"), to deal
  652. -- in the Software without restriction, including without limitation the rights
  653. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  654. -- copies of the Software, and to permit persons to whom the Software is
  655. -- furnished to do so, subject to the following conditions:
  656. --
  657. -- The above copyright notice and this permission notice shall be included in
  658. -- all copies or substantial portions of the Software.
  659. --
  660. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  661. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  662. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  663. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  664. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  665. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  666. -- THE SOFTWARE.