bundler 2.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (303) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3111 -0
  3. data/LICENSE.md +23 -0
  4. data/README.md +63 -0
  5. data/bundler.gemspec +65 -0
  6. data/exe/bundle +31 -0
  7. data/exe/bundle_ruby +60 -0
  8. data/exe/bundler +4 -0
  9. data/lib/bundler.rb +567 -0
  10. data/lib/bundler/build_metadata.rb +53 -0
  11. data/lib/bundler/capistrano.rb +22 -0
  12. data/lib/bundler/cli.rb +792 -0
  13. data/lib/bundler/cli/add.rb +35 -0
  14. data/lib/bundler/cli/binstubs.rb +49 -0
  15. data/lib/bundler/cli/cache.rb +36 -0
  16. data/lib/bundler/cli/check.rb +38 -0
  17. data/lib/bundler/cli/clean.rb +25 -0
  18. data/lib/bundler/cli/common.rb +102 -0
  19. data/lib/bundler/cli/config.rb +119 -0
  20. data/lib/bundler/cli/console.rb +43 -0
  21. data/lib/bundler/cli/doctor.rb +140 -0
  22. data/lib/bundler/cli/exec.rb +105 -0
  23. data/lib/bundler/cli/gem.rb +252 -0
  24. data/lib/bundler/cli/info.rb +50 -0
  25. data/lib/bundler/cli/init.rb +47 -0
  26. data/lib/bundler/cli/inject.rb +60 -0
  27. data/lib/bundler/cli/install.rb +218 -0
  28. data/lib/bundler/cli/issue.rb +40 -0
  29. data/lib/bundler/cli/list.rb +58 -0
  30. data/lib/bundler/cli/lock.rb +63 -0
  31. data/lib/bundler/cli/open.rb +26 -0
  32. data/lib/bundler/cli/outdated.rb +266 -0
  33. data/lib/bundler/cli/package.rb +49 -0
  34. data/lib/bundler/cli/platform.rb +46 -0
  35. data/lib/bundler/cli/plugin.rb +24 -0
  36. data/lib/bundler/cli/pristine.rb +47 -0
  37. data/lib/bundler/cli/remove.rb +18 -0
  38. data/lib/bundler/cli/show.rb +75 -0
  39. data/lib/bundler/cli/update.rb +91 -0
  40. data/lib/bundler/cli/viz.rb +31 -0
  41. data/lib/bundler/compact_index_client.rb +109 -0
  42. data/lib/bundler/compact_index_client/cache.rb +118 -0
  43. data/lib/bundler/compact_index_client/updater.rb +116 -0
  44. data/lib/bundler/compatibility_guard.rb +13 -0
  45. data/lib/bundler/constants.rb +7 -0
  46. data/lib/bundler/current_ruby.rb +94 -0
  47. data/lib/bundler/definition.rb +995 -0
  48. data/lib/bundler/dep_proxy.rb +48 -0
  49. data/lib/bundler/dependency.rb +139 -0
  50. data/lib/bundler/deployment.rb +69 -0
  51. data/lib/bundler/deprecate.rb +44 -0
  52. data/lib/bundler/dsl.rb +615 -0
  53. data/lib/bundler/endpoint_specification.rb +141 -0
  54. data/lib/bundler/env.rb +149 -0
  55. data/lib/bundler/environment_preserver.rb +59 -0
  56. data/lib/bundler/errors.rb +158 -0
  57. data/lib/bundler/feature_flag.rb +75 -0
  58. data/lib/bundler/fetcher.rb +312 -0
  59. data/lib/bundler/fetcher/base.rb +52 -0
  60. data/lib/bundler/fetcher/compact_index.rb +126 -0
  61. data/lib/bundler/fetcher/dependency.rb +82 -0
  62. data/lib/bundler/fetcher/downloader.rb +84 -0
  63. data/lib/bundler/fetcher/index.rb +52 -0
  64. data/lib/bundler/friendly_errors.rb +131 -0
  65. data/lib/bundler/gem_helper.rb +217 -0
  66. data/lib/bundler/gem_helpers.rb +101 -0
  67. data/lib/bundler/gem_remote_fetcher.rb +43 -0
  68. data/lib/bundler/gem_tasks.rb +7 -0
  69. data/lib/bundler/gem_version_promoter.rb +190 -0
  70. data/lib/bundler/gemdeps.rb +29 -0
  71. data/lib/bundler/graph.rb +152 -0
  72. data/lib/bundler/index.rb +213 -0
  73. data/lib/bundler/injector.rb +253 -0
  74. data/lib/bundler/inline.rb +74 -0
  75. data/lib/bundler/installer.rb +318 -0
  76. data/lib/bundler/installer/gem_installer.rb +85 -0
  77. data/lib/bundler/installer/parallel_installer.rb +229 -0
  78. data/lib/bundler/installer/standalone.rb +53 -0
  79. data/lib/bundler/lazy_specification.rb +123 -0
  80. data/lib/bundler/lockfile_generator.rb +95 -0
  81. data/lib/bundler/lockfile_parser.rb +256 -0
  82. data/lib/bundler/match_platform.rb +24 -0
  83. data/lib/bundler/mirror.rb +223 -0
  84. data/lib/bundler/plugin.rb +294 -0
  85. data/lib/bundler/plugin/api.rb +81 -0
  86. data/lib/bundler/plugin/api/source.rb +306 -0
  87. data/lib/bundler/plugin/dsl.rb +53 -0
  88. data/lib/bundler/plugin/events.rb +61 -0
  89. data/lib/bundler/plugin/index.rb +165 -0
  90. data/lib/bundler/plugin/installer.rb +96 -0
  91. data/lib/bundler/plugin/installer/git.rb +38 -0
  92. data/lib/bundler/plugin/installer/rubygems.rb +27 -0
  93. data/lib/bundler/plugin/source_list.rb +27 -0
  94. data/lib/bundler/process_lock.rb +24 -0
  95. data/lib/bundler/psyched_yaml.rb +37 -0
  96. data/lib/bundler/remote_specification.rb +114 -0
  97. data/lib/bundler/resolver.rb +373 -0
  98. data/lib/bundler/resolver/spec_group.rb +106 -0
  99. data/lib/bundler/retry.rb +66 -0
  100. data/lib/bundler/ruby_dsl.rb +18 -0
  101. data/lib/bundler/ruby_version.rb +152 -0
  102. data/lib/bundler/rubygems_ext.rb +209 -0
  103. data/lib/bundler/rubygems_gem_installer.rb +99 -0
  104. data/lib/bundler/rubygems_integration.rb +915 -0
  105. data/lib/bundler/runtime.rb +322 -0
  106. data/lib/bundler/settings.rb +464 -0
  107. data/lib/bundler/settings/validator.rb +102 -0
  108. data/lib/bundler/setup.rb +28 -0
  109. data/lib/bundler/shared_helpers.rb +386 -0
  110. data/lib/bundler/similarity_detector.rb +63 -0
  111. data/lib/bundler/source.rb +94 -0
  112. data/lib/bundler/source/gemspec.rb +18 -0
  113. data/lib/bundler/source/git.rb +329 -0
  114. data/lib/bundler/source/git/git_proxy.rb +262 -0
  115. data/lib/bundler/source/metadata.rb +62 -0
  116. data/lib/bundler/source/path.rb +249 -0
  117. data/lib/bundler/source/path/installer.rb +74 -0
  118. data/lib/bundler/source/rubygems.rb +539 -0
  119. data/lib/bundler/source/rubygems/remote.rb +69 -0
  120. data/lib/bundler/source_list.rb +186 -0
  121. data/lib/bundler/spec_set.rb +208 -0
  122. data/lib/bundler/ssl_certs/.document +1 -0
  123. data/lib/bundler/ssl_certs/certificate_manager.rb +66 -0
  124. data/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  125. data/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
  126. data/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
  127. data/lib/bundler/stub_specification.rb +108 -0
  128. data/lib/bundler/templates/.document +1 -0
  129. data/lib/bundler/templates/Executable +29 -0
  130. data/lib/bundler/templates/Executable.bundler +105 -0
  131. data/lib/bundler/templates/Executable.standalone +14 -0
  132. data/lib/bundler/templates/Gemfile +7 -0
  133. data/lib/bundler/templates/gems.rb +8 -0
  134. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +74 -0
  135. data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
  136. data/lib/bundler/templates/newgem/LICENSE.txt.tt +21 -0
  137. data/lib/bundler/templates/newgem/README.md.tt +47 -0
  138. data/lib/bundler/templates/newgem/Rakefile.tt +29 -0
  139. data/lib/bundler/templates/newgem/bin/console.tt +14 -0
  140. data/lib/bundler/templates/newgem/bin/setup.tt +8 -0
  141. data/lib/bundler/templates/newgem/exe/newgem.tt +3 -0
  142. data/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
  143. data/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  144. data/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  145. data/lib/bundler/templates/newgem/gitignore.tt +20 -0
  146. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +13 -0
  147. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
  148. data/lib/bundler/templates/newgem/newgem.gemspec.tt +50 -0
  149. data/lib/bundler/templates/newgem/rspec.tt +3 -0
  150. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +9 -0
  151. data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +14 -0
  152. data/lib/bundler/templates/newgem/test/newgem_test.rb.tt +11 -0
  153. data/lib/bundler/templates/newgem/test/test_helper.rb.tt +8 -0
  154. data/lib/bundler/templates/newgem/travis.yml.tt +7 -0
  155. data/lib/bundler/ui.rb +9 -0
  156. data/lib/bundler/ui/rg_proxy.rb +19 -0
  157. data/lib/bundler/ui/shell.rb +146 -0
  158. data/lib/bundler/ui/silent.rb +69 -0
  159. data/lib/bundler/uri_credentials_filter.rb +37 -0
  160. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +1741 -0
  161. data/lib/bundler/vendor/fileutils/lib/fileutils/version.rb +5 -0
  162. data/lib/bundler/vendor/molinillo/lib/molinillo.rb +12 -0
  163. data/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +26 -0
  164. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +57 -0
  165. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +81 -0
  166. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +223 -0
  167. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +36 -0
  168. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +66 -0
  169. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +62 -0
  170. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +63 -0
  171. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +61 -0
  172. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +126 -0
  173. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +46 -0
  174. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +36 -0
  175. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +136 -0
  176. data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +143 -0
  177. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +6 -0
  178. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +101 -0
  179. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +67 -0
  180. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +837 -0
  181. data/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +46 -0
  182. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +58 -0
  183. data/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb +27 -0
  184. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1233 -0
  185. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb +129 -0
  186. data/lib/bundler/vendor/thor/lib/thor.rb +509 -0
  187. data/lib/bundler/vendor/thor/lib/thor/actions.rb +331 -0
  188. data/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +104 -0
  189. data/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +60 -0
  190. data/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +118 -0
  191. data/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +143 -0
  192. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +373 -0
  193. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +109 -0
  194. data/lib/bundler/vendor/thor/lib/thor/base.rb +678 -0
  195. data/lib/bundler/vendor/thor/lib/thor/command.rb +135 -0
  196. data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +97 -0
  197. data/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  198. data/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
  199. data/lib/bundler/vendor/thor/lib/thor/error.rb +114 -0
  200. data/lib/bundler/vendor/thor/lib/thor/group.rb +281 -0
  201. data/lib/bundler/vendor/thor/lib/thor/invocation.rb +177 -0
  202. data/lib/bundler/vendor/thor/lib/thor/line_editor.rb +17 -0
  203. data/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb +37 -0
  204. data/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  205. data/lib/bundler/vendor/thor/lib/thor/parser.rb +4 -0
  206. data/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +70 -0
  207. data/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  208. data/lib/bundler/vendor/thor/lib/thor/parser/option.rb +146 -0
  209. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +226 -0
  210. data/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +71 -0
  211. data/lib/bundler/vendor/thor/lib/thor/runner.rb +324 -0
  212. data/lib/bundler/vendor/thor/lib/thor/shell.rb +81 -0
  213. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +482 -0
  214. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +149 -0
  215. data/lib/bundler/vendor/thor/lib/thor/shell/html.rb +126 -0
  216. data/lib/bundler/vendor/thor/lib/thor/util.rb +268 -0
  217. data/lib/bundler/vendor/thor/lib/thor/version.rb +3 -0
  218. data/lib/bundler/vendored_fileutils.rb +9 -0
  219. data/lib/bundler/vendored_molinillo.rb +4 -0
  220. data/lib/bundler/vendored_persistent.rb +52 -0
  221. data/lib/bundler/vendored_thor.rb +8 -0
  222. data/lib/bundler/version.rb +28 -0
  223. data/lib/bundler/version_ranges.rb +76 -0
  224. data/lib/bundler/vlad.rb +17 -0
  225. data/lib/bundler/worker.rb +106 -0
  226. data/lib/bundler/yaml_serializer.rb +90 -0
  227. data/man/bundle-add.1 +58 -0
  228. data/man/bundle-add.1.txt +52 -0
  229. data/man/bundle-add.ronn +40 -0
  230. data/man/bundle-binstubs.1 +40 -0
  231. data/man/bundle-binstubs.1.txt +48 -0
  232. data/man/bundle-binstubs.ronn +43 -0
  233. data/man/bundle-check.1 +31 -0
  234. data/man/bundle-check.1.txt +33 -0
  235. data/man/bundle-check.ronn +26 -0
  236. data/man/bundle-clean.1 +24 -0
  237. data/man/bundle-clean.1.txt +26 -0
  238. data/man/bundle-clean.ronn +18 -0
  239. data/man/bundle-config.1 +497 -0
  240. data/man/bundle-config.1.txt +529 -0
  241. data/man/bundle-config.ronn +397 -0
  242. data/man/bundle-doctor.1 +44 -0
  243. data/man/bundle-doctor.1.txt +44 -0
  244. data/man/bundle-doctor.ronn +33 -0
  245. data/man/bundle-exec.1 +165 -0
  246. data/man/bundle-exec.1.txt +178 -0
  247. data/man/bundle-exec.ronn +152 -0
  248. data/man/bundle-gem.1 +80 -0
  249. data/man/bundle-gem.1.txt +91 -0
  250. data/man/bundle-gem.ronn +78 -0
  251. data/man/bundle-info.1 +20 -0
  252. data/man/bundle-info.1.txt +21 -0
  253. data/man/bundle-info.ronn +17 -0
  254. data/man/bundle-init.1 +25 -0
  255. data/man/bundle-init.1.txt +34 -0
  256. data/man/bundle-init.ronn +29 -0
  257. data/man/bundle-inject.1 +33 -0
  258. data/man/bundle-inject.1.txt +32 -0
  259. data/man/bundle-inject.ronn +22 -0
  260. data/man/bundle-install.1 +308 -0
  261. data/man/bundle-install.1.txt +396 -0
  262. data/man/bundle-install.ronn +378 -0
  263. data/man/bundle-list.1 +50 -0
  264. data/man/bundle-list.1.txt +43 -0
  265. data/man/bundle-list.ronn +33 -0
  266. data/man/bundle-lock.1 +84 -0
  267. data/man/bundle-lock.1.txt +93 -0
  268. data/man/bundle-lock.ronn +94 -0
  269. data/man/bundle-open.1 +32 -0
  270. data/man/bundle-open.1.txt +29 -0
  271. data/man/bundle-open.ronn +19 -0
  272. data/man/bundle-outdated.1 +155 -0
  273. data/man/bundle-outdated.1.txt +131 -0
  274. data/man/bundle-outdated.ronn +111 -0
  275. data/man/bundle-package.1 +55 -0
  276. data/man/bundle-package.1.txt +79 -0
  277. data/man/bundle-package.ronn +72 -0
  278. data/man/bundle-platform.1 +61 -0
  279. data/man/bundle-platform.1.txt +57 -0
  280. data/man/bundle-platform.ronn +42 -0
  281. data/man/bundle-pristine.1 +34 -0
  282. data/man/bundle-pristine.1.txt +44 -0
  283. data/man/bundle-pristine.ronn +34 -0
  284. data/man/bundle-remove.1 +31 -0
  285. data/man/bundle-remove.1.txt +34 -0
  286. data/man/bundle-remove.ronn +23 -0
  287. data/man/bundle-show.1 +23 -0
  288. data/man/bundle-show.1.txt +27 -0
  289. data/man/bundle-show.ronn +21 -0
  290. data/man/bundle-update.1 +394 -0
  291. data/man/bundle-update.1.txt +391 -0
  292. data/man/bundle-update.ronn +350 -0
  293. data/man/bundle-viz.1 +39 -0
  294. data/man/bundle-viz.1.txt +39 -0
  295. data/man/bundle-viz.ronn +30 -0
  296. data/man/bundle.1 +136 -0
  297. data/man/bundle.1.txt +116 -0
  298. data/man/bundle.ronn +111 -0
  299. data/man/gemfile.5 +689 -0
  300. data/man/gemfile.5.ronn +521 -0
  301. data/man/gemfile.5.txt +653 -0
  302. data/man/index.txt +25 -0
  303. metadata +463 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module UI
5
+ class Silent
6
+ attr_writer :shell
7
+
8
+ def initialize
9
+ @warnings = []
10
+ end
11
+
12
+ def add_color(string, color)
13
+ string
14
+ end
15
+
16
+ def info(message, newline = nil)
17
+ end
18
+
19
+ def confirm(message, newline = nil)
20
+ end
21
+
22
+ def warn(message, newline = nil)
23
+ @warnings |= [message]
24
+ end
25
+
26
+ def error(message, newline = nil)
27
+ end
28
+
29
+ def debug(message, newline = nil)
30
+ end
31
+
32
+ def debug?
33
+ false
34
+ end
35
+
36
+ def quiet?
37
+ false
38
+ end
39
+
40
+ def ask(message)
41
+ end
42
+
43
+ def yes?(msg)
44
+ raise "Cannot ask yes? with a silent shell"
45
+ end
46
+
47
+ def no?
48
+ raise "Cannot ask no? with a silent shell"
49
+ end
50
+
51
+ def level=(name)
52
+ end
53
+
54
+ def level(name = nil)
55
+ end
56
+
57
+ def trace(message, newline = nil, force = false)
58
+ end
59
+
60
+ def silence
61
+ yield
62
+ end
63
+
64
+ def unprinted_warnings
65
+ @warnings
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module URICredentialsFilter
5
+ module_function
6
+
7
+ def credential_filtered_uri(uri_to_anonymize)
8
+ return uri_to_anonymize if uri_to_anonymize.nil?
9
+ uri = uri_to_anonymize.dup
10
+ uri = URI(uri.to_s) unless uri.is_a?(URI)
11
+ if uri.userinfo
12
+ # oauth authentication
13
+ if uri.password == "x-oauth-basic" || uri.password == "x"
14
+ # URI as string does not display with password if no user is set
15
+ oauth_designation = uri.password
16
+ uri.user = oauth_designation
17
+ end
18
+ uri.password = nil
19
+ end
20
+ return uri if uri_to_anonymize.is_a?(URI)
21
+ return uri.to_s if uri_to_anonymize.is_a?(String)
22
+ rescue URI::InvalidURIError # uri is not canonical uri scheme
23
+ uri
24
+ end
25
+
26
+ def credential_filtered_string(str_to_filter, uri)
27
+ return str_to_filter if uri.nil? || str_to_filter.nil?
28
+ str_with_no_credentials = str_to_filter.dup
29
+ anonymous_uri_str = credential_filtered_uri(uri).to_s
30
+ uri_str = uri.to_s
31
+ if anonymous_uri_str != uri_str
32
+ str_with_no_credentials = str_with_no_credentials.gsub(uri_str, anonymous_uri_str)
33
+ end
34
+ str_with_no_credentials
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,1741 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'rbconfig'
5
+ rescue LoadError
6
+ # for make mjit-headers
7
+ end
8
+
9
+ require "bundler/vendor/fileutils/lib/fileutils/version"
10
+
11
+ #
12
+ # = fileutils.rb
13
+ #
14
+ # Copyright (c) 2000-2007 Minero Aoki
15
+ #
16
+ # This program is free software.
17
+ # You can distribute/modify this program under the same terms of ruby.
18
+ #
19
+ # == module Bundler::FileUtils
20
+ #
21
+ # Namespace for several file utility methods for copying, moving, removing, etc.
22
+ #
23
+ # === Module Functions
24
+ #
25
+ # require 'bundler/vendor/fileutils/lib/fileutils'
26
+ #
27
+ # Bundler::FileUtils.cd(dir, options)
28
+ # Bundler::FileUtils.cd(dir, options) {|dir| block }
29
+ # Bundler::FileUtils.pwd()
30
+ # Bundler::FileUtils.mkdir(dir, options)
31
+ # Bundler::FileUtils.mkdir(list, options)
32
+ # Bundler::FileUtils.mkdir_p(dir, options)
33
+ # Bundler::FileUtils.mkdir_p(list, options)
34
+ # Bundler::FileUtils.rmdir(dir, options)
35
+ # Bundler::FileUtils.rmdir(list, options)
36
+ # Bundler::FileUtils.ln(target, link, options)
37
+ # Bundler::FileUtils.ln(targets, dir, options)
38
+ # Bundler::FileUtils.ln_s(target, link, options)
39
+ # Bundler::FileUtils.ln_s(targets, dir, options)
40
+ # Bundler::FileUtils.ln_sf(target, link, options)
41
+ # Bundler::FileUtils.cp(src, dest, options)
42
+ # Bundler::FileUtils.cp(list, dir, options)
43
+ # Bundler::FileUtils.cp_r(src, dest, options)
44
+ # Bundler::FileUtils.cp_r(list, dir, options)
45
+ # Bundler::FileUtils.mv(src, dest, options)
46
+ # Bundler::FileUtils.mv(list, dir, options)
47
+ # Bundler::FileUtils.rm(list, options)
48
+ # Bundler::FileUtils.rm_r(list, options)
49
+ # Bundler::FileUtils.rm_rf(list, options)
50
+ # Bundler::FileUtils.install(src, dest, options)
51
+ # Bundler::FileUtils.chmod(mode, list, options)
52
+ # Bundler::FileUtils.chmod_R(mode, list, options)
53
+ # Bundler::FileUtils.chown(user, group, list, options)
54
+ # Bundler::FileUtils.chown_R(user, group, list, options)
55
+ # Bundler::FileUtils.touch(list, options)
56
+ #
57
+ # The <tt>options</tt> parameter is a hash of options, taken from the list
58
+ # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
59
+ # <tt>:noop</tt> means that no changes are made. The other three are obvious.
60
+ # Each method documents the options that it honours.
61
+ #
62
+ # All methods that have the concept of a "source" file or directory can take
63
+ # either one file or a list of files in that argument. See the method
64
+ # documentation for examples.
65
+ #
66
+ # There are some `low level' methods, which do not accept any option:
67
+ #
68
+ # Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
69
+ # Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
70
+ # Bundler::FileUtils.copy_stream(srcstream, deststream)
71
+ # Bundler::FileUtils.remove_entry(path, force = false)
72
+ # Bundler::FileUtils.remove_entry_secure(path, force = false)
73
+ # Bundler::FileUtils.remove_file(path, force = false)
74
+ # Bundler::FileUtils.compare_file(path_a, path_b)
75
+ # Bundler::FileUtils.compare_stream(stream_a, stream_b)
76
+ # Bundler::FileUtils.uptodate?(file, cmp_list)
77
+ #
78
+ # == module Bundler::FileUtils::Verbose
79
+ #
80
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
81
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to methods
82
+ # in Bundler::FileUtils.
83
+ #
84
+ # == module Bundler::FileUtils::NoWrite
85
+ #
86
+ # This module has all methods of Bundler::FileUtils module, but never changes
87
+ # files/directories. This equates to passing the <tt>:noop</tt> flag to methods
88
+ # in Bundler::FileUtils.
89
+ #
90
+ # == module Bundler::FileUtils::DryRun
91
+ #
92
+ # This module has all methods of Bundler::FileUtils module, but never changes
93
+ # files/directories. This equates to passing the <tt>:noop</tt> and
94
+ # <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
95
+ #
96
+ module Bundler::FileUtils
97
+
98
+ def self.private_module_function(name) #:nodoc:
99
+ module_function name
100
+ private_class_method name
101
+ end
102
+
103
+ #
104
+ # Returns the name of the current directory.
105
+ #
106
+ def pwd
107
+ Dir.pwd
108
+ end
109
+ module_function :pwd
110
+
111
+ alias getwd pwd
112
+ module_function :getwd
113
+
114
+ #
115
+ # Changes the current directory to the directory +dir+.
116
+ #
117
+ # If this method is called with block, resumes to the previous
118
+ # working directory after the block execution has finished.
119
+ #
120
+ # Bundler::FileUtils.cd('/') # change directory
121
+ #
122
+ # Bundler::FileUtils.cd('/', :verbose => true) # change directory and report it
123
+ #
124
+ # Bundler::FileUtils.cd('/') do # change directory
125
+ # # ... # do something
126
+ # end # return to original directory
127
+ #
128
+ def cd(dir, verbose: nil, &block) # :yield: dir
129
+ fu_output_message "cd #{dir}" if verbose
130
+ result = Dir.chdir(dir, &block)
131
+ fu_output_message 'cd -' if verbose and block
132
+ result
133
+ end
134
+ module_function :cd
135
+
136
+ alias chdir cd
137
+ module_function :chdir
138
+
139
+ #
140
+ # Returns true if +new+ is newer than all +old_list+.
141
+ # Non-existent files are older than any file.
142
+ #
143
+ # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
144
+ # system 'make hello.o'
145
+ #
146
+ def uptodate?(new, old_list)
147
+ return false unless File.exist?(new)
148
+ new_time = File.mtime(new)
149
+ old_list.each do |old|
150
+ if File.exist?(old)
151
+ return false unless new_time > File.mtime(old)
152
+ end
153
+ end
154
+ true
155
+ end
156
+ module_function :uptodate?
157
+
158
+ def remove_trailing_slash(dir) #:nodoc:
159
+ dir == '/' ? dir : dir.chomp(?/)
160
+ end
161
+ private_module_function :remove_trailing_slash
162
+
163
+ #
164
+ # Creates one or more directories.
165
+ #
166
+ # Bundler::FileUtils.mkdir 'test'
167
+ # Bundler::FileUtils.mkdir %w( tmp data )
168
+ # Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create.
169
+ # Bundler::FileUtils.mkdir 'tmp', :mode => 0700
170
+ #
171
+ def mkdir(list, mode: nil, noop: nil, verbose: nil)
172
+ list = fu_list(list)
173
+ fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
174
+ return if noop
175
+
176
+ list.each do |dir|
177
+ fu_mkdir dir, mode
178
+ end
179
+ end
180
+ module_function :mkdir
181
+
182
+ #
183
+ # Creates a directory and all its parent directories.
184
+ # For example,
185
+ #
186
+ # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
187
+ #
188
+ # causes to make following directories, if it does not exist.
189
+ #
190
+ # * /usr
191
+ # * /usr/local
192
+ # * /usr/local/lib
193
+ # * /usr/local/lib/ruby
194
+ #
195
+ # You can pass several directories at a time in a list.
196
+ #
197
+ def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
198
+ list = fu_list(list)
199
+ fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
200
+ return *list if noop
201
+
202
+ list.map {|path| remove_trailing_slash(path)}.each do |path|
203
+ # optimize for the most common case
204
+ begin
205
+ fu_mkdir path, mode
206
+ next
207
+ rescue SystemCallError
208
+ next if File.directory?(path)
209
+ end
210
+
211
+ stack = []
212
+ until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
213
+ stack.push path
214
+ path = File.dirname(path)
215
+ end
216
+ stack.pop # root directory should exist
217
+ stack.reverse_each do |dir|
218
+ begin
219
+ fu_mkdir dir, mode
220
+ rescue SystemCallError
221
+ raise unless File.directory?(dir)
222
+ end
223
+ end
224
+ end
225
+
226
+ return *list
227
+ end
228
+ module_function :mkdir_p
229
+
230
+ alias mkpath mkdir_p
231
+ alias makedirs mkdir_p
232
+ module_function :mkpath
233
+ module_function :makedirs
234
+
235
+ def fu_mkdir(path, mode) #:nodoc:
236
+ path = remove_trailing_slash(path)
237
+ if mode
238
+ Dir.mkdir path, mode
239
+ File.chmod mode, path
240
+ else
241
+ Dir.mkdir path
242
+ end
243
+ end
244
+ private_module_function :fu_mkdir
245
+
246
+ #
247
+ # Removes one or more directories.
248
+ #
249
+ # Bundler::FileUtils.rmdir 'somedir'
250
+ # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
251
+ # # Does not really remove directory; outputs message.
252
+ # Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
253
+ #
254
+ def rmdir(list, parents: nil, noop: nil, verbose: nil)
255
+ list = fu_list(list)
256
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
257
+ return if noop
258
+ list.each do |dir|
259
+ Dir.rmdir(dir = remove_trailing_slash(dir))
260
+ if parents
261
+ begin
262
+ until (parent = File.dirname(dir)) == '.' or parent == dir
263
+ dir = parent
264
+ Dir.rmdir(dir)
265
+ end
266
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
267
+ end
268
+ end
269
+ end
270
+ end
271
+ module_function :rmdir
272
+
273
+ #
274
+ # :call-seq:
275
+ # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
276
+ # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
277
+ # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
278
+ #
279
+ # In the first form, creates a hard link +link+ which points to +target+.
280
+ # If +link+ already exists, raises Errno::EEXIST.
281
+ # But if the :force option is set, overwrites +link+.
282
+ #
283
+ # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
284
+ # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
285
+ #
286
+ # In the second form, creates a link +dir/target+ pointing to +target+.
287
+ # In the third form, creates several hard links in the directory +dir+,
288
+ # pointing to each item in +targets+.
289
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
290
+ #
291
+ # Bundler::FileUtils.cd '/sbin'
292
+ # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
293
+ #
294
+ def ln(src, dest, force: nil, noop: nil, verbose: nil)
295
+ fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
296
+ return if noop
297
+ fu_each_src_dest0(src, dest) do |s,d|
298
+ remove_file d, true if force
299
+ File.link s, d
300
+ end
301
+ end
302
+ module_function :ln
303
+
304
+ alias link ln
305
+ module_function :link
306
+
307
+ #
308
+ # :call-seq:
309
+ # Bundler::FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false)
310
+ #
311
+ # Hard link +src+ to +dest+. If +src+ is a directory, this method links
312
+ # all its contents recursively. If +dest+ is a directory, links
313
+ # +src+ to +dest/src+.
314
+ #
315
+ # +src+ can be a list of files.
316
+ #
317
+ # # Installing the library "mylib" under the site_ruby directory.
318
+ # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force => true
319
+ # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
320
+ #
321
+ # # Examples of linking several files to target directory.
322
+ # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
323
+ # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
324
+ #
325
+ # # If you want to link all contents of a directory instead of the
326
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
327
+ # # use the following code.
328
+ # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
329
+ #
330
+ def cp_lr(src, dest, noop: nil, verbose: nil,
331
+ dereference_root: true, remove_destination: false)
332
+ fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
333
+ return if noop
334
+ fu_each_src_dest(src, dest) do |s, d|
335
+ link_entry s, d, dereference_root, remove_destination
336
+ end
337
+ end
338
+ module_function :cp_lr
339
+
340
+ #
341
+ # :call-seq:
342
+ # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
343
+ # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
344
+ # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
345
+ #
346
+ # In the first form, creates a symbolic link +link+ which points to +target+.
347
+ # If +link+ already exists, raises Errno::EEXIST.
348
+ # But if the :force option is set, overwrites +link+.
349
+ #
350
+ # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
351
+ # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
352
+ #
353
+ # In the second form, creates a link +dir/target+ pointing to +target+.
354
+ # In the third form, creates several symbolic links in the directory +dir+,
355
+ # pointing to each item in +targets+.
356
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
357
+ #
358
+ # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
359
+ #
360
+ def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
361
+ fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
362
+ return if noop
363
+ fu_each_src_dest0(src, dest) do |s,d|
364
+ remove_file d, true if force
365
+ File.symlink s, d
366
+ end
367
+ end
368
+ module_function :ln_s
369
+
370
+ alias symlink ln_s
371
+ module_function :symlink
372
+
373
+ #
374
+ # :call-seq:
375
+ # Bundler::FileUtils.ln_sf(*args)
376
+ #
377
+ # Same as
378
+ #
379
+ # Bundler::FileUtils.ln_s(*args, force: true)
380
+ #
381
+ def ln_sf(src, dest, noop: nil, verbose: nil)
382
+ ln_s src, dest, force: true, noop: noop, verbose: verbose
383
+ end
384
+ module_function :ln_sf
385
+
386
+ #
387
+ # Hard links a file system entry +src+ to +dest+.
388
+ # If +src+ is a directory, this method links its contents recursively.
389
+ #
390
+ # Both of +src+ and +dest+ must be a path name.
391
+ # +src+ must exist, +dest+ must not exist.
392
+ #
393
+ # If +dereference_root+ is true, this method dereferences the tree root.
394
+ #
395
+ # If +remove_destination+ is true, this method removes each destination file before copy.
396
+ #
397
+ def link_entry(src, dest, dereference_root = false, remove_destination = false)
398
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
399
+ destent = Entry_.new(dest, ent.rel, false)
400
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
401
+ ent.link destent.path
402
+ end
403
+ end
404
+ module_function :link_entry
405
+
406
+ #
407
+ # Copies a file content +src+ to +dest+. If +dest+ is a directory,
408
+ # copies +src+ to +dest/src+.
409
+ #
410
+ # If +src+ is a list of files, then +dest+ must be a directory.
411
+ #
412
+ # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
413
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
414
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
415
+ # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
416
+ #
417
+ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
418
+ fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
419
+ return if noop
420
+ fu_each_src_dest(src, dest) do |s, d|
421
+ copy_file s, d, preserve
422
+ end
423
+ end
424
+ module_function :cp
425
+
426
+ alias copy cp
427
+ module_function :copy
428
+
429
+ #
430
+ # Copies +src+ to +dest+. If +src+ is a directory, this method copies
431
+ # all its contents recursively. If +dest+ is a directory, copies
432
+ # +src+ to +dest/src+.
433
+ #
434
+ # +src+ can be a list of files.
435
+ #
436
+ # # Installing Ruby library "mylib" under the site_ruby
437
+ # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
438
+ # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
439
+ #
440
+ # # Examples of copying several files to target directory.
441
+ # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
442
+ # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
443
+ #
444
+ # # If you want to copy all contents of a directory instead of the
445
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
446
+ # # use following code.
447
+ # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
448
+ # # but this doesn't.
449
+ #
450
+ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
451
+ dereference_root: true, remove_destination: nil)
452
+ fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
453
+ return if noop
454
+ fu_each_src_dest(src, dest) do |s, d|
455
+ copy_entry s, d, preserve, dereference_root, remove_destination
456
+ end
457
+ end
458
+ module_function :cp_r
459
+
460
+ #
461
+ # Copies a file system entry +src+ to +dest+.
462
+ # If +src+ is a directory, this method copies its contents recursively.
463
+ # This method preserves file types, c.f. symlink, directory...
464
+ # (FIFO, device files and etc. are not supported yet)
465
+ #
466
+ # Both of +src+ and +dest+ must be a path name.
467
+ # +src+ must exist, +dest+ must not exist.
468
+ #
469
+ # If +preserve+ is true, this method preserves owner, group, and
470
+ # modified time. Permissions are copied regardless +preserve+.
471
+ #
472
+ # If +dereference_root+ is true, this method dereference tree root.
473
+ #
474
+ # If +remove_destination+ is true, this method removes each destination file before copy.
475
+ #
476
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
477
+ Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
478
+ destent = Entry_.new(dest, ent.rel, false)
479
+ File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
480
+ ent.copy destent.path
481
+ end, proc do |ent|
482
+ destent = Entry_.new(dest, ent.rel, false)
483
+ ent.copy_metadata destent.path if preserve
484
+ end)
485
+ end
486
+ module_function :copy_entry
487
+
488
+ #
489
+ # Copies file contents of +src+ to +dest+.
490
+ # Both of +src+ and +dest+ must be a path name.
491
+ #
492
+ def copy_file(src, dest, preserve = false, dereference = true)
493
+ ent = Entry_.new(src, nil, dereference)
494
+ ent.copy_file dest
495
+ ent.copy_metadata dest if preserve
496
+ end
497
+ module_function :copy_file
498
+
499
+ #
500
+ # Copies stream +src+ to +dest+.
501
+ # +src+ must respond to #read(n) and
502
+ # +dest+ must respond to #write(str).
503
+ #
504
+ def copy_stream(src, dest)
505
+ IO.copy_stream(src, dest)
506
+ end
507
+ module_function :copy_stream
508
+
509
+ #
510
+ # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
511
+ # disk partition, the file is copied then the original file is removed.
512
+ #
513
+ # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
514
+ # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
515
+ #
516
+ # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
517
+ # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
518
+ #
519
+ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
520
+ fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
521
+ return if noop
522
+ fu_each_src_dest(src, dest) do |s, d|
523
+ destent = Entry_.new(d, nil, true)
524
+ begin
525
+ if destent.exist?
526
+ if destent.directory?
527
+ raise Errno::EEXIST, d
528
+ end
529
+ end
530
+ begin
531
+ File.rename s, d
532
+ rescue Errno::EXDEV,
533
+ Errno::EPERM # move from unencrypted to encrypted dir (ext4)
534
+ copy_entry s, d, true
535
+ if secure
536
+ remove_entry_secure s, force
537
+ else
538
+ remove_entry s, force
539
+ end
540
+ end
541
+ rescue SystemCallError
542
+ raise unless force
543
+ end
544
+ end
545
+ end
546
+ module_function :mv
547
+
548
+ alias move mv
549
+ module_function :move
550
+
551
+ #
552
+ # Remove file(s) specified in +list+. This method cannot remove directories.
553
+ # All StandardErrors are ignored when the :force option is set.
554
+ #
555
+ # Bundler::FileUtils.rm %w( junk.txt dust.txt )
556
+ # Bundler::FileUtils.rm Dir.glob('*.so')
557
+ # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
558
+ #
559
+ def rm(list, force: nil, noop: nil, verbose: nil)
560
+ list = fu_list(list)
561
+ fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
562
+ return if noop
563
+
564
+ list.each do |path|
565
+ remove_file path, force
566
+ end
567
+ end
568
+ module_function :rm
569
+
570
+ alias remove rm
571
+ module_function :remove
572
+
573
+ #
574
+ # Equivalent to
575
+ #
576
+ # Bundler::FileUtils.rm(list, :force => true)
577
+ #
578
+ def rm_f(list, noop: nil, verbose: nil)
579
+ rm list, force: true, noop: noop, verbose: verbose
580
+ end
581
+ module_function :rm_f
582
+
583
+ alias safe_unlink rm_f
584
+ module_function :safe_unlink
585
+
586
+ #
587
+ # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
588
+ # removes its all contents recursively. This method ignores
589
+ # StandardError when :force option is set.
590
+ #
591
+ # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
592
+ # Bundler::FileUtils.rm_r 'some_dir', :force => true
593
+ #
594
+ # WARNING: This method causes local vulnerability
595
+ # if one of parent directories or removing directory tree are world
596
+ # writable (including /tmp, whose permission is 1777), and the current
597
+ # process has strong privilege such as Unix super user (root), and the
598
+ # system has symbolic link. For secure removing, read the documentation
599
+ # of #remove_entry_secure carefully, and set :secure option to true.
600
+ # Default is :secure=>false.
601
+ #
602
+ # NOTE: This method calls #remove_entry_secure if :secure option is set.
603
+ # See also #remove_entry_secure.
604
+ #
605
+ def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
606
+ list = fu_list(list)
607
+ fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
608
+ return if noop
609
+ list.each do |path|
610
+ if secure
611
+ remove_entry_secure path, force
612
+ else
613
+ remove_entry path, force
614
+ end
615
+ end
616
+ end
617
+ module_function :rm_r
618
+
619
+ #
620
+ # Equivalent to
621
+ #
622
+ # Bundler::FileUtils.rm_r(list, :force => true)
623
+ #
624
+ # WARNING: This method causes local vulnerability.
625
+ # Read the documentation of #rm_r first.
626
+ #
627
+ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
628
+ rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
629
+ end
630
+ module_function :rm_rf
631
+
632
+ alias rmtree rm_rf
633
+ module_function :rmtree
634
+
635
+ #
636
+ # This method removes a file system entry +path+. +path+ shall be a
637
+ # regular file, a directory, or something. If +path+ is a directory,
638
+ # remove it recursively. This method is required to avoid TOCTTOU
639
+ # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
640
+ # #rm_r causes security hole when:
641
+ #
642
+ # * Parent directory is world writable (including /tmp).
643
+ # * Removing directory tree includes world writable directory.
644
+ # * The system has symbolic link.
645
+ #
646
+ # To avoid this security hole, this method applies special preprocess.
647
+ # If +path+ is a directory, this method chown(2) and chmod(2) all
648
+ # removing directories. This requires the current process is the
649
+ # owner of the removing whole directory tree, or is the super user (root).
650
+ #
651
+ # WARNING: You must ensure that *ALL* parent directories cannot be
652
+ # moved by other untrusted users. For example, parent directories
653
+ # should not be owned by untrusted users, and should not be world
654
+ # writable except when the sticky bit set.
655
+ #
656
+ # WARNING: Only the owner of the removing directory tree, or Unix super
657
+ # user (root) should invoke this method. Otherwise this method does not
658
+ # work.
659
+ #
660
+ # For details of this security vulnerability, see Perl's case:
661
+ #
662
+ # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
663
+ # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
664
+ #
665
+ # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
666
+ #
667
+ def remove_entry_secure(path, force = false)
668
+ unless fu_have_symlink?
669
+ remove_entry path, force
670
+ return
671
+ end
672
+ fullpath = File.expand_path(path)
673
+ st = File.lstat(fullpath)
674
+ unless st.directory?
675
+ File.unlink fullpath
676
+ return
677
+ end
678
+ # is a directory.
679
+ parent_st = File.stat(File.dirname(fullpath))
680
+ unless parent_st.world_writable?
681
+ remove_entry path, force
682
+ return
683
+ end
684
+ unless parent_st.sticky?
685
+ raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
686
+ end
687
+
688
+ # freeze tree root
689
+ euid = Process.euid
690
+ dot_file = fullpath + "/."
691
+ begin
692
+ File.open(dot_file) {|f|
693
+ unless fu_stat_identical_entry?(st, f.stat)
694
+ # symlink (TOC-to-TOU attack?)
695
+ File.unlink fullpath
696
+ return
697
+ end
698
+ f.chown euid, -1
699
+ f.chmod 0700
700
+ }
701
+ rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
702
+ File.lstat(dot_file).tap {|fstat|
703
+ unless fu_stat_identical_entry?(st, fstat)
704
+ # symlink (TOC-to-TOU attack?)
705
+ File.unlink fullpath
706
+ return
707
+ end
708
+ File.chown euid, -1, dot_file
709
+ File.chmod 0700, dot_file
710
+ }
711
+ end
712
+
713
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
714
+ # TOC-to-TOU attack?
715
+ File.unlink fullpath
716
+ return
717
+ end
718
+
719
+ # ---- tree root is frozen ----
720
+ root = Entry_.new(path)
721
+ root.preorder_traverse do |ent|
722
+ if ent.directory?
723
+ ent.chown euid, -1
724
+ ent.chmod 0700
725
+ end
726
+ end
727
+ root.postorder_traverse do |ent|
728
+ begin
729
+ ent.remove
730
+ rescue
731
+ raise unless force
732
+ end
733
+ end
734
+ rescue
735
+ raise unless force
736
+ end
737
+ module_function :remove_entry_secure
738
+
739
+ def fu_have_symlink? #:nodoc:
740
+ File.symlink nil, nil
741
+ rescue NotImplementedError
742
+ return false
743
+ rescue TypeError
744
+ return true
745
+ end
746
+ private_module_function :fu_have_symlink?
747
+
748
+ def fu_stat_identical_entry?(a, b) #:nodoc:
749
+ a.dev == b.dev and a.ino == b.ino
750
+ end
751
+ private_module_function :fu_stat_identical_entry?
752
+
753
+ #
754
+ # This method removes a file system entry +path+.
755
+ # +path+ might be a regular file, a directory, or something.
756
+ # If +path+ is a directory, remove it recursively.
757
+ #
758
+ # See also #remove_entry_secure.
759
+ #
760
+ def remove_entry(path, force = false)
761
+ Entry_.new(path).postorder_traverse do |ent|
762
+ begin
763
+ ent.remove
764
+ rescue
765
+ raise unless force
766
+ end
767
+ end
768
+ rescue
769
+ raise unless force
770
+ end
771
+ module_function :remove_entry
772
+
773
+ #
774
+ # Removes a file +path+.
775
+ # This method ignores StandardError if +force+ is true.
776
+ #
777
+ def remove_file(path, force = false)
778
+ Entry_.new(path).remove_file
779
+ rescue
780
+ raise unless force
781
+ end
782
+ module_function :remove_file
783
+
784
+ #
785
+ # Removes a directory +dir+ and its contents recursively.
786
+ # This method ignores StandardError if +force+ is true.
787
+ #
788
+ def remove_dir(path, force = false)
789
+ remove_entry path, force # FIXME?? check if it is a directory
790
+ end
791
+ module_function :remove_dir
792
+
793
+ #
794
+ # Returns true if the contents of a file +a+ and a file +b+ are identical.
795
+ #
796
+ # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
797
+ # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
798
+ #
799
+ def compare_file(a, b)
800
+ return false unless File.size(a) == File.size(b)
801
+ File.open(a, 'rb') {|fa|
802
+ File.open(b, 'rb') {|fb|
803
+ return compare_stream(fa, fb)
804
+ }
805
+ }
806
+ end
807
+ module_function :compare_file
808
+
809
+ alias identical? compare_file
810
+ alias cmp compare_file
811
+ module_function :identical?
812
+ module_function :cmp
813
+
814
+ #
815
+ # Returns true if the contents of a stream +a+ and +b+ are identical.
816
+ #
817
+ def compare_stream(a, b)
818
+ bsize = fu_stream_blksize(a, b)
819
+
820
+ if RUBY_VERSION > "2.4"
821
+ sa = String.new(capacity: bsize)
822
+ sb = String.new(capacity: bsize)
823
+ else
824
+ sa = String.new
825
+ sb = String.new
826
+ end
827
+
828
+ begin
829
+ a.read(bsize, sa)
830
+ b.read(bsize, sb)
831
+ return true if sa.empty? && sb.empty?
832
+ end while sa == sb
833
+ false
834
+ end
835
+ module_function :compare_stream
836
+
837
+ #
838
+ # If +src+ is not same as +dest+, copies it and changes the permission
839
+ # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
840
+ # This method removes destination before copy.
841
+ #
842
+ # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
843
+ # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
844
+ #
845
+ def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
846
+ noop: nil, verbose: nil)
847
+ if verbose
848
+ msg = +"install -c"
849
+ msg << ' -p' if preserve
850
+ msg << ' -m ' << mode_to_s(mode) if mode
851
+ msg << " -o #{owner}" if owner
852
+ msg << " -g #{group}" if group
853
+ msg << ' ' << [src,dest].flatten.join(' ')
854
+ fu_output_message msg
855
+ end
856
+ return if noop
857
+ uid = fu_get_uid(owner)
858
+ gid = fu_get_gid(group)
859
+ fu_each_src_dest(src, dest) do |s, d|
860
+ st = File.stat(s)
861
+ unless File.exist?(d) and compare_file(s, d)
862
+ remove_file d, true
863
+ copy_file s, d
864
+ File.utime st.atime, st.mtime, d if preserve
865
+ File.chmod fu_mode(mode, st), d if mode
866
+ File.chown uid, gid, d if uid or gid
867
+ end
868
+ end
869
+ end
870
+ module_function :install
871
+
872
+ def user_mask(target) #:nodoc:
873
+ target.each_char.inject(0) do |mask, chr|
874
+ case chr
875
+ when "u"
876
+ mask | 04700
877
+ when "g"
878
+ mask | 02070
879
+ when "o"
880
+ mask | 01007
881
+ when "a"
882
+ mask | 07777
883
+ else
884
+ raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
885
+ end
886
+ end
887
+ end
888
+ private_module_function :user_mask
889
+
890
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
891
+ case op
892
+ when '='
893
+ (mode & ~user_mask) | (user_mask & mode_mask)
894
+ when '+'
895
+ mode | (user_mask & mode_mask)
896
+ when '-'
897
+ mode & ~(user_mask & mode_mask)
898
+ end
899
+ end
900
+ private_module_function :apply_mask
901
+
902
+ def symbolic_modes_to_i(mode_sym, path) #:nodoc:
903
+ mode = if File::Stat === path
904
+ path.mode
905
+ else
906
+ File.stat(path).mode
907
+ end
908
+ mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
909
+ target, *actions = clause.split(/([=+-])/)
910
+ raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
911
+ target = 'a' if target.empty?
912
+ user_mask = user_mask(target)
913
+ actions.each_slice(2) do |op, perm|
914
+ need_apply = op == '='
915
+ mode_mask = (perm || '').each_char.inject(0) do |mask, chr|
916
+ case chr
917
+ when "r"
918
+ mask | 0444
919
+ when "w"
920
+ mask | 0222
921
+ when "x"
922
+ mask | 0111
923
+ when "X"
924
+ if FileTest.directory? path
925
+ mask | 0111
926
+ else
927
+ mask
928
+ end
929
+ when "s"
930
+ mask | 06000
931
+ when "t"
932
+ mask | 01000
933
+ when "u", "g", "o"
934
+ if mask.nonzero?
935
+ current_mode = apply_mask(current_mode, user_mask, op, mask)
936
+ end
937
+ need_apply = false
938
+ copy_mask = user_mask(chr)
939
+ (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
940
+ else
941
+ raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
942
+ end
943
+ end
944
+
945
+ if mode_mask.nonzero? || need_apply
946
+ current_mode = apply_mask(current_mode, user_mask, op, mode_mask)
947
+ end
948
+ end
949
+ current_mode
950
+ end
951
+ end
952
+ private_module_function :symbolic_modes_to_i
953
+
954
+ def fu_mode(mode, path) #:nodoc:
955
+ mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
956
+ end
957
+ private_module_function :fu_mode
958
+
959
+ def mode_to_s(mode) #:nodoc:
960
+ mode.is_a?(String) ? mode : "%o" % mode
961
+ end
962
+ private_module_function :mode_to_s
963
+
964
+ #
965
+ # Changes permission bits on the named files (in +list+) to the bit pattern
966
+ # represented by +mode+.
967
+ #
968
+ # +mode+ is the symbolic and absolute mode can be used.
969
+ #
970
+ # Absolute mode is
971
+ # Bundler::FileUtils.chmod 0755, 'somecommand'
972
+ # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
973
+ # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
974
+ #
975
+ # Symbolic mode is
976
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
977
+ # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
978
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
979
+ #
980
+ # "a" :: is user, group, other mask.
981
+ # "u" :: is user's mask.
982
+ # "g" :: is group's mask.
983
+ # "o" :: is other's mask.
984
+ # "w" :: is write permission.
985
+ # "r" :: is read permission.
986
+ # "x" :: is execute permission.
987
+ # "X" ::
988
+ # is execute permission for directories only, must be used in conjunction with "+"
989
+ # "s" :: is uid, gid.
990
+ # "t" :: is sticky bit.
991
+ # "+" :: is added to a class given the specified mode.
992
+ # "-" :: Is removed from a given class given mode.
993
+ # "=" :: Is the exact nature of the class will be given a specified mode.
994
+
995
+ def chmod(mode, list, noop: nil, verbose: nil)
996
+ list = fu_list(list)
997
+ fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
998
+ return if noop
999
+ list.each do |path|
1000
+ Entry_.new(path).chmod(fu_mode(mode, path))
1001
+ end
1002
+ end
1003
+ module_function :chmod
1004
+
1005
+ #
1006
+ # Changes permission bits on the named files (in +list+)
1007
+ # to the bit pattern represented by +mode+.
1008
+ #
1009
+ # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
1010
+ # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
1011
+ #
1012
+ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
1013
+ list = fu_list(list)
1014
+ fu_output_message sprintf('chmod -R%s %s %s',
1015
+ (force ? 'f' : ''),
1016
+ mode_to_s(mode), list.join(' ')) if verbose
1017
+ return if noop
1018
+ list.each do |root|
1019
+ Entry_.new(root).traverse do |ent|
1020
+ begin
1021
+ ent.chmod(fu_mode(mode, ent.path))
1022
+ rescue
1023
+ raise unless force
1024
+ end
1025
+ end
1026
+ end
1027
+ end
1028
+ module_function :chmod_R
1029
+
1030
+ #
1031
+ # Changes owner and group on the named files (in +list+)
1032
+ # to the user +user+ and the group +group+. +user+ and +group+
1033
+ # may be an ID (Integer/String) or a name (String).
1034
+ # If +user+ or +group+ is nil, this method does not change
1035
+ # the attribute.
1036
+ #
1037
+ # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
1038
+ # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
1039
+ #
1040
+ def chown(user, group, list, noop: nil, verbose: nil)
1041
+ list = fu_list(list)
1042
+ fu_output_message sprintf('chown %s %s',
1043
+ (group ? "#{user}:#{group}" : user || ':'),
1044
+ list.join(' ')) if verbose
1045
+ return if noop
1046
+ uid = fu_get_uid(user)
1047
+ gid = fu_get_gid(group)
1048
+ list.each do |path|
1049
+ Entry_.new(path).chown uid, gid
1050
+ end
1051
+ end
1052
+ module_function :chown
1053
+
1054
+ #
1055
+ # Changes owner and group on the named files (in +list+)
1056
+ # to the user +user+ and the group +group+ recursively.
1057
+ # +user+ and +group+ may be an ID (Integer/String) or
1058
+ # a name (String). If +user+ or +group+ is nil, this
1059
+ # method does not change the attribute.
1060
+ #
1061
+ # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
1062
+ # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
1063
+ #
1064
+ def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
1065
+ list = fu_list(list)
1066
+ fu_output_message sprintf('chown -R%s %s %s',
1067
+ (force ? 'f' : ''),
1068
+ (group ? "#{user}:#{group}" : user || ':'),
1069
+ list.join(' ')) if verbose
1070
+ return if noop
1071
+ uid = fu_get_uid(user)
1072
+ gid = fu_get_gid(group)
1073
+ list.each do |root|
1074
+ Entry_.new(root).traverse do |ent|
1075
+ begin
1076
+ ent.chown uid, gid
1077
+ rescue
1078
+ raise unless force
1079
+ end
1080
+ end
1081
+ end
1082
+ end
1083
+ module_function :chown_R
1084
+
1085
+ def fu_get_uid(user) #:nodoc:
1086
+ return nil unless user
1087
+ case user
1088
+ when Integer
1089
+ user
1090
+ when /\A\d+\z/
1091
+ user.to_i
1092
+ else
1093
+ require 'etc'
1094
+ Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
1095
+ end
1096
+ end
1097
+ private_module_function :fu_get_uid
1098
+
1099
+ def fu_get_gid(group) #:nodoc:
1100
+ return nil unless group
1101
+ case group
1102
+ when Integer
1103
+ group
1104
+ when /\A\d+\z/
1105
+ group.to_i
1106
+ else
1107
+ require 'etc'
1108
+ Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
1109
+ end
1110
+ end
1111
+ private_module_function :fu_get_gid
1112
+
1113
+ #
1114
+ # Updates modification time (mtime) and access time (atime) of file(s) in
1115
+ # +list+. Files are created if they don't exist.
1116
+ #
1117
+ # Bundler::FileUtils.touch 'timestamp'
1118
+ # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
1119
+ #
1120
+ def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
1121
+ list = fu_list(list)
1122
+ t = mtime
1123
+ if verbose
1124
+ fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
1125
+ end
1126
+ return if noop
1127
+ list.each do |path|
1128
+ created = nocreate
1129
+ begin
1130
+ File.utime(t, t, path)
1131
+ rescue Errno::ENOENT
1132
+ raise if created
1133
+ File.open(path, 'a') {
1134
+ ;
1135
+ }
1136
+ created = true
1137
+ retry if t
1138
+ end
1139
+ end
1140
+ end
1141
+ module_function :touch
1142
+
1143
+ private
1144
+
1145
+ module StreamUtils_
1146
+ private
1147
+
1148
+ case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
1149
+ when /mswin|mingw/
1150
+ def fu_windows?; true end
1151
+ else
1152
+ def fu_windows?; false end
1153
+ end
1154
+
1155
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
1156
+ IO.copy_stream(src, dest)
1157
+ end
1158
+
1159
+ def fu_stream_blksize(*streams)
1160
+ streams.each do |s|
1161
+ next unless s.respond_to?(:stat)
1162
+ size = fu_blksize(s.stat)
1163
+ return size if size
1164
+ end
1165
+ fu_default_blksize()
1166
+ end
1167
+
1168
+ def fu_blksize(st)
1169
+ s = st.blksize
1170
+ return nil unless s
1171
+ return nil if s == 0
1172
+ s
1173
+ end
1174
+
1175
+ def fu_default_blksize
1176
+ 1024
1177
+ end
1178
+ end
1179
+
1180
+ include StreamUtils_
1181
+ extend StreamUtils_
1182
+
1183
+ class Entry_ #:nodoc: internal use only
1184
+ include StreamUtils_
1185
+
1186
+ def initialize(a, b = nil, deref = false)
1187
+ @prefix = @rel = @path = nil
1188
+ if b
1189
+ @prefix = a
1190
+ @rel = b
1191
+ else
1192
+ @path = a
1193
+ end
1194
+ @deref = deref
1195
+ @stat = nil
1196
+ @lstat = nil
1197
+ end
1198
+
1199
+ def inspect
1200
+ "\#<#{self.class} #{path()}>"
1201
+ end
1202
+
1203
+ def path
1204
+ if @path
1205
+ File.path(@path)
1206
+ else
1207
+ join(@prefix, @rel)
1208
+ end
1209
+ end
1210
+
1211
+ def prefix
1212
+ @prefix || @path
1213
+ end
1214
+
1215
+ def rel
1216
+ @rel
1217
+ end
1218
+
1219
+ def dereference?
1220
+ @deref
1221
+ end
1222
+
1223
+ def exist?
1224
+ begin
1225
+ lstat
1226
+ true
1227
+ rescue Errno::ENOENT
1228
+ false
1229
+ end
1230
+ end
1231
+
1232
+ def file?
1233
+ s = lstat!
1234
+ s and s.file?
1235
+ end
1236
+
1237
+ def directory?
1238
+ s = lstat!
1239
+ s and s.directory?
1240
+ end
1241
+
1242
+ def symlink?
1243
+ s = lstat!
1244
+ s and s.symlink?
1245
+ end
1246
+
1247
+ def chardev?
1248
+ s = lstat!
1249
+ s and s.chardev?
1250
+ end
1251
+
1252
+ def blockdev?
1253
+ s = lstat!
1254
+ s and s.blockdev?
1255
+ end
1256
+
1257
+ def socket?
1258
+ s = lstat!
1259
+ s and s.socket?
1260
+ end
1261
+
1262
+ def pipe?
1263
+ s = lstat!
1264
+ s and s.pipe?
1265
+ end
1266
+
1267
+ S_IF_DOOR = 0xD000
1268
+
1269
+ def door?
1270
+ s = lstat!
1271
+ s and (s.mode & 0xF000 == S_IF_DOOR)
1272
+ end
1273
+
1274
+ def entries
1275
+ opts = {}
1276
+ opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
1277
+
1278
+ files = if Dir.respond_to?(:children)
1279
+ Dir.children(path, opts)
1280
+ else
1281
+ Dir.entries(path(), opts)
1282
+ .reject {|n| n == '.' or n == '..' }
1283
+ end
1284
+
1285
+ files.map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
1286
+ end
1287
+
1288
+ def stat
1289
+ return @stat if @stat
1290
+ if lstat() and lstat().symlink?
1291
+ @stat = File.stat(path())
1292
+ else
1293
+ @stat = lstat()
1294
+ end
1295
+ @stat
1296
+ end
1297
+
1298
+ def stat!
1299
+ return @stat if @stat
1300
+ if lstat! and lstat!.symlink?
1301
+ @stat = File.stat(path())
1302
+ else
1303
+ @stat = lstat!
1304
+ end
1305
+ @stat
1306
+ rescue SystemCallError
1307
+ nil
1308
+ end
1309
+
1310
+ def lstat
1311
+ if dereference?
1312
+ @lstat ||= File.stat(path())
1313
+ else
1314
+ @lstat ||= File.lstat(path())
1315
+ end
1316
+ end
1317
+
1318
+ def lstat!
1319
+ lstat()
1320
+ rescue SystemCallError
1321
+ nil
1322
+ end
1323
+
1324
+ def chmod(mode)
1325
+ if symlink?
1326
+ File.lchmod mode, path() if have_lchmod?
1327
+ else
1328
+ File.chmod mode, path()
1329
+ end
1330
+ end
1331
+
1332
+ def chown(uid, gid)
1333
+ if symlink?
1334
+ File.lchown uid, gid, path() if have_lchown?
1335
+ else
1336
+ File.chown uid, gid, path()
1337
+ end
1338
+ end
1339
+
1340
+ def link(dest)
1341
+ case
1342
+ when directory?
1343
+ if !File.exist?(dest) and descendant_directory?(dest, path)
1344
+ raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
1345
+ end
1346
+ begin
1347
+ Dir.mkdir dest
1348
+ rescue
1349
+ raise unless File.directory?(dest)
1350
+ end
1351
+ else
1352
+ File.link path(), dest
1353
+ end
1354
+ end
1355
+
1356
+ def copy(dest)
1357
+ lstat
1358
+ case
1359
+ when file?
1360
+ copy_file dest
1361
+ when directory?
1362
+ if !File.exist?(dest) and descendant_directory?(dest, path)
1363
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
1364
+ end
1365
+ begin
1366
+ Dir.mkdir dest
1367
+ rescue
1368
+ raise unless File.directory?(dest)
1369
+ end
1370
+ when symlink?
1371
+ File.symlink File.readlink(path()), dest
1372
+ when chardev?
1373
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
1374
+ mknod dest, ?c, 0666, lstat().rdev
1375
+ when blockdev?
1376
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
1377
+ mknod dest, ?b, 0666, lstat().rdev
1378
+ when socket?
1379
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
1380
+ mknod dest, nil, lstat().mode, 0
1381
+ when pipe?
1382
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
1383
+ mkfifo dest, 0666
1384
+ when door?
1385
+ raise "cannot handle door: #{path()}"
1386
+ else
1387
+ raise "unknown file type: #{path()}"
1388
+ end
1389
+ end
1390
+
1391
+ def copy_file(dest)
1392
+ File.open(path()) do |s|
1393
+ File.open(dest, 'wb', s.stat.mode) do |f|
1394
+ IO.copy_stream(s, f)
1395
+ end
1396
+ end
1397
+ end
1398
+
1399
+ def copy_metadata(path)
1400
+ st = lstat()
1401
+ if !st.symlink?
1402
+ File.utime st.atime, st.mtime, path
1403
+ end
1404
+ mode = st.mode
1405
+ begin
1406
+ if st.symlink?
1407
+ begin
1408
+ File.lchown st.uid, st.gid, path
1409
+ rescue NotImplementedError
1410
+ end
1411
+ else
1412
+ File.chown st.uid, st.gid, path
1413
+ end
1414
+ rescue Errno::EPERM, Errno::EACCES
1415
+ # clear setuid/setgid
1416
+ mode &= 01777
1417
+ end
1418
+ if st.symlink?
1419
+ begin
1420
+ File.lchmod mode, path
1421
+ rescue NotImplementedError
1422
+ end
1423
+ else
1424
+ File.chmod mode, path
1425
+ end
1426
+ end
1427
+
1428
+ def remove
1429
+ if directory?
1430
+ remove_dir1
1431
+ else
1432
+ remove_file
1433
+ end
1434
+ end
1435
+
1436
+ def remove_dir1
1437
+ platform_support {
1438
+ Dir.rmdir path().chomp(?/)
1439
+ }
1440
+ end
1441
+
1442
+ def remove_file
1443
+ platform_support {
1444
+ File.unlink path
1445
+ }
1446
+ end
1447
+
1448
+ def platform_support
1449
+ return yield unless fu_windows?
1450
+ first_time_p = true
1451
+ begin
1452
+ yield
1453
+ rescue Errno::ENOENT
1454
+ raise
1455
+ rescue => err
1456
+ if first_time_p
1457
+ first_time_p = false
1458
+ begin
1459
+ File.chmod 0700, path() # Windows does not have symlink
1460
+ retry
1461
+ rescue SystemCallError
1462
+ end
1463
+ end
1464
+ raise err
1465
+ end
1466
+ end
1467
+
1468
+ def preorder_traverse
1469
+ stack = [self]
1470
+ while ent = stack.pop
1471
+ yield ent
1472
+ stack.concat ent.entries.reverse if ent.directory?
1473
+ end
1474
+ end
1475
+
1476
+ alias traverse preorder_traverse
1477
+
1478
+ def postorder_traverse
1479
+ if directory?
1480
+ entries().each do |ent|
1481
+ ent.postorder_traverse do |e|
1482
+ yield e
1483
+ end
1484
+ end
1485
+ end
1486
+ ensure
1487
+ yield self
1488
+ end
1489
+
1490
+ def wrap_traverse(pre, post)
1491
+ pre.call self
1492
+ if directory?
1493
+ entries.each do |ent|
1494
+ ent.wrap_traverse pre, post
1495
+ end
1496
+ end
1497
+ post.call self
1498
+ end
1499
+
1500
+ private
1501
+
1502
+ $fileutils_rb_have_lchmod = nil
1503
+
1504
+ def have_lchmod?
1505
+ # This is not MT-safe, but it does not matter.
1506
+ if $fileutils_rb_have_lchmod == nil
1507
+ $fileutils_rb_have_lchmod = check_have_lchmod?
1508
+ end
1509
+ $fileutils_rb_have_lchmod
1510
+ end
1511
+
1512
+ def check_have_lchmod?
1513
+ return false unless File.respond_to?(:lchmod)
1514
+ File.lchmod 0
1515
+ return true
1516
+ rescue NotImplementedError
1517
+ return false
1518
+ end
1519
+
1520
+ $fileutils_rb_have_lchown = nil
1521
+
1522
+ def have_lchown?
1523
+ # This is not MT-safe, but it does not matter.
1524
+ if $fileutils_rb_have_lchown == nil
1525
+ $fileutils_rb_have_lchown = check_have_lchown?
1526
+ end
1527
+ $fileutils_rb_have_lchown
1528
+ end
1529
+
1530
+ def check_have_lchown?
1531
+ return false unless File.respond_to?(:lchown)
1532
+ File.lchown nil, nil
1533
+ return true
1534
+ rescue NotImplementedError
1535
+ return false
1536
+ end
1537
+
1538
+ def join(dir, base)
1539
+ return File.path(dir) if not base or base == '.'
1540
+ return File.path(base) if not dir or dir == '.'
1541
+ File.join(dir, base)
1542
+ end
1543
+
1544
+ if File::ALT_SEPARATOR
1545
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
1546
+ else
1547
+ DIRECTORY_TERM = "(?=/|\\z)"
1548
+ end
1549
+ SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
1550
+
1551
+ def descendant_directory?(descendant, ascendant)
1552
+ /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
1553
+ end
1554
+ end # class Entry_
1555
+
1556
+ def fu_list(arg) #:nodoc:
1557
+ [arg].flatten.map {|path| File.path(path) }
1558
+ end
1559
+ private_module_function :fu_list
1560
+
1561
+ def fu_each_src_dest(src, dest) #:nodoc:
1562
+ fu_each_src_dest0(src, dest) do |s, d|
1563
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
1564
+ yield s, d
1565
+ end
1566
+ end
1567
+ private_module_function :fu_each_src_dest
1568
+
1569
+ def fu_each_src_dest0(src, dest) #:nodoc:
1570
+ if tmp = Array.try_convert(src)
1571
+ tmp.each do |s|
1572
+ s = File.path(s)
1573
+ yield s, File.join(dest, File.basename(s))
1574
+ end
1575
+ else
1576
+ src = File.path(src)
1577
+ if File.directory?(dest)
1578
+ yield src, File.join(dest, File.basename(src))
1579
+ else
1580
+ yield src, File.path(dest)
1581
+ end
1582
+ end
1583
+ end
1584
+ private_module_function :fu_each_src_dest0
1585
+
1586
+ def fu_same?(a, b) #:nodoc:
1587
+ File.identical?(a, b)
1588
+ end
1589
+ private_module_function :fu_same?
1590
+
1591
+ @fileutils_output = $stderr
1592
+ @fileutils_label = ''
1593
+
1594
+ def fu_output_message(msg) #:nodoc:
1595
+ @fileutils_output ||= $stderr
1596
+ @fileutils_label ||= ''
1597
+ @fileutils_output.puts @fileutils_label + msg
1598
+ end
1599
+ private_module_function :fu_output_message
1600
+
1601
+ # This hash table holds command options.
1602
+ OPT_TABLE = {} #:nodoc: internal use only
1603
+ (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
1604
+ (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact!
1605
+ tbl
1606
+ }
1607
+
1608
+ #
1609
+ # Returns an Array of method names which have any options.
1610
+ #
1611
+ # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
1612
+ #
1613
+ def self.commands
1614
+ OPT_TABLE.keys
1615
+ end
1616
+
1617
+ #
1618
+ # Returns an Array of option names.
1619
+ #
1620
+ # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
1621
+ #
1622
+ def self.options
1623
+ OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
1624
+ end
1625
+
1626
+ #
1627
+ # Returns true if the method +mid+ have an option +opt+.
1628
+ #
1629
+ # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
1630
+ # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
1631
+ # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
1632
+ #
1633
+ def self.have_option?(mid, opt)
1634
+ li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
1635
+ li.include?(opt)
1636
+ end
1637
+
1638
+ #
1639
+ # Returns an Array of option names of the method +mid+.
1640
+ #
1641
+ # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
1642
+ #
1643
+ def self.options_of(mid)
1644
+ OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
1645
+ end
1646
+
1647
+ #
1648
+ # Returns an Array of method names which have the option +opt+.
1649
+ #
1650
+ # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
1651
+ #
1652
+ def self.collect_method(opt)
1653
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
1654
+ end
1655
+
1656
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
1657
+ module LowMethods
1658
+ private
1659
+ def _do_nothing(*)end
1660
+ ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
1661
+ end
1662
+
1663
+ METHODS = singleton_methods() - [:private_module_function,
1664
+ :commands, :options, :have_option?, :options_of, :collect_method]
1665
+
1666
+ #
1667
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
1668
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to
1669
+ # methods in Bundler::FileUtils.
1670
+ #
1671
+ module Verbose
1672
+ include Bundler::FileUtils
1673
+ @fileutils_output = $stderr
1674
+ @fileutils_label = ''
1675
+ names = ::Bundler::FileUtils.collect_method(:verbose)
1676
+ names.each do |name|
1677
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1678
+ def #{name}(*args, **options)
1679
+ super(*args, **options, verbose: true)
1680
+ end
1681
+ EOS
1682
+ end
1683
+ private(*names)
1684
+ extend self
1685
+ class << self
1686
+ public(*::Bundler::FileUtils::METHODS)
1687
+ end
1688
+ end
1689
+
1690
+ #
1691
+ # This module has all methods of Bundler::FileUtils module, but never changes
1692
+ # files/directories. This equates to passing the <tt>:noop</tt> flag
1693
+ # to methods in Bundler::FileUtils.
1694
+ #
1695
+ module NoWrite
1696
+ include Bundler::FileUtils
1697
+ include LowMethods
1698
+ @fileutils_output = $stderr
1699
+ @fileutils_label = ''
1700
+ names = ::Bundler::FileUtils.collect_method(:noop)
1701
+ names.each do |name|
1702
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1703
+ def #{name}(*args, **options)
1704
+ super(*args, **options, noop: true)
1705
+ end
1706
+ EOS
1707
+ end
1708
+ private(*names)
1709
+ extend self
1710
+ class << self
1711
+ public(*::Bundler::FileUtils::METHODS)
1712
+ end
1713
+ end
1714
+
1715
+ #
1716
+ # This module has all methods of Bundler::FileUtils module, but never changes
1717
+ # files/directories, with printing message before acting.
1718
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
1719
+ # to methods in Bundler::FileUtils.
1720
+ #
1721
+ module DryRun
1722
+ include Bundler::FileUtils
1723
+ include LowMethods
1724
+ @fileutils_output = $stderr
1725
+ @fileutils_label = ''
1726
+ names = ::Bundler::FileUtils.collect_method(:noop)
1727
+ names.each do |name|
1728
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1729
+ def #{name}(*args, **options)
1730
+ super(*args, **options, noop: true, verbose: true)
1731
+ end
1732
+ EOS
1733
+ end
1734
+ private(*names)
1735
+ extend self
1736
+ class << self
1737
+ public(*::Bundler::FileUtils::METHODS)
1738
+ end
1739
+ end
1740
+
1741
+ end