bundler_pack 4.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5670 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +58 -0
  5. data/bundler.gemspec +46 -0
  6. data/exe/bundle +29 -0
  7. data/exe/bundler +4 -0
  8. data/lib/bundler/.document +1 -0
  9. data/lib/bundler/build_metadata.rb +44 -0
  10. data/lib/bundler/capistrano.rb +4 -0
  11. data/lib/bundler/checksum.rb +270 -0
  12. data/lib/bundler/ci_detector.rb +75 -0
  13. data/lib/bundler/cli/add.rb +62 -0
  14. data/lib/bundler/cli/binstubs.rb +57 -0
  15. data/lib/bundler/cli/cache.rb +32 -0
  16. data/lib/bundler/cli/check.rb +40 -0
  17. data/lib/bundler/cli/clean.rb +25 -0
  18. data/lib/bundler/cli/common.rb +161 -0
  19. data/lib/bundler/cli/config.rb +208 -0
  20. data/lib/bundler/cli/console.rb +47 -0
  21. data/lib/bundler/cli/doctor/diagnose.rb +167 -0
  22. data/lib/bundler/cli/doctor/ssl.rb +249 -0
  23. data/lib/bundler/cli/doctor.rb +33 -0
  24. data/lib/bundler/cli/exec.rb +114 -0
  25. data/lib/bundler/cli/fund.rb +36 -0
  26. data/lib/bundler/cli/gem.rb +493 -0
  27. data/lib/bundler/cli/info.rb +83 -0
  28. data/lib/bundler/cli/init.rb +51 -0
  29. data/lib/bundler/cli/install.rb +127 -0
  30. data/lib/bundler/cli/issue.rb +41 -0
  31. data/lib/bundler/cli/list.rb +97 -0
  32. data/lib/bundler/cli/lock.rb +94 -0
  33. data/lib/bundler/cli/open.rb +29 -0
  34. data/lib/bundler/cli/outdated.rb +337 -0
  35. data/lib/bundler/cli/platform.rb +48 -0
  36. data/lib/bundler/cli/plugin.rb +39 -0
  37. data/lib/bundler/cli/pristine.rb +64 -0
  38. data/lib/bundler/cli/remove.rb +17 -0
  39. data/lib/bundler/cli/show.rb +71 -0
  40. data/lib/bundler/cli/update.rb +125 -0
  41. data/lib/bundler/cli.rb +829 -0
  42. data/lib/bundler/compact_index_client/cache.rb +96 -0
  43. data/lib/bundler/compact_index_client/cache_file.rb +148 -0
  44. data/lib/bundler/compact_index_client/parser.rb +87 -0
  45. data/lib/bundler/compact_index_client/updater.rb +105 -0
  46. data/lib/bundler/compact_index_client.rb +92 -0
  47. data/lib/bundler/constants.rb +14 -0
  48. data/lib/bundler/current_ruby.rb +94 -0
  49. data/lib/bundler/definition.rb +1304 -0
  50. data/lib/bundler/dependency.rb +151 -0
  51. data/lib/bundler/deployment.rb +6 -0
  52. data/lib/bundler/deprecate.rb +44 -0
  53. data/lib/bundler/digest.rb +71 -0
  54. data/lib/bundler/dsl.rb +642 -0
  55. data/lib/bundler/endpoint_specification.rb +184 -0
  56. data/lib/bundler/env.rb +148 -0
  57. data/lib/bundler/environment_preserver.rb +69 -0
  58. data/lib/bundler/errors.rb +277 -0
  59. data/lib/bundler/feature_flag.rb +20 -0
  60. data/lib/bundler/fetcher/base.rb +55 -0
  61. data/lib/bundler/fetcher/compact_index.rb +133 -0
  62. data/lib/bundler/fetcher/dependency.rb +85 -0
  63. data/lib/bundler/fetcher/downloader.rb +116 -0
  64. data/lib/bundler/fetcher/gem_remote_fetcher.rb +24 -0
  65. data/lib/bundler/fetcher/index.rb +25 -0
  66. data/lib/bundler/fetcher.rb +365 -0
  67. data/lib/bundler/force_platform.rb +16 -0
  68. data/lib/bundler/friendly_errors.rb +127 -0
  69. data/lib/bundler/gem_helper.rb +237 -0
  70. data/lib/bundler/gem_tasks.rb +7 -0
  71. data/lib/bundler/gem_version_promoter.rb +147 -0
  72. data/lib/bundler/index.rb +203 -0
  73. data/lib/bundler/injector.rb +284 -0
  74. data/lib/bundler/inline.rb +106 -0
  75. data/lib/bundler/installer/gem_installer.rb +88 -0
  76. data/lib/bundler/installer/parallel_installer.rb +280 -0
  77. data/lib/bundler/installer/standalone.rb +113 -0
  78. data/lib/bundler/installer.rb +241 -0
  79. data/lib/bundler/lazy_specification.rb +270 -0
  80. data/lib/bundler/lockfile_generator.rb +119 -0
  81. data/lib/bundler/lockfile_parser.rb +328 -0
  82. data/lib/bundler/man/.document +1 -0
  83. data/lib/bundler/man/bundle-add.1 +79 -0
  84. data/lib/bundler/man/bundle-add.1.ronn +92 -0
  85. data/lib/bundler/man/bundle-binstubs.1 +30 -0
  86. data/lib/bundler/man/bundle-binstubs.1.ronn +42 -0
  87. data/lib/bundler/man/bundle-cache.1 +56 -0
  88. data/lib/bundler/man/bundle-cache.1.ronn +95 -0
  89. data/lib/bundler/man/bundle-check.1 +21 -0
  90. data/lib/bundler/man/bundle-check.1.ronn +26 -0
  91. data/lib/bundler/man/bundle-clean.1 +17 -0
  92. data/lib/bundler/man/bundle-clean.1.ronn +18 -0
  93. data/lib/bundler/man/bundle-config.1 +339 -0
  94. data/lib/bundler/man/bundle-config.1.ronn +455 -0
  95. data/lib/bundler/man/bundle-console.1 +33 -0
  96. data/lib/bundler/man/bundle-console.1.ronn +39 -0
  97. data/lib/bundler/man/bundle-doctor.1 +69 -0
  98. data/lib/bundler/man/bundle-doctor.1.ronn +77 -0
  99. data/lib/bundler/man/bundle-env.1 +9 -0
  100. data/lib/bundler/man/bundle-env.1.ronn +10 -0
  101. data/lib/bundler/man/bundle-exec.1 +104 -0
  102. data/lib/bundler/man/bundle-exec.1.ronn +150 -0
  103. data/lib/bundler/man/bundle-fund.1 +22 -0
  104. data/lib/bundler/man/bundle-fund.1.ronn +25 -0
  105. data/lib/bundler/man/bundle-gem.1 +107 -0
  106. data/lib/bundler/man/bundle-gem.1.ronn +150 -0
  107. data/lib/bundler/man/bundle-help.1 +9 -0
  108. data/lib/bundler/man/bundle-help.1.ronn +12 -0
  109. data/lib/bundler/man/bundle-info.1 +17 -0
  110. data/lib/bundler/man/bundle-info.1.ronn +21 -0
  111. data/lib/bundler/man/bundle-init.1 +20 -0
  112. data/lib/bundler/man/bundle-init.1.ronn +32 -0
  113. data/lib/bundler/man/bundle-install.1 +178 -0
  114. data/lib/bundler/man/bundle-install.1.ronn +314 -0
  115. data/lib/bundler/man/bundle-issue.1 +45 -0
  116. data/lib/bundler/man/bundle-issue.1.ronn +37 -0
  117. data/lib/bundler/man/bundle-licenses.1 +9 -0
  118. data/lib/bundler/man/bundle-licenses.1.ronn +10 -0
  119. data/lib/bundler/man/bundle-list.1 +40 -0
  120. data/lib/bundler/man/bundle-list.1.ronn +41 -0
  121. data/lib/bundler/man/bundle-lock.1 +75 -0
  122. data/lib/bundler/man/bundle-lock.1.ronn +115 -0
  123. data/lib/bundler/man/bundle-open.1 +32 -0
  124. data/lib/bundler/man/bundle-open.1.ronn +28 -0
  125. data/lib/bundler/man/bundle-outdated.1 +106 -0
  126. data/lib/bundler/man/bundle-outdated.1.ronn +117 -0
  127. data/lib/bundler/man/bundle-platform.1 +49 -0
  128. data/lib/bundler/man/bundle-platform.1.ronn +49 -0
  129. data/lib/bundler/man/bundle-plugin.1 +76 -0
  130. data/lib/bundler/man/bundle-plugin.1.ronn +84 -0
  131. data/lib/bundler/man/bundle-pristine.1 +23 -0
  132. data/lib/bundler/man/bundle-pristine.1.ronn +34 -0
  133. data/lib/bundler/man/bundle-remove.1 +15 -0
  134. data/lib/bundler/man/bundle-remove.1.ronn +16 -0
  135. data/lib/bundler/man/bundle-show.1 +16 -0
  136. data/lib/bundler/man/bundle-show.1.ronn +21 -0
  137. data/lib/bundler/man/bundle-update.1 +284 -0
  138. data/lib/bundler/man/bundle-update.1.ronn +367 -0
  139. data/lib/bundler/man/bundle-version.1 +22 -0
  140. data/lib/bundler/man/bundle-version.1.ronn +24 -0
  141. data/lib/bundler/man/bundle.1 +93 -0
  142. data/lib/bundler/man/bundle.1.ronn +107 -0
  143. data/lib/bundler/man/gemfile.5 +503 -0
  144. data/lib/bundler/man/gemfile.5.ronn +586 -0
  145. data/lib/bundler/man/index.txt +31 -0
  146. data/lib/bundler/match_metadata.rb +30 -0
  147. data/lib/bundler/match_platform.rb +42 -0
  148. data/lib/bundler/match_remote_metadata.rb +29 -0
  149. data/lib/bundler/materialization.rb +59 -0
  150. data/lib/bundler/mirror.rb +221 -0
  151. data/lib/bundler/plugin/api/source.rb +330 -0
  152. data/lib/bundler/plugin/api.rb +81 -0
  153. data/lib/bundler/plugin/dsl.rb +53 -0
  154. data/lib/bundler/plugin/events.rb +85 -0
  155. data/lib/bundler/plugin/index.rb +203 -0
  156. data/lib/bundler/plugin/installer/git.rb +34 -0
  157. data/lib/bundler/plugin/installer/path.rb +26 -0
  158. data/lib/bundler/plugin/installer/rubygems.rb +19 -0
  159. data/lib/bundler/plugin/installer.rb +123 -0
  160. data/lib/bundler/plugin/source_list.rb +31 -0
  161. data/lib/bundler/plugin/unloaded_source.rb +25 -0
  162. data/lib/bundler/plugin.rb +387 -0
  163. data/lib/bundler/process_lock.rb +20 -0
  164. data/lib/bundler/remote_specification.rb +126 -0
  165. data/lib/bundler/resolver/base.rb +127 -0
  166. data/lib/bundler/resolver/candidate.rb +85 -0
  167. data/lib/bundler/resolver/incompatibility.rb +15 -0
  168. data/lib/bundler/resolver/package.rb +95 -0
  169. data/lib/bundler/resolver/root.rb +25 -0
  170. data/lib/bundler/resolver/spec_group.rb +74 -0
  171. data/lib/bundler/resolver/strategy.rb +43 -0
  172. data/lib/bundler/resolver.rb +603 -0
  173. data/lib/bundler/retry.rb +92 -0
  174. data/lib/bundler/ruby_dsl.rb +67 -0
  175. data/lib/bundler/ruby_version.rb +135 -0
  176. data/lib/bundler/rubygems_ext.rb +503 -0
  177. data/lib/bundler/rubygems_gem_installer.rb +206 -0
  178. data/lib/bundler/rubygems_integration.rb +456 -0
  179. data/lib/bundler/runtime.rb +331 -0
  180. data/lib/bundler/safe_marshal.rb +31 -0
  181. data/lib/bundler/self_manager.rb +197 -0
  182. data/lib/bundler/settings/validator.rb +86 -0
  183. data/lib/bundler/settings.rb +585 -0
  184. data/lib/bundler/setup.rb +39 -0
  185. data/lib/bundler/shared_helpers.rb +392 -0
  186. data/lib/bundler/source/gemspec.rb +19 -0
  187. data/lib/bundler/source/git/git_proxy.rb +509 -0
  188. data/lib/bundler/source/git.rb +451 -0
  189. data/lib/bundler/source/metadata.rb +67 -0
  190. data/lib/bundler/source/path/installer.rb +53 -0
  191. data/lib/bundler/source/path.rb +256 -0
  192. data/lib/bundler/source/rubygems/remote.rb +86 -0
  193. data/lib/bundler/source/rubygems.rb +606 -0
  194. data/lib/bundler/source/rubygems_aggregate.rb +71 -0
  195. data/lib/bundler/source.rb +120 -0
  196. data/lib/bundler/source_list.rb +240 -0
  197. data/lib/bundler/source_map.rb +72 -0
  198. data/lib/bundler/spec_set.rb +390 -0
  199. data/lib/bundler/stub_specification.rb +147 -0
  200. data/lib/bundler/templates/.document +1 -0
  201. data/lib/bundler/templates/Executable +16 -0
  202. data/lib/bundler/templates/Executable.standalone +14 -0
  203. data/lib/bundler/templates/Gemfile +5 -0
  204. data/lib/bundler/templates/newgem/CHANGELOG.md.tt +5 -0
  205. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +10 -0
  206. data/lib/bundler/templates/newgem/Cargo.toml.tt +13 -0
  207. data/lib/bundler/templates/newgem/Gemfile.tt +24 -0
  208. data/lib/bundler/templates/newgem/LICENSE.txt.tt +21 -0
  209. data/lib/bundler/templates/newgem/README.md.tt +49 -0
  210. data/lib/bundler/templates/newgem/Rakefile.tt +72 -0
  211. data/lib/bundler/templates/newgem/bin/console.tt +11 -0
  212. data/lib/bundler/templates/newgem/bin/setup.tt +8 -0
  213. data/lib/bundler/templates/newgem/circleci/config.yml.tt +37 -0
  214. data/lib/bundler/templates/newgem/exe/newgem.tt +3 -0
  215. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +22 -0
  216. data/lib/bundler/templates/newgem/ext/newgem/build.rs.tt +5 -0
  217. data/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt +10 -0
  218. data/lib/bundler/templates/newgem/ext/newgem/extconf-go.rb.tt +11 -0
  219. data/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt +6 -0
  220. data/lib/bundler/templates/newgem/ext/newgem/go.mod.tt +5 -0
  221. data/lib/bundler/templates/newgem/ext/newgem/newgem-go.c.tt +2 -0
  222. data/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  223. data/lib/bundler/templates/newgem/ext/newgem/newgem.go.tt +31 -0
  224. data/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  225. data/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +23 -0
  226. data/lib/bundler/templates/newgem/github/workflows/build-gems.yml.tt +69 -0
  227. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +48 -0
  228. data/lib/bundler/templates/newgem/gitignore.tt +23 -0
  229. data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +27 -0
  230. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +9 -0
  231. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +15 -0
  232. data/lib/bundler/templates/newgem/newgem.gemspec.tt +58 -0
  233. data/lib/bundler/templates/newgem/rspec.tt +3 -0
  234. data/lib/bundler/templates/newgem/rubocop.yml.tt +8 -0
  235. data/lib/bundler/templates/newgem/sig/newgem.rbs.tt +8 -0
  236. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +19 -0
  237. data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +15 -0
  238. data/lib/bundler/templates/newgem/standard.yml.tt +3 -0
  239. data/lib/bundler/templates/newgem/test/minitest/test_helper.rb.tt +6 -0
  240. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +19 -0
  241. data/lib/bundler/templates/newgem/test/test-unit/newgem_test.rb.tt +15 -0
  242. data/lib/bundler/templates/newgem/test/test-unit/test_helper.rb.tt +6 -0
  243. data/lib/bundler/ui/rg_proxy.rb +19 -0
  244. data/lib/bundler/ui/shell.rb +191 -0
  245. data/lib/bundler/ui/silent.rb +96 -0
  246. data/lib/bundler/ui.rb +9 -0
  247. data/lib/bundler/uri_credentials_filter.rb +43 -0
  248. data/lib/bundler/uri_normalizer.rb +23 -0
  249. data/lib/bundler/vendor/.document +1 -0
  250. data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
  251. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +227 -0
  252. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +3 -0
  253. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +56 -0
  254. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +230 -0
  255. data/lib/bundler/vendor/fileutils/COPYING +56 -0
  256. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +2701 -0
  257. data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
  258. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +41 -0
  259. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +65 -0
  260. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +80 -0
  261. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1153 -0
  262. data/lib/bundler/vendor/pub_grub/LICENSE.txt +21 -0
  263. data/lib/bundler/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  264. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +169 -0
  265. data/lib/bundler/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  266. data/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +150 -0
  267. data/lib/bundler/vendor/pub_grub/lib/pub_grub/package.rb +43 -0
  268. data/lib/bundler/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  269. data/lib/bundler/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  270. data/lib/bundler/vendor/pub_grub/lib/pub_grub/solve_failure.rb +19 -0
  271. data/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +61 -0
  272. data/lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb +42 -0
  273. data/lib/bundler/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  274. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  275. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +129 -0
  276. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +423 -0
  277. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +236 -0
  278. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_union.rb +178 -0
  279. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +31 -0
  280. data/lib/bundler/vendor/securerandom/COPYING +56 -0
  281. data/lib/bundler/vendor/securerandom/lib/securerandom.rb +102 -0
  282. data/lib/bundler/vendor/thor/LICENSE.md +20 -0
  283. data/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +105 -0
  284. data/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +61 -0
  285. data/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +108 -0
  286. data/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +143 -0
  287. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +407 -0
  288. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +130 -0
  289. data/lib/bundler/vendor/thor/lib/thor/actions.rb +340 -0
  290. data/lib/bundler/vendor/thor/lib/thor/base.rb +825 -0
  291. data/lib/bundler/vendor/thor/lib/thor/command.rb +151 -0
  292. data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +107 -0
  293. data/lib/bundler/vendor/thor/lib/thor/error.rb +106 -0
  294. data/lib/bundler/vendor/thor/lib/thor/group.rb +292 -0
  295. data/lib/bundler/vendor/thor/lib/thor/invocation.rb +178 -0
  296. data/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb +37 -0
  297. data/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  298. data/lib/bundler/vendor/thor/lib/thor/line_editor.rb +17 -0
  299. data/lib/bundler/vendor/thor/lib/thor/nested_context.rb +29 -0
  300. data/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +86 -0
  301. data/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +195 -0
  302. data/lib/bundler/vendor/thor/lib/thor/parser/option.rb +178 -0
  303. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +294 -0
  304. data/lib/bundler/vendor/thor/lib/thor/parser.rb +4 -0
  305. data/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +72 -0
  306. data/lib/bundler/vendor/thor/lib/thor/runner.rb +335 -0
  307. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +384 -0
  308. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +112 -0
  309. data/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb +29 -0
  310. data/lib/bundler/vendor/thor/lib/thor/shell/html.rb +81 -0
  311. data/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb +118 -0
  312. data/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb +42 -0
  313. data/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb +38 -0
  314. data/lib/bundler/vendor/thor/lib/thor/shell.rb +81 -0
  315. data/lib/bundler/vendor/thor/lib/thor/util.rb +285 -0
  316. data/lib/bundler/vendor/thor/lib/thor/version.rb +3 -0
  317. data/lib/bundler/vendor/thor/lib/thor.rb +674 -0
  318. data/lib/bundler/vendor/tsort/LICENSE.txt +22 -0
  319. data/lib/bundler/vendor/tsort/lib/tsort.rb +455 -0
  320. data/lib/bundler/vendor/uri/COPYING +56 -0
  321. data/lib/bundler/vendor/uri/lib/uri/common.rb +922 -0
  322. data/lib/bundler/vendor/uri/lib/uri/file.rb +100 -0
  323. data/lib/bundler/vendor/uri/lib/uri/ftp.rb +267 -0
  324. data/lib/bundler/vendor/uri/lib/uri/generic.rb +1592 -0
  325. data/lib/bundler/vendor/uri/lib/uri/http.rb +137 -0
  326. data/lib/bundler/vendor/uri/lib/uri/https.rb +23 -0
  327. data/lib/bundler/vendor/uri/lib/uri/ldap.rb +261 -0
  328. data/lib/bundler/vendor/uri/lib/uri/ldaps.rb +22 -0
  329. data/lib/bundler/vendor/uri/lib/uri/mailto.rb +293 -0
  330. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +547 -0
  331. data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +206 -0
  332. data/lib/bundler/vendor/uri/lib/uri/version.rb +6 -0
  333. data/lib/bundler/vendor/uri/lib/uri/ws.rb +83 -0
  334. data/lib/bundler/vendor/uri/lib/uri/wss.rb +23 -0
  335. data/lib/bundler/vendor/uri/lib/uri.rb +104 -0
  336. data/lib/bundler/vendored_fileutils.rb +4 -0
  337. data/lib/bundler/vendored_net_http.rb +23 -0
  338. data/lib/bundler/vendored_persistent.rb +11 -0
  339. data/lib/bundler/vendored_pub_grub.rb +4 -0
  340. data/lib/bundler/vendored_securerandom.rb +12 -0
  341. data/lib/bundler/vendored_thor.rb +8 -0
  342. data/lib/bundler/vendored_timeout.rb +12 -0
  343. data/lib/bundler/vendored_tsort.rb +4 -0
  344. data/lib/bundler/vendored_uri.rb +21 -0
  345. data/lib/bundler/version.rb +21 -0
  346. data/lib/bundler/vlad.rb +4 -0
  347. data/lib/bundler/worker.rb +125 -0
  348. data/lib/bundler/yaml_serializer.rb +98 -0
  349. data/lib/bundler.rb +691 -0
  350. metadata +409 -0
@@ -0,0 +1,2701 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'rbconfig'
5
+ rescue LoadError
6
+ # for make rjit-headers
7
+ end
8
+
9
+ # Namespace for file utility methods for copying, moving, removing, etc.
10
+ #
11
+ # == What's Here
12
+ #
13
+ # First, what’s elsewhere. \Module \Bundler::FileUtils:
14
+ #
15
+ # - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
16
+ # - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
17
+ # (but is not included or extended there).
18
+ #
19
+ # Here, module \Bundler::FileUtils provides methods that are useful for:
20
+ #
21
+ # - {Creating}[rdoc-ref:FileUtils@Creating].
22
+ # - {Deleting}[rdoc-ref:FileUtils@Deleting].
23
+ # - {Querying}[rdoc-ref:FileUtils@Querying].
24
+ # - {Setting}[rdoc-ref:FileUtils@Setting].
25
+ # - {Comparing}[rdoc-ref:FileUtils@Comparing].
26
+ # - {Copying}[rdoc-ref:FileUtils@Copying].
27
+ # - {Moving}[rdoc-ref:FileUtils@Moving].
28
+ # - {Options}[rdoc-ref:FileUtils@Options].
29
+ #
30
+ # === Creating
31
+ #
32
+ # - ::mkdir: Creates directories.
33
+ # - ::mkdir_p, ::makedirs, ::mkpath: Creates directories,
34
+ # also creating ancestor directories as needed.
35
+ # - ::link_entry: Creates a hard link.
36
+ # - ::ln, ::link: Creates hard links.
37
+ # - ::ln_s, ::symlink: Creates symbolic links.
38
+ # - ::ln_sf: Creates symbolic links, overwriting if necessary.
39
+ # - ::ln_sr: Creates symbolic links relative to targets
40
+ #
41
+ # === Deleting
42
+ #
43
+ # - ::remove_dir: Removes a directory and its descendants.
44
+ # - ::remove_entry: Removes an entry, including its descendants if it is a directory.
45
+ # - ::remove_entry_secure: Like ::remove_entry, but removes securely.
46
+ # - ::remove_file: Removes a file entry.
47
+ # - ::rm, ::remove: Removes entries.
48
+ # - ::rm_f, ::safe_unlink: Like ::rm, but removes forcibly.
49
+ # - ::rm_r: Removes entries and their descendants.
50
+ # - ::rm_rf, ::rmtree: Like ::rm_r, but removes forcibly.
51
+ # - ::rmdir: Removes directories.
52
+ #
53
+ # === Querying
54
+ #
55
+ # - ::pwd, ::getwd: Returns the path to the working directory.
56
+ # - ::uptodate?: Returns whether a given entry is newer than given other entries.
57
+ #
58
+ # === Setting
59
+ #
60
+ # - ::cd, ::chdir: Sets the working directory.
61
+ # - ::chmod: Sets permissions for an entry.
62
+ # - ::chmod_R: Sets permissions for an entry and its descendants.
63
+ # - ::chown: Sets the owner and group for entries.
64
+ # - ::chown_R: Sets the owner and group for entries and their descendants.
65
+ # - ::touch: Sets modification and access times for entries,
66
+ # creating if necessary.
67
+ #
68
+ # === Comparing
69
+ #
70
+ # - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
71
+ # - ::compare_stream: Returns whether two streams are identical.
72
+ #
73
+ # === Copying
74
+ #
75
+ # - ::copy_entry: Recursively copies an entry.
76
+ # - ::copy_file: Copies an entry.
77
+ # - ::copy_stream: Copies a stream.
78
+ # - ::cp, ::copy: Copies files.
79
+ # - ::cp_lr: Recursively creates hard links.
80
+ # - ::cp_r: Recursively copies files, retaining mode, owner, and group.
81
+ # - ::install: Recursively copies files, optionally setting mode,
82
+ # owner, and group.
83
+ #
84
+ # === Moving
85
+ #
86
+ # - ::mv, ::move: Moves entries.
87
+ #
88
+ # === Options
89
+ #
90
+ # - ::collect_method: Returns the names of methods that accept a given option.
91
+ # - ::commands: Returns the names of methods that accept options.
92
+ # - ::have_option?: Returns whether a given method accepts a given option.
93
+ # - ::options: Returns all option names.
94
+ # - ::options_of: Returns the names of the options for a given method.
95
+ #
96
+ # == Path Arguments
97
+ #
98
+ # Some methods in \Bundler::FileUtils accept _path_ arguments,
99
+ # which are interpreted as paths to filesystem entries:
100
+ #
101
+ # - If the argument is a string, that value is the path.
102
+ # - If the argument has method +:to_path+, it is converted via that method.
103
+ # - If the argument has method +:to_str+, it is converted via that method.
104
+ #
105
+ # == About the Examples
106
+ #
107
+ # Some examples here involve trees of file entries.
108
+ # For these, we sometimes display trees using the
109
+ # {tree command-line utility}[https://en.wikipedia.org/wiki/Tree_(command)],
110
+ # which is a recursive directory-listing utility that produces
111
+ # a depth-indented listing of files and directories.
112
+ #
113
+ # We use a helper method to launch the command and control the format:
114
+ #
115
+ # def tree(dirpath = '.')
116
+ # command = "tree --noreport --charset=ascii #{dirpath}"
117
+ # system(command)
118
+ # end
119
+ #
120
+ # To illustrate:
121
+ #
122
+ # tree('src0')
123
+ # # => src0
124
+ # # |-- sub0
125
+ # # | |-- src0.txt
126
+ # # | `-- src1.txt
127
+ # # `-- sub1
128
+ # # |-- src2.txt
129
+ # # `-- src3.txt
130
+ #
131
+ # == Avoiding the TOCTTOU Vulnerability
132
+ #
133
+ # For certain methods that recursively remove entries,
134
+ # there is a potential vulnerability called the
135
+ # {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use],
136
+ # or TOCTTOU, vulnerability that can exist when:
137
+ #
138
+ # - An ancestor directory of the entry at the target path is world writable;
139
+ # such directories include <tt>/tmp</tt>.
140
+ # - The directory tree at the target path includes:
141
+ #
142
+ # - A world-writable descendant directory.
143
+ # - A symbolic link.
144
+ #
145
+ # To avoid that vulnerability, you can use this method to remove entries:
146
+ #
147
+ # - Bundler::FileUtils.remove_entry_secure: removes recursively
148
+ # if the target path points to a directory.
149
+ #
150
+ # Also available are these methods,
151
+ # each of which calls \Bundler::FileUtils.remove_entry_secure:
152
+ #
153
+ # - Bundler::FileUtils.rm_r with keyword argument <tt>secure: true</tt>.
154
+ # - Bundler::FileUtils.rm_rf with keyword argument <tt>secure: true</tt>.
155
+ #
156
+ # Finally, this method for moving entries calls \Bundler::FileUtils.remove_entry_secure
157
+ # if the source and destination are on different file systems
158
+ # (which means that the "move" is really a copy and remove):
159
+ #
160
+ # - Bundler::FileUtils.mv with keyword argument <tt>secure: true</tt>.
161
+ #
162
+ # \Method \Bundler::FileUtils.remove_entry_secure removes securely
163
+ # by applying a special pre-process:
164
+ #
165
+ # - If the target path points to a directory, this method uses methods
166
+ # {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
167
+ # and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
168
+ # in removing directories.
169
+ # - The owner of the target directory should be either the current process
170
+ # or the super user (root).
171
+ #
172
+ # WARNING: You must ensure that *ALL* parent directories cannot be
173
+ # moved by other untrusted users. For example, parent directories
174
+ # should not be owned by untrusted users, and should not be world
175
+ # writable except when the sticky bit is set.
176
+ #
177
+ # For details of this security vulnerability, see Perl cases:
178
+ #
179
+ # - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448].
180
+ # - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
181
+ #
182
+ module Bundler::FileUtils
183
+ # The version number.
184
+ VERSION = "1.8.0"
185
+
186
+ def self.private_module_function(name) #:nodoc:
187
+ module_function name
188
+ private_class_method name
189
+ end
190
+
191
+ #
192
+ # Returns a string containing the path to the current directory:
193
+ #
194
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
195
+ #
196
+ # Related: Bundler::FileUtils.cd.
197
+ #
198
+ def pwd
199
+ Dir.pwd
200
+ end
201
+ module_function :pwd
202
+
203
+ alias getwd pwd
204
+ module_function :getwd
205
+
206
+ # Changes the working directory to the given +dir+, which
207
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
208
+ #
209
+ # With no block given,
210
+ # changes the current directory to the directory at +dir+; returns zero:
211
+ #
212
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
213
+ # Bundler::FileUtils.cd('..')
214
+ # Bundler::FileUtils.pwd # => "/rdoc"
215
+ # Bundler::FileUtils.cd('fileutils')
216
+ #
217
+ # With a block given, changes the current directory to the directory
218
+ # at +dir+, calls the block with argument +dir+,
219
+ # and restores the original current directory; returns the block's value:
220
+ #
221
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
222
+ # Bundler::FileUtils.cd('..') { |arg| [arg, Bundler::FileUtils.pwd] } # => ["..", "/rdoc"]
223
+ # Bundler::FileUtils.pwd # => "/rdoc/fileutils"
224
+ #
225
+ # Keyword arguments:
226
+ #
227
+ # - <tt>verbose: true</tt> - prints an equivalent command:
228
+ #
229
+ # Bundler::FileUtils.cd('..')
230
+ # Bundler::FileUtils.cd('fileutils')
231
+ #
232
+ # Output:
233
+ #
234
+ # cd ..
235
+ # cd fileutils
236
+ #
237
+ # Related: Bundler::FileUtils.pwd.
238
+ #
239
+ def cd(dir, verbose: nil, &block) # :yield: dir
240
+ fu_output_message "cd #{dir}" if verbose
241
+ result = Dir.chdir(dir, &block)
242
+ fu_output_message 'cd -' if verbose and block
243
+ result
244
+ end
245
+ module_function :cd
246
+
247
+ alias chdir cd
248
+ module_function :chdir
249
+
250
+ #
251
+ # Returns +true+ if the file at path +new+
252
+ # is newer than all the files at paths in array +old_list+;
253
+ # +false+ otherwise.
254
+ #
255
+ # Argument +new+ and the elements of +old_list+
256
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
257
+ #
258
+ # Bundler::FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
259
+ # Bundler::FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
260
+ #
261
+ # A non-existent file is considered to be infinitely old.
262
+ #
263
+ # Related: Bundler::FileUtils.touch.
264
+ #
265
+ def uptodate?(new, old_list)
266
+ return false unless File.exist?(new)
267
+ new_time = File.mtime(new)
268
+ old_list.each do |old|
269
+ if File.exist?(old)
270
+ return false unless new_time > File.mtime(old)
271
+ end
272
+ end
273
+ true
274
+ end
275
+ module_function :uptodate?
276
+
277
+ def remove_trailing_slash(dir) #:nodoc:
278
+ dir == '/' ? dir : dir.chomp(?/)
279
+ end
280
+ private_module_function :remove_trailing_slash
281
+
282
+ #
283
+ # Creates directories at the paths in the given +list+
284
+ # (a single path or an array of paths);
285
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
286
+ #
287
+ # Argument +list+ or its elements
288
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
289
+ #
290
+ # With no keyword arguments, creates a directory at each +path+ in +list+
291
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
292
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
293
+ #
294
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
295
+ # Bundler::FileUtils.mkdir('tmp4') # => ["tmp4"]
296
+ #
297
+ # Keyword arguments:
298
+ #
299
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
300
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
301
+ # - <tt>noop: true</tt> - does not create directories.
302
+ # - <tt>verbose: true</tt> - prints an equivalent command:
303
+ #
304
+ # Bundler::FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
305
+ # Bundler::FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
306
+ #
307
+ # Output:
308
+ #
309
+ # mkdir tmp0 tmp1
310
+ # mkdir -m 700 tmp2 tmp3
311
+ #
312
+ # Raises an exception if any path points to an existing
313
+ # file or directory, or if for any reason a directory cannot be created.
314
+ #
315
+ # Related: Bundler::FileUtils.mkdir_p.
316
+ #
317
+ def mkdir(list, mode: nil, noop: nil, verbose: nil)
318
+ list = fu_list(list)
319
+ fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
320
+ return if noop
321
+
322
+ list.each do |dir|
323
+ fu_mkdir dir, mode
324
+ end
325
+ end
326
+ module_function :mkdir
327
+
328
+ #
329
+ # Creates directories at the paths in the given +list+
330
+ # (a single path or an array of paths),
331
+ # also creating ancestor directories as needed;
332
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
333
+ #
334
+ # Argument +list+ or its elements
335
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
336
+ #
337
+ # With no keyword arguments, creates a directory at each +path+ in +list+,
338
+ # along with any needed ancestor directories,
339
+ # by calling: <tt>Dir.mkdir(path, mode)</tt>;
340
+ # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
341
+ #
342
+ # Bundler::FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
343
+ # Bundler::FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
344
+ #
345
+ # Keyword arguments:
346
+ #
347
+ # - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
348
+ # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
349
+ # - <tt>noop: true</tt> - does not create directories.
350
+ # - <tt>verbose: true</tt> - prints an equivalent command:
351
+ #
352
+ # Bundler::FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
353
+ # Bundler::FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
354
+ #
355
+ # Output:
356
+ #
357
+ # mkdir -p tmp0 tmp1
358
+ # mkdir -p -m 700 tmp2 tmp3
359
+ #
360
+ # Raises an exception if for any reason a directory cannot be created.
361
+ #
362
+ # Bundler::FileUtils.mkpath and Bundler::FileUtils.makedirs are aliases for Bundler::FileUtils.mkdir_p.
363
+ #
364
+ # Related: Bundler::FileUtils.mkdir.
365
+ #
366
+ def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
367
+ list = fu_list(list)
368
+ fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
369
+ return *list if noop
370
+
371
+ list.each do |item|
372
+ path = remove_trailing_slash(item)
373
+
374
+ stack = []
375
+ until File.directory?(path) || File.dirname(path) == path
376
+ stack.push path
377
+ path = File.dirname(path)
378
+ end
379
+ stack.reverse_each do |dir|
380
+ begin
381
+ fu_mkdir dir, mode
382
+ rescue SystemCallError
383
+ raise unless File.directory?(dir)
384
+ end
385
+ end
386
+ end
387
+
388
+ return *list
389
+ end
390
+ module_function :mkdir_p
391
+
392
+ alias mkpath mkdir_p
393
+ alias makedirs mkdir_p
394
+ module_function :mkpath
395
+ module_function :makedirs
396
+
397
+ def fu_mkdir(path, mode) #:nodoc:
398
+ path = remove_trailing_slash(path)
399
+ if mode
400
+ Dir.mkdir path, mode
401
+ File.chmod mode, path
402
+ else
403
+ Dir.mkdir path
404
+ end
405
+ end
406
+ private_module_function :fu_mkdir
407
+
408
+ #
409
+ # Removes directories at the paths in the given +list+
410
+ # (a single path or an array of paths);
411
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
412
+ #
413
+ # Argument +list+ or its elements
414
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
415
+ #
416
+ # With no keyword arguments, removes the directory at each +path+ in +list+,
417
+ # by calling: <tt>Dir.rmdir(path)</tt>;
418
+ # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
419
+ #
420
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
421
+ # Bundler::FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
422
+ #
423
+ # Keyword arguments:
424
+ #
425
+ # - <tt>parents: true</tt> - removes successive ancestor directories
426
+ # if empty.
427
+ # - <tt>noop: true</tt> - does not remove directories.
428
+ # - <tt>verbose: true</tt> - prints an equivalent command:
429
+ #
430
+ # Bundler::FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
431
+ # Bundler::FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
432
+ #
433
+ # Output:
434
+ #
435
+ # rmdir -p tmp0/tmp1 tmp2/tmp3
436
+ # rmdir -p tmp4/tmp5
437
+ #
438
+ # Raises an exception if a directory does not exist
439
+ # or if for any reason a directory cannot be removed.
440
+ #
441
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
442
+ #
443
+ def rmdir(list, parents: nil, noop: nil, verbose: nil)
444
+ list = fu_list(list)
445
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
446
+ return if noop
447
+ list.each do |dir|
448
+ Dir.rmdir(dir = remove_trailing_slash(dir))
449
+ if parents
450
+ begin
451
+ until (parent = File.dirname(dir)) == '.' or parent == dir
452
+ dir = parent
453
+ Dir.rmdir(dir)
454
+ end
455
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
456
+ end
457
+ end
458
+ end
459
+ end
460
+ module_function :rmdir
461
+
462
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
463
+ #
464
+ # Arguments +src+ (a single path or an array of paths)
465
+ # and +dest+ (a single path)
466
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
467
+ #
468
+ # When +src+ is the path to an existing file
469
+ # and +dest+ is the path to a non-existent file,
470
+ # creates a hard link at +dest+ pointing to +src+; returns zero:
471
+ #
472
+ # Dir.children('tmp0/') # => ["t.txt"]
473
+ # Dir.children('tmp1/') # => []
474
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
475
+ # Dir.children('tmp1/') # => ["t.lnk"]
476
+ #
477
+ # When +src+ is the path to an existing file
478
+ # and +dest+ is the path to an existing directory,
479
+ # creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
480
+ #
481
+ # Dir.children('tmp2') # => ["t.dat"]
482
+ # Dir.children('tmp3') # => []
483
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
484
+ # Dir.children('tmp3') # => ["t.dat"]
485
+ #
486
+ # When +src+ is an array of paths to existing files
487
+ # and +dest+ is the path to an existing directory,
488
+ # then for each path +target+ in +src+,
489
+ # creates a hard link at <tt>dest/target</tt> pointing to +target+;
490
+ # returns +src+:
491
+ #
492
+ # Dir.children('tmp4/') # => []
493
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
494
+ # Dir.children('tmp4/') # => ["t.dat", "t.txt"]
495
+ #
496
+ # Keyword arguments:
497
+ #
498
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
499
+ # - <tt>noop: true</tt> - does not create links.
500
+ # - <tt>verbose: true</tt> - prints an equivalent command:
501
+ #
502
+ # Bundler::FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
503
+ # Bundler::FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
504
+ # Bundler::FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
505
+ #
506
+ # Output:
507
+ #
508
+ # ln tmp0/t.txt tmp1/t.lnk
509
+ # ln tmp2/t.dat tmp3
510
+ # ln tmp0/t.txt tmp2/t.dat tmp4/
511
+ #
512
+ # Raises an exception if +dest+ is the path to an existing file
513
+ # and keyword argument +force+ is not +true+.
514
+ #
515
+ # Related: Bundler::FileUtils.link_entry (has different options).
516
+ #
517
+ def ln(src, dest, force: nil, noop: nil, verbose: nil)
518
+ fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
519
+ return if noop
520
+ fu_each_src_dest0(src, dest) do |s,d|
521
+ remove_file d, true if force
522
+ File.link s, d
523
+ end
524
+ end
525
+ module_function :ln
526
+
527
+ alias link ln
528
+ module_function :link
529
+
530
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
531
+ #
532
+ # Arguments +src+ (a single path or an array of paths)
533
+ # and +dest+ (a single path)
534
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
535
+ #
536
+ # If +src+ is the path to a directory and +dest+ does not exist,
537
+ # creates links +dest+ and descendents pointing to +src+ and its descendents:
538
+ #
539
+ # tree('src0')
540
+ # # => src0
541
+ # # |-- sub0
542
+ # # | |-- src0.txt
543
+ # # | `-- src1.txt
544
+ # # `-- sub1
545
+ # # |-- src2.txt
546
+ # # `-- src3.txt
547
+ # File.exist?('dest0') # => false
548
+ # Bundler::FileUtils.cp_lr('src0', 'dest0')
549
+ # tree('dest0')
550
+ # # => dest0
551
+ # # |-- sub0
552
+ # # | |-- src0.txt
553
+ # # | `-- src1.txt
554
+ # # `-- sub1
555
+ # # |-- src2.txt
556
+ # # `-- src3.txt
557
+ #
558
+ # If +src+ and +dest+ are both paths to directories,
559
+ # creates links <tt>dest/src</tt> and descendents
560
+ # pointing to +src+ and its descendents:
561
+ #
562
+ # tree('src1')
563
+ # # => src1
564
+ # # |-- sub0
565
+ # # | |-- src0.txt
566
+ # # | `-- src1.txt
567
+ # # `-- sub1
568
+ # # |-- src2.txt
569
+ # # `-- src3.txt
570
+ # Bundler::FileUtils.mkdir('dest1')
571
+ # Bundler::FileUtils.cp_lr('src1', 'dest1')
572
+ # tree('dest1')
573
+ # # => dest1
574
+ # # `-- src1
575
+ # # |-- sub0
576
+ # # | |-- src0.txt
577
+ # # | `-- src1.txt
578
+ # # `-- sub1
579
+ # # |-- src2.txt
580
+ # # `-- src3.txt
581
+ #
582
+ # If +src+ is an array of paths to entries and +dest+ is the path to a directory,
583
+ # for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
584
+ # pointing to that path:
585
+ #
586
+ # tree('src2')
587
+ # # => src2
588
+ # # |-- sub0
589
+ # # | |-- src0.txt
590
+ # # | `-- src1.txt
591
+ # # `-- sub1
592
+ # # |-- src2.txt
593
+ # # `-- src3.txt
594
+ # Bundler::FileUtils.mkdir('dest2')
595
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
596
+ # tree('dest2')
597
+ # # => dest2
598
+ # # |-- sub0
599
+ # # | |-- src0.txt
600
+ # # | `-- src1.txt
601
+ # # `-- sub1
602
+ # # |-- src2.txt
603
+ # # `-- src3.txt
604
+ #
605
+ # Keyword arguments:
606
+ #
607
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
608
+ # does not dereference it.
609
+ # - <tt>noop: true</tt> - does not create links.
610
+ # - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
611
+ # - <tt>verbose: true</tt> - prints an equivalent command:
612
+ #
613
+ # Bundler::FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
614
+ # Bundler::FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
615
+ # Bundler::FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
616
+ #
617
+ # Output:
618
+ #
619
+ # cp -lr src0 dest0
620
+ # cp -lr src1 dest1
621
+ # cp -lr src2/sub0 src2/sub1 dest2
622
+ #
623
+ # Raises an exception if +dest+ is the path to an existing file or directory
624
+ # and keyword argument <tt>remove_destination: true</tt> is not given.
625
+ #
626
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
627
+ #
628
+ def cp_lr(src, dest, noop: nil, verbose: nil,
629
+ dereference_root: true, remove_destination: false)
630
+ fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
631
+ return if noop
632
+ fu_each_src_dest(src, dest) do |s, d|
633
+ link_entry s, d, dereference_root, remove_destination
634
+ end
635
+ end
636
+ module_function :cp_lr
637
+
638
+ # Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
639
+ #
640
+ # Arguments +src+ (a single path or an array of paths)
641
+ # and +dest+ (a single path)
642
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
643
+ #
644
+ # If +src+ is the path to an existing file:
645
+ #
646
+ # - When +dest+ is the path to a non-existent file,
647
+ # creates a symbolic link at +dest+ pointing to +src+:
648
+ #
649
+ # Bundler::FileUtils.touch('src0.txt')
650
+ # File.exist?('dest0.txt') # => false
651
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt')
652
+ # File.symlink?('dest0.txt') # => true
653
+ #
654
+ # - When +dest+ is the path to an existing file,
655
+ # creates a symbolic link at +dest+ pointing to +src+
656
+ # if and only if keyword argument <tt>force: true</tt> is given
657
+ # (raises an exception otherwise):
658
+ #
659
+ # Bundler::FileUtils.touch('src1.txt')
660
+ # Bundler::FileUtils.touch('dest1.txt')
661
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
662
+ # FileTest.symlink?('dest1.txt') # => true
663
+ #
664
+ # Bundler::FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
665
+ #
666
+ # If +dest+ is the path to a directory,
667
+ # creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
668
+ #
669
+ # Bundler::FileUtils.touch('src2.txt')
670
+ # Bundler::FileUtils.mkdir('destdir2')
671
+ # Bundler::FileUtils.ln_s('src2.txt', 'destdir2')
672
+ # File.symlink?('destdir2/src2.txt') # => true
673
+ #
674
+ # If +src+ is an array of paths to existing files and +dest+ is a directory,
675
+ # for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
676
+ # pointing to +child+:
677
+ #
678
+ # Bundler::FileUtils.mkdir('srcdir3')
679
+ # Bundler::FileUtils.touch('srcdir3/src0.txt')
680
+ # Bundler::FileUtils.touch('srcdir3/src1.txt')
681
+ # Bundler::FileUtils.mkdir('destdir3')
682
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
683
+ # File.symlink?('destdir3/src0.txt') # => true
684
+ # File.symlink?('destdir3/src1.txt') # => true
685
+ #
686
+ # Keyword arguments:
687
+ #
688
+ # - <tt>force: true</tt> - overwrites +dest+ if it exists.
689
+ # - <tt>relative: false</tt> - create links relative to +dest+.
690
+ # - <tt>noop: true</tt> - does not create links.
691
+ # - <tt>verbose: true</tt> - prints an equivalent command:
692
+ #
693
+ # Bundler::FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
694
+ # Bundler::FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
695
+ # Bundler::FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
696
+ # Bundler::FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
697
+ #
698
+ # Output:
699
+ #
700
+ # ln -s src0.txt dest0.txt
701
+ # ln -s src1.txt destdir1
702
+ # ln -sf src2.txt dest2.txt
703
+ # ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
704
+ #
705
+ # Related: Bundler::FileUtils.ln_sf.
706
+ #
707
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
708
+ if relative
709
+ return ln_sr(src, dest, force: force, target_directory: target_directory, noop: noop, verbose: verbose)
710
+ end
711
+ fu_output_message "ln -s#{force ? 'f' : ''}#{
712
+ target_directory ? '' : 'T'} #{[src,dest].flatten.join ' '}" if verbose
713
+ return if noop
714
+ fu_each_src_dest0(src, dest, target_directory) do |s,d|
715
+ remove_file d, true if force
716
+ File.symlink s, d
717
+ end
718
+ end
719
+ module_function :ln_s
720
+
721
+ alias symlink ln_s
722
+ module_function :symlink
723
+
724
+ # Like Bundler::FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
725
+ #
726
+ def ln_sf(src, dest, noop: nil, verbose: nil)
727
+ ln_s src, dest, force: true, noop: noop, verbose: verbose
728
+ end
729
+ module_function :ln_sf
730
+
731
+ # Like Bundler::FileUtils.ln_s, but create links relative to +dest+.
732
+ #
733
+ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
734
+ cmd = "ln -s#{force ? 'f' : ''}#{target_directory ? '' : 'T'}" if verbose
735
+ fu_each_src_dest0(src, dest, target_directory) do |s,d|
736
+ if target_directory
737
+ parent = File.dirname(d)
738
+ destdirs = fu_split_path(parent)
739
+ real_ddirs = fu_split_path(File.realpath(parent))
740
+ else
741
+ destdirs ||= fu_split_path(dest)
742
+ real_ddirs ||= fu_split_path(File.realdirpath(dest))
743
+ end
744
+ srcdirs = fu_split_path(s)
745
+ i = fu_common_components(srcdirs, destdirs)
746
+ n = destdirs.size - i
747
+ n -= 1 unless target_directory
748
+ link1 = fu_clean_components(*Array.new([n, 0].max, '..'), *srcdirs[i..-1])
749
+ begin
750
+ real_sdirs = fu_split_path(File.realdirpath(s)) rescue nil
751
+ rescue
752
+ else
753
+ i = fu_common_components(real_sdirs, real_ddirs)
754
+ n = real_ddirs.size - i
755
+ n -= 1 unless target_directory
756
+ link2 = fu_clean_components(*Array.new([n, 0].max, '..'), *real_sdirs[i..-1])
757
+ link1 = link2 if link1.size > link2.size
758
+ end
759
+ s = File.join(link1)
760
+ fu_output_message [cmd, s, d].flatten.join(' ') if verbose
761
+ next if noop
762
+ remove_file d, true if force
763
+ File.symlink s, d
764
+ end
765
+ end
766
+ module_function :ln_sr
767
+
768
+ # Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
769
+ #
770
+ # Arguments +src+ and +dest+
771
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
772
+ #
773
+ # If +src+ is the path to a file and +dest+ does not exist,
774
+ # creates a hard link at +dest+ pointing to +src+:
775
+ #
776
+ # Bundler::FileUtils.touch('src0.txt')
777
+ # File.exist?('dest0.txt') # => false
778
+ # Bundler::FileUtils.link_entry('src0.txt', 'dest0.txt')
779
+ # File.file?('dest0.txt') # => true
780
+ #
781
+ # If +src+ is the path to a directory and +dest+ does not exist,
782
+ # recursively creates hard links at +dest+ pointing to paths in +src+:
783
+ #
784
+ # Bundler::FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
785
+ # src_file_paths = [
786
+ # 'src1/dir0/t0.txt',
787
+ # 'src1/dir0/t1.txt',
788
+ # 'src1/dir1/t2.txt',
789
+ # 'src1/dir1/t3.txt',
790
+ # ]
791
+ # Bundler::FileUtils.touch(src_file_paths)
792
+ # File.directory?('dest1') # => true
793
+ # Bundler::FileUtils.link_entry('src1', 'dest1')
794
+ # File.file?('dest1/dir0/t0.txt') # => true
795
+ # File.file?('dest1/dir0/t1.txt') # => true
796
+ # File.file?('dest1/dir1/t2.txt') # => true
797
+ # File.file?('dest1/dir1/t3.txt') # => true
798
+ #
799
+ # Optional arguments:
800
+ #
801
+ # - +dereference_root+ - dereferences +src+ if it is a symbolic link (+false+ by default).
802
+ # - +remove_destination+ - removes +dest+ before creating links (+false+ by default).
803
+ #
804
+ # Raises an exception if +dest+ is the path to an existing file or directory
805
+ # and optional argument +remove_destination+ is not given.
806
+ #
807
+ # Related: Bundler::FileUtils.ln (has different options).
808
+ #
809
+ def link_entry(src, dest, dereference_root = false, remove_destination = false)
810
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
811
+ destent = Entry_.new(dest, ent.rel, false)
812
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
813
+ ent.link destent.path
814
+ end
815
+ end
816
+ module_function :link_entry
817
+
818
+ # Copies files.
819
+ #
820
+ # Arguments +src+ (a single path or an array of paths)
821
+ # and +dest+ (a single path)
822
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
823
+ #
824
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
825
+ # copies +src+ to +dest+:
826
+ #
827
+ # Bundler::FileUtils.touch('src0.txt')
828
+ # File.exist?('dest0.txt') # => false
829
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt')
830
+ # File.file?('dest0.txt') # => true
831
+ #
832
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
833
+ # copies +src+ to <tt>dest/src</tt>:
834
+ #
835
+ # Bundler::FileUtils.touch('src1.txt')
836
+ # Bundler::FileUtils.mkdir('dest1')
837
+ # Bundler::FileUtils.cp('src1.txt', 'dest1')
838
+ # File.file?('dest1/src1.txt') # => true
839
+ #
840
+ # If +src+ is an array of paths to files and +dest+ is the path to a directory,
841
+ # copies from each +src+ to +dest+:
842
+ #
843
+ # src_file_paths = ['src2.txt', 'src2.dat']
844
+ # Bundler::FileUtils.touch(src_file_paths)
845
+ # Bundler::FileUtils.mkdir('dest2')
846
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2')
847
+ # File.file?('dest2/src2.txt') # => true
848
+ # File.file?('dest2/src2.dat') # => true
849
+ #
850
+ # Keyword arguments:
851
+ #
852
+ # - <tt>preserve: true</tt> - preserves file times.
853
+ # - <tt>noop: true</tt> - does not copy files.
854
+ # - <tt>verbose: true</tt> - prints an equivalent command:
855
+ #
856
+ # Bundler::FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
857
+ # Bundler::FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
858
+ # Bundler::FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
859
+ #
860
+ # Output:
861
+ #
862
+ # cp src0.txt dest0.txt
863
+ # cp src1.txt dest1
864
+ # cp src2.txt src2.dat dest2
865
+ #
866
+ # Raises an exception if +src+ is a directory.
867
+ #
868
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
869
+ #
870
+ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
871
+ fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
872
+ return if noop
873
+ fu_each_src_dest(src, dest) do |s, d|
874
+ copy_file s, d, preserve
875
+ end
876
+ end
877
+ module_function :cp
878
+
879
+ alias copy cp
880
+ module_function :copy
881
+
882
+ # Recursively copies files.
883
+ #
884
+ # Arguments +src+ (a single path or an array of paths)
885
+ # and +dest+ (a single path)
886
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
887
+ #
888
+ # The mode, owner, and group are retained in the copy;
889
+ # to change those, use Bundler::FileUtils.install instead.
890
+ #
891
+ # If +src+ is the path to a file and +dest+ is not the path to a directory,
892
+ # copies +src+ to +dest+:
893
+ #
894
+ # Bundler::FileUtils.touch('src0.txt')
895
+ # File.exist?('dest0.txt') # => false
896
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt')
897
+ # File.file?('dest0.txt') # => true
898
+ #
899
+ # If +src+ is the path to a file and +dest+ is the path to a directory,
900
+ # copies +src+ to <tt>dest/src</tt>:
901
+ #
902
+ # Bundler::FileUtils.touch('src1.txt')
903
+ # Bundler::FileUtils.mkdir('dest1')
904
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1')
905
+ # File.file?('dest1/src1.txt') # => true
906
+ #
907
+ # If +src+ is the path to a directory and +dest+ does not exist,
908
+ # recursively copies +src+ to +dest+:
909
+ #
910
+ # tree('src2')
911
+ # # => src2
912
+ # # |-- dir0
913
+ # # | |-- src0.txt
914
+ # # | `-- src1.txt
915
+ # # `-- dir1
916
+ # # |-- src2.txt
917
+ # # `-- src3.txt
918
+ # Bundler::FileUtils.exist?('dest2') # => false
919
+ # Bundler::FileUtils.cp_r('src2', 'dest2')
920
+ # tree('dest2')
921
+ # # => dest2
922
+ # # |-- dir0
923
+ # # | |-- src0.txt
924
+ # # | `-- src1.txt
925
+ # # `-- dir1
926
+ # # |-- src2.txt
927
+ # # `-- src3.txt
928
+ #
929
+ # If +src+ and +dest+ are paths to directories,
930
+ # recursively copies +src+ to <tt>dest/src</tt>:
931
+ #
932
+ # tree('src3')
933
+ # # => src3
934
+ # # |-- dir0
935
+ # # | |-- src0.txt
936
+ # # | `-- src1.txt
937
+ # # `-- dir1
938
+ # # |-- src2.txt
939
+ # # `-- src3.txt
940
+ # Bundler::FileUtils.mkdir('dest3')
941
+ # Bundler::FileUtils.cp_r('src3', 'dest3')
942
+ # tree('dest3')
943
+ # # => dest3
944
+ # # `-- src3
945
+ # # |-- dir0
946
+ # # | |-- src0.txt
947
+ # # | `-- src1.txt
948
+ # # `-- dir1
949
+ # # |-- src2.txt
950
+ # # `-- src3.txt
951
+ #
952
+ # If +src+ is an array of paths and +dest+ is a directory,
953
+ # recursively copies from each path in +src+ to +dest+;
954
+ # the paths in +src+ may point to files and/or directories.
955
+ #
956
+ # Keyword arguments:
957
+ #
958
+ # - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
959
+ # does not dereference it.
960
+ # - <tt>noop: true</tt> - does not copy files.
961
+ # - <tt>preserve: true</tt> - preserves file times.
962
+ # - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
963
+ # - <tt>verbose: true</tt> - prints an equivalent command:
964
+ #
965
+ # Bundler::FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
966
+ # Bundler::FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
967
+ # Bundler::FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
968
+ # Bundler::FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
969
+ #
970
+ # Output:
971
+ #
972
+ # cp -r src0.txt dest0.txt
973
+ # cp -r src1.txt dest1
974
+ # cp -r src2 dest2
975
+ # cp -r src3 dest3
976
+ #
977
+ # Raises an exception of +src+ is the path to a directory
978
+ # and +dest+ is the path to a file.
979
+ #
980
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
981
+ #
982
+ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
983
+ dereference_root: true, remove_destination: nil)
984
+ fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
985
+ return if noop
986
+ fu_each_src_dest(src, dest) do |s, d|
987
+ copy_entry s, d, preserve, dereference_root, remove_destination
988
+ end
989
+ end
990
+ module_function :cp_r
991
+
992
+ # Recursively copies files from +src+ to +dest+.
993
+ #
994
+ # Arguments +src+ and +dest+
995
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
996
+ #
997
+ # If +src+ is the path to a file, copies +src+ to +dest+:
998
+ #
999
+ # Bundler::FileUtils.touch('src0.txt')
1000
+ # File.exist?('dest0.txt') # => false
1001
+ # Bundler::FileUtils.copy_entry('src0.txt', 'dest0.txt')
1002
+ # File.file?('dest0.txt') # => true
1003
+ #
1004
+ # If +src+ is a directory, recursively copies +src+ to +dest+:
1005
+ #
1006
+ # tree('src1')
1007
+ # # => src1
1008
+ # # |-- dir0
1009
+ # # | |-- src0.txt
1010
+ # # | `-- src1.txt
1011
+ # # `-- dir1
1012
+ # # |-- src2.txt
1013
+ # # `-- src3.txt
1014
+ # Bundler::FileUtils.copy_entry('src1', 'dest1')
1015
+ # tree('dest1')
1016
+ # # => dest1
1017
+ # # |-- dir0
1018
+ # # | |-- src0.txt
1019
+ # # | `-- src1.txt
1020
+ # # `-- dir1
1021
+ # # |-- src2.txt
1022
+ # # `-- src3.txt
1023
+ #
1024
+ # The recursive copying preserves file types for regular files,
1025
+ # directories, and symbolic links;
1026
+ # other file types (FIFO streams, device files, etc.) are not supported.
1027
+ #
1028
+ # Optional arguments:
1029
+ #
1030
+ # - +dereference_root+ - if +src+ is a symbolic link,
1031
+ # follows the link (+false+ by default).
1032
+ # - +preserve+ - preserves file times (+false+ by default).
1033
+ # - +remove_destination+ - removes +dest+ before copying files (+false+ by default).
1034
+ #
1035
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
1036
+ #
1037
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
1038
+ if dereference_root
1039
+ src = File.realpath(src)
1040
+ end
1041
+
1042
+ Entry_.new(src, nil, false).wrap_traverse(proc do |ent|
1043
+ destent = Entry_.new(dest, ent.rel, false)
1044
+ File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
1045
+ ent.copy destent.path
1046
+ end, proc do |ent|
1047
+ destent = Entry_.new(dest, ent.rel, false)
1048
+ ent.copy_metadata destent.path if preserve
1049
+ end)
1050
+ end
1051
+ module_function :copy_entry
1052
+
1053
+ # Copies file from +src+ to +dest+, which should not be directories.
1054
+ #
1055
+ # Arguments +src+ and +dest+
1056
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1057
+ #
1058
+ # Examples:
1059
+ #
1060
+ # Bundler::FileUtils.touch('src0.txt')
1061
+ # Bundler::FileUtils.copy_file('src0.txt', 'dest0.txt')
1062
+ # File.file?('dest0.txt') # => true
1063
+ #
1064
+ # Optional arguments:
1065
+ #
1066
+ # - +dereference+ - if +src+ is a symbolic link,
1067
+ # follows the link (+true+ by default).
1068
+ # - +preserve+ - preserves file times (+false+ by default).
1069
+ # - +remove_destination+ - removes +dest+ before copying files (+false+ by default).
1070
+ #
1071
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
1072
+ #
1073
+ def copy_file(src, dest, preserve = false, dereference = true)
1074
+ ent = Entry_.new(src, nil, dereference)
1075
+ ent.copy_file dest
1076
+ ent.copy_metadata dest if preserve
1077
+ end
1078
+ module_function :copy_file
1079
+
1080
+ # Copies \IO stream +src+ to \IO stream +dest+ via
1081
+ # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
1082
+ #
1083
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
1084
+ #
1085
+ def copy_stream(src, dest)
1086
+ IO.copy_stream(src, dest)
1087
+ end
1088
+ module_function :copy_stream
1089
+
1090
+ # Moves entries.
1091
+ #
1092
+ # Arguments +src+ (a single path or an array of paths)
1093
+ # and +dest+ (a single path)
1094
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1095
+ #
1096
+ # If +src+ and +dest+ are on different file systems,
1097
+ # first copies, then removes +src+.
1098
+ #
1099
+ # May cause a local vulnerability if not called with keyword argument
1100
+ # <tt>secure: true</tt>;
1101
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1102
+ #
1103
+ # If +src+ is the path to a single file or directory and +dest+ does not exist,
1104
+ # moves +src+ to +dest+:
1105
+ #
1106
+ # tree('src0')
1107
+ # # => src0
1108
+ # # |-- src0.txt
1109
+ # # `-- src1.txt
1110
+ # File.exist?('dest0') # => false
1111
+ # Bundler::FileUtils.mv('src0', 'dest0')
1112
+ # File.exist?('src0') # => false
1113
+ # tree('dest0')
1114
+ # # => dest0
1115
+ # # |-- src0.txt
1116
+ # # `-- src1.txt
1117
+ #
1118
+ # If +src+ is an array of paths to files and directories
1119
+ # and +dest+ is the path to a directory,
1120
+ # copies from each path in the array to +dest+:
1121
+ #
1122
+ # File.file?('src1.txt') # => true
1123
+ # tree('src1')
1124
+ # # => src1
1125
+ # # |-- src.dat
1126
+ # # `-- src.txt
1127
+ # Dir.empty?('dest1') # => true
1128
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1')
1129
+ # tree('dest1')
1130
+ # # => dest1
1131
+ # # |-- src1
1132
+ # # | |-- src.dat
1133
+ # # | `-- src.txt
1134
+ # # `-- src1.txt
1135
+ #
1136
+ # Keyword arguments:
1137
+ #
1138
+ # - <tt>force: true</tt> - if the move includes removing +src+
1139
+ # (that is, if +src+ and +dest+ are on different file systems),
1140
+ # ignores raised exceptions of StandardError and its descendants.
1141
+ # - <tt>noop: true</tt> - does not move files.
1142
+ # - <tt>secure: true</tt> - removes +src+ securely;
1143
+ # see details at Bundler::FileUtils.remove_entry_secure.
1144
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1145
+ #
1146
+ # Bundler::FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
1147
+ # Bundler::FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
1148
+ #
1149
+ # Output:
1150
+ #
1151
+ # mv src0 dest0
1152
+ # mv src1.txt src1 dest1
1153
+ #
1154
+ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
1155
+ fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
1156
+ return if noop
1157
+ fu_each_src_dest(src, dest) do |s, d|
1158
+ destent = Entry_.new(d, nil, true)
1159
+ begin
1160
+ if destent.exist?
1161
+ if destent.directory?
1162
+ raise Errno::EEXIST, d
1163
+ end
1164
+ end
1165
+ begin
1166
+ File.rename s, d
1167
+ rescue Errno::EXDEV,
1168
+ Errno::EPERM # move from unencrypted to encrypted dir (ext4)
1169
+ copy_entry s, d, true
1170
+ if secure
1171
+ remove_entry_secure s, force
1172
+ else
1173
+ remove_entry s, force
1174
+ end
1175
+ end
1176
+ rescue SystemCallError
1177
+ raise unless force
1178
+ end
1179
+ end
1180
+ end
1181
+ module_function :mv
1182
+
1183
+ alias move mv
1184
+ module_function :move
1185
+
1186
+ # Removes entries at the paths in the given +list+
1187
+ # (a single path or an array of paths)
1188
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1189
+ #
1190
+ # Argument +list+ or its elements
1191
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1192
+ #
1193
+ # With no keyword arguments, removes files at the paths given in +list+:
1194
+ #
1195
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1196
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
1197
+ #
1198
+ # Keyword arguments:
1199
+ #
1200
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1201
+ # and its descendants.
1202
+ # - <tt>noop: true</tt> - does not remove files; returns +nil+.
1203
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1204
+ #
1205
+ # Bundler::FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
1206
+ #
1207
+ # Output:
1208
+ #
1209
+ # rm src0.dat src0.txt
1210
+ #
1211
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1212
+ #
1213
+ def rm(list, force: nil, noop: nil, verbose: nil)
1214
+ list = fu_list(list)
1215
+ fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
1216
+ return if noop
1217
+
1218
+ list.each do |path|
1219
+ remove_file path, force
1220
+ end
1221
+ end
1222
+ module_function :rm
1223
+
1224
+ alias remove rm
1225
+ module_function :remove
1226
+
1227
+ # Equivalent to:
1228
+ #
1229
+ # Bundler::FileUtils.rm(list, force: true, **kwargs)
1230
+ #
1231
+ # Argument +list+ (a single path or an array of paths)
1232
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1233
+ #
1234
+ # See Bundler::FileUtils.rm for keyword arguments.
1235
+ #
1236
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1237
+ #
1238
+ def rm_f(list, noop: nil, verbose: nil)
1239
+ rm list, force: true, noop: noop, verbose: verbose
1240
+ end
1241
+ module_function :rm_f
1242
+
1243
+ alias safe_unlink rm_f
1244
+ module_function :safe_unlink
1245
+
1246
+ # Removes entries at the paths in the given +list+
1247
+ # (a single path or an array of paths);
1248
+ # returns +list+, if it is an array, <tt>[list]</tt> otherwise.
1249
+ #
1250
+ # Argument +list+ or its elements
1251
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1252
+ #
1253
+ # May cause a local vulnerability if not called with keyword argument
1254
+ # <tt>secure: true</tt>;
1255
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1256
+ #
1257
+ # For each file path, removes the file at that path:
1258
+ #
1259
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1260
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'])
1261
+ # File.exist?('src0.txt') # => false
1262
+ # File.exist?('src0.dat') # => false
1263
+ #
1264
+ # For each directory path, recursively removes files and directories:
1265
+ #
1266
+ # tree('src1')
1267
+ # # => src1
1268
+ # # |-- dir0
1269
+ # # | |-- src0.txt
1270
+ # # | `-- src1.txt
1271
+ # # `-- dir1
1272
+ # # |-- src2.txt
1273
+ # # `-- src3.txt
1274
+ # Bundler::FileUtils.rm_r('src1')
1275
+ # File.exist?('src1') # => false
1276
+ #
1277
+ # Keyword arguments:
1278
+ #
1279
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
1280
+ # and its descendants.
1281
+ # - <tt>noop: true</tt> - does not remove entries; returns +nil+.
1282
+ # - <tt>secure: true</tt> - removes +src+ securely;
1283
+ # see details at Bundler::FileUtils.remove_entry_secure.
1284
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1285
+ #
1286
+ # Bundler::FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
1287
+ # Bundler::FileUtils.rm_r('src1', noop: true, verbose: true)
1288
+ #
1289
+ # Output:
1290
+ #
1291
+ # rm -r src0.dat src0.txt
1292
+ # rm -r src1
1293
+ #
1294
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1295
+ #
1296
+ def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
1297
+ list = fu_list(list)
1298
+ fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
1299
+ return if noop
1300
+ list.each do |path|
1301
+ if secure
1302
+ remove_entry_secure path, force
1303
+ else
1304
+ remove_entry path, force
1305
+ end
1306
+ end
1307
+ end
1308
+ module_function :rm_r
1309
+
1310
+ # Equivalent to:
1311
+ #
1312
+ # Bundler::FileUtils.rm_r(list, force: true, **kwargs)
1313
+ #
1314
+ # Argument +list+ or its elements
1315
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1316
+ #
1317
+ # May cause a local vulnerability if not called with keyword argument
1318
+ # <tt>secure: true</tt>;
1319
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1320
+ #
1321
+ # See Bundler::FileUtils.rm_r for keyword arguments.
1322
+ #
1323
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1324
+ #
1325
+ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
1326
+ rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
1327
+ end
1328
+ module_function :rm_rf
1329
+
1330
+ alias rmtree rm_rf
1331
+ module_function :rmtree
1332
+
1333
+ # Securely removes the entry given by +path+,
1334
+ # which should be the entry for a regular file, a symbolic link,
1335
+ # or a directory.
1336
+ #
1337
+ # Argument +path+
1338
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1339
+ #
1340
+ # Avoids a local vulnerability that can exist in certain circumstances;
1341
+ # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
1342
+ #
1343
+ # Optional argument +force+ specifies whether to ignore
1344
+ # raised exceptions of StandardError and its descendants.
1345
+ #
1346
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1347
+ #
1348
+ def remove_entry_secure(path, force = false)
1349
+ unless fu_have_symlink?
1350
+ remove_entry path, force
1351
+ return
1352
+ end
1353
+ fullpath = File.expand_path(path)
1354
+ st = File.lstat(fullpath)
1355
+ unless st.directory?
1356
+ File.unlink fullpath
1357
+ return
1358
+ end
1359
+ # is a directory.
1360
+ parent_st = File.stat(File.dirname(fullpath))
1361
+ unless parent_st.world_writable?
1362
+ remove_entry path, force
1363
+ return
1364
+ end
1365
+ unless parent_st.sticky?
1366
+ 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})"
1367
+ end
1368
+
1369
+ # freeze tree root
1370
+ euid = Process.euid
1371
+ dot_file = fullpath + "/."
1372
+ begin
1373
+ File.open(dot_file) {|f|
1374
+ unless fu_stat_identical_entry?(st, f.stat)
1375
+ # symlink (TOC-to-TOU attack?)
1376
+ File.unlink fullpath
1377
+ return
1378
+ end
1379
+ f.chown euid, -1
1380
+ f.chmod 0700
1381
+ }
1382
+ rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
1383
+ File.lstat(dot_file).tap {|fstat|
1384
+ unless fu_stat_identical_entry?(st, fstat)
1385
+ # symlink (TOC-to-TOU attack?)
1386
+ File.unlink fullpath
1387
+ return
1388
+ end
1389
+ File.chown euid, -1, dot_file
1390
+ File.chmod 0700, dot_file
1391
+ }
1392
+ end
1393
+
1394
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
1395
+ # TOC-to-TOU attack?
1396
+ File.unlink fullpath
1397
+ return
1398
+ end
1399
+
1400
+ # ---- tree root is frozen ----
1401
+ root = Entry_.new(path)
1402
+ root.preorder_traverse do |ent|
1403
+ if ent.directory?
1404
+ ent.chown euid, -1
1405
+ ent.chmod 0700
1406
+ end
1407
+ end
1408
+ root.postorder_traverse do |ent|
1409
+ begin
1410
+ ent.remove
1411
+ rescue
1412
+ raise unless force
1413
+ end
1414
+ end
1415
+ rescue
1416
+ raise unless force
1417
+ end
1418
+ module_function :remove_entry_secure
1419
+
1420
+ def fu_have_symlink? #:nodoc:
1421
+ File.symlink nil, nil
1422
+ rescue NotImplementedError
1423
+ return false
1424
+ rescue TypeError
1425
+ return true
1426
+ end
1427
+ private_module_function :fu_have_symlink?
1428
+
1429
+ def fu_stat_identical_entry?(a, b) #:nodoc:
1430
+ a.dev == b.dev and a.ino == b.ino
1431
+ end
1432
+ private_module_function :fu_stat_identical_entry?
1433
+
1434
+ # Removes the entry given by +path+,
1435
+ # which should be the entry for a regular file, a symbolic link,
1436
+ # or a directory.
1437
+ #
1438
+ # Argument +path+
1439
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1440
+ #
1441
+ # Optional argument +force+ specifies whether to ignore
1442
+ # raised exceptions of StandardError and its descendants.
1443
+ #
1444
+ # Related: Bundler::FileUtils.remove_entry_secure.
1445
+ #
1446
+ def remove_entry(path, force = false)
1447
+ Entry_.new(path).postorder_traverse do |ent|
1448
+ begin
1449
+ ent.remove
1450
+ rescue
1451
+ raise unless force
1452
+ end
1453
+ end
1454
+ rescue
1455
+ raise unless force
1456
+ end
1457
+ module_function :remove_entry
1458
+
1459
+ # Removes the file entry given by +path+,
1460
+ # which should be the entry for a regular file or a symbolic link.
1461
+ #
1462
+ # Argument +path+
1463
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1464
+ #
1465
+ # Optional argument +force+ specifies whether to ignore
1466
+ # raised exceptions of StandardError and its descendants.
1467
+ #
1468
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1469
+ #
1470
+ def remove_file(path, force = false)
1471
+ Entry_.new(path).remove_file
1472
+ rescue
1473
+ raise unless force
1474
+ end
1475
+ module_function :remove_file
1476
+
1477
+ # Recursively removes the directory entry given by +path+,
1478
+ # which should be the entry for a regular file, a symbolic link,
1479
+ # or a directory.
1480
+ #
1481
+ # Argument +path+
1482
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1483
+ #
1484
+ # Optional argument +force+ specifies whether to ignore
1485
+ # raised exceptions of StandardError and its descendants.
1486
+ #
1487
+ # Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
1488
+ #
1489
+ def remove_dir(path, force = false)
1490
+ raise Errno::ENOTDIR, path unless force or File.directory?(path)
1491
+ remove_entry path, force
1492
+ end
1493
+ module_function :remove_dir
1494
+
1495
+ # Returns +true+ if the contents of files +a+ and +b+ are identical,
1496
+ # +false+ otherwise.
1497
+ #
1498
+ # Arguments +a+ and +b+
1499
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1500
+ #
1501
+ # Bundler::FileUtils.identical? and Bundler::FileUtils.cmp are aliases for Bundler::FileUtils.compare_file.
1502
+ #
1503
+ # Related: Bundler::FileUtils.compare_stream.
1504
+ #
1505
+ def compare_file(a, b)
1506
+ return false unless File.size(a) == File.size(b)
1507
+ File.open(a, 'rb') {|fa|
1508
+ File.open(b, 'rb') {|fb|
1509
+ return compare_stream(fa, fb)
1510
+ }
1511
+ }
1512
+ end
1513
+ module_function :compare_file
1514
+
1515
+ alias identical? compare_file
1516
+ alias cmp compare_file
1517
+ module_function :identical?
1518
+ module_function :cmp
1519
+
1520
+ # Returns +true+ if the contents of streams +a+ and +b+ are identical,
1521
+ # +false+ otherwise.
1522
+ #
1523
+ # Arguments +a+ and +b+
1524
+ # should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
1525
+ #
1526
+ # Related: Bundler::FileUtils.compare_file.
1527
+ #
1528
+ def compare_stream(a, b)
1529
+ bsize = fu_stream_blksize(a, b)
1530
+
1531
+ sa = String.new(capacity: bsize)
1532
+ sb = String.new(capacity: bsize)
1533
+
1534
+ begin
1535
+ a.read(bsize, sa)
1536
+ b.read(bsize, sb)
1537
+ return true if sa.empty? && sb.empty?
1538
+ end while sa == sb
1539
+ false
1540
+ end
1541
+ module_function :compare_stream
1542
+
1543
+ # Copies a file entry.
1544
+ # See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
1545
+ #
1546
+ # Arguments +src+ (a single path or an array of paths)
1547
+ # and +dest+ (a single path)
1548
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
1549
+ #
1550
+ # If the entry at +dest+ does not exist, copies from +src+ to +dest+:
1551
+ #
1552
+ # File.read('src0.txt') # => "aaa\n"
1553
+ # File.exist?('dest0.txt') # => false
1554
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt')
1555
+ # File.read('dest0.txt') # => "aaa\n"
1556
+ #
1557
+ # If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
1558
+ #
1559
+ # File.read('src1.txt') # => "aaa\n"
1560
+ # File.read('dest1.txt') # => "bbb\n"
1561
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt')
1562
+ # File.read('dest1.txt') # => "aaa\n"
1563
+ #
1564
+ # If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
1565
+ # overwriting if necessary:
1566
+ #
1567
+ # File.read('src2.txt') # => "aaa\n"
1568
+ # File.read('dest2/src2.txt') # => "bbb\n"
1569
+ # Bundler::FileUtils.install('src2.txt', 'dest2')
1570
+ # File.read('dest2/src2.txt') # => "aaa\n"
1571
+ #
1572
+ # If +src+ is an array of paths and +dest+ points to a directory,
1573
+ # copies each path +path+ in +src+ to <tt>dest/path</tt>:
1574
+ #
1575
+ # File.file?('src3.txt') # => true
1576
+ # File.file?('src3.dat') # => true
1577
+ # Bundler::FileUtils.mkdir('dest3')
1578
+ # Bundler::FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
1579
+ # File.file?('dest3/src3.txt') # => true
1580
+ # File.file?('dest3/src3.dat') # => true
1581
+ #
1582
+ # Keyword arguments:
1583
+ #
1584
+ # - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
1585
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1586
+ # - <tt>mode: <i>permissions</i></tt> - changes the permissions.
1587
+ # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1588
+ # - <tt>noop: true</tt> - does not copy entries; returns +nil+.
1589
+ # - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
1590
+ # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1591
+ # - <tt>preserve: true</tt> - preserve timestamps
1592
+ # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
1593
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1594
+ #
1595
+ # Bundler::FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
1596
+ # Bundler::FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
1597
+ # Bundler::FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
1598
+ #
1599
+ # Output:
1600
+ #
1601
+ # install -c src0.txt dest0.txt
1602
+ # install -c src1.txt dest1.txt
1603
+ # install -c src2.txt dest2
1604
+ #
1605
+ # Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
1606
+ #
1607
+ def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
1608
+ noop: nil, verbose: nil)
1609
+ if verbose
1610
+ msg = +"install -c"
1611
+ msg << ' -p' if preserve
1612
+ msg << ' -m ' << mode_to_s(mode) if mode
1613
+ msg << " -o #{owner}" if owner
1614
+ msg << " -g #{group}" if group
1615
+ msg << ' ' << [src,dest].flatten.join(' ')
1616
+ fu_output_message msg
1617
+ end
1618
+ return if noop
1619
+ uid = fu_get_uid(owner)
1620
+ gid = fu_get_gid(group)
1621
+ fu_each_src_dest(src, dest) do |s, d|
1622
+ st = File.stat(s)
1623
+ unless File.exist?(d) and compare_file(s, d)
1624
+ remove_file d, true
1625
+ if d.end_with?('/')
1626
+ mkdir_p d
1627
+ copy_file s, d + File.basename(s)
1628
+ else
1629
+ mkdir_p File.expand_path('..', d)
1630
+ copy_file s, d
1631
+ end
1632
+ File.utime st.atime, st.mtime, d if preserve
1633
+ File.chmod fu_mode(mode, st), d if mode
1634
+ File.chown uid, gid, d if uid or gid
1635
+ end
1636
+ end
1637
+ end
1638
+ module_function :install
1639
+
1640
+ def user_mask(target) #:nodoc:
1641
+ target.each_char.inject(0) do |mask, chr|
1642
+ case chr
1643
+ when "u"
1644
+ mask | 04700
1645
+ when "g"
1646
+ mask | 02070
1647
+ when "o"
1648
+ mask | 01007
1649
+ when "a"
1650
+ mask | 07777
1651
+ else
1652
+ raise ArgumentError, "invalid 'who' symbol in file mode: #{chr}"
1653
+ end
1654
+ end
1655
+ end
1656
+ private_module_function :user_mask
1657
+
1658
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
1659
+ case op
1660
+ when '='
1661
+ (mode & ~user_mask) | (user_mask & mode_mask)
1662
+ when '+'
1663
+ mode | (user_mask & mode_mask)
1664
+ when '-'
1665
+ mode & ~(user_mask & mode_mask)
1666
+ end
1667
+ end
1668
+ private_module_function :apply_mask
1669
+
1670
+ def symbolic_modes_to_i(mode_sym, path) #:nodoc:
1671
+ path = File.stat(path) unless File::Stat === path
1672
+ mode = path.mode
1673
+ mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
1674
+ target, *actions = clause.split(/([=+-])/)
1675
+ raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
1676
+ target = 'a' if target.empty?
1677
+ user_mask = user_mask(target)
1678
+ actions.each_slice(2) do |op, perm|
1679
+ need_apply = op == '='
1680
+ mode_mask = (perm || '').each_char.inject(0) do |mask, chr|
1681
+ case chr
1682
+ when "r"
1683
+ mask | 0444
1684
+ when "w"
1685
+ mask | 0222
1686
+ when "x"
1687
+ mask | 0111
1688
+ when "X"
1689
+ if path.directory?
1690
+ mask | 0111
1691
+ else
1692
+ mask
1693
+ end
1694
+ when "s"
1695
+ mask | 06000
1696
+ when "t"
1697
+ mask | 01000
1698
+ when "u", "g", "o"
1699
+ if mask.nonzero?
1700
+ current_mode = apply_mask(current_mode, user_mask, op, mask)
1701
+ end
1702
+ need_apply = false
1703
+ copy_mask = user_mask(chr)
1704
+ (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
1705
+ else
1706
+ raise ArgumentError, "invalid 'perm' symbol in file mode: #{chr}"
1707
+ end
1708
+ end
1709
+
1710
+ if mode_mask.nonzero? || need_apply
1711
+ current_mode = apply_mask(current_mode, user_mask, op, mode_mask)
1712
+ end
1713
+ end
1714
+ current_mode
1715
+ end
1716
+ end
1717
+ private_module_function :symbolic_modes_to_i
1718
+
1719
+ def fu_mode(mode, path) #:nodoc:
1720
+ mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
1721
+ end
1722
+ private_module_function :fu_mode
1723
+
1724
+ def mode_to_s(mode) #:nodoc:
1725
+ mode.is_a?(String) ? mode : "%o" % mode
1726
+ end
1727
+ private_module_function :mode_to_s
1728
+
1729
+ # Changes permissions on the entries at the paths given in +list+
1730
+ # (a single path or an array of paths)
1731
+ # to the permissions given by +mode+;
1732
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1733
+ #
1734
+ # - Modifies each entry that is a regular file using
1735
+ # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
1736
+ # - Modifies each entry that is a symbolic link using
1737
+ # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
1738
+ #
1739
+ # Argument +list+ or its elements
1740
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1741
+ #
1742
+ # Argument +mode+ may be either an integer or a string:
1743
+ #
1744
+ # - \Integer +mode+: represents the permission bits to be set:
1745
+ #
1746
+ # Bundler::FileUtils.chmod(0755, 'src0.txt')
1747
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
1748
+ #
1749
+ # - \String +mode+: represents the permissions to be set:
1750
+ #
1751
+ # The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
1752
+ #
1753
+ # - +targets+ may be any combination of these letters:
1754
+ #
1755
+ # - <tt>'u'</tt>: permissions apply to the file's owner.
1756
+ # - <tt>'g'</tt>: permissions apply to users in the file's group.
1757
+ # - <tt>'o'</tt>: permissions apply to other users not in the file's group.
1758
+ # - <tt>'a'</tt> (the default): permissions apply to all users.
1759
+ #
1760
+ # - +operator+ may be one of these letters:
1761
+ #
1762
+ # - <tt>'+'</tt>: adds permissions.
1763
+ # - <tt>'-'</tt>: removes permissions.
1764
+ # - <tt>'='</tt>: sets (replaces) permissions.
1765
+ #
1766
+ # - +perms+ (may be repeated, with separating commas)
1767
+ # may be any combination of these letters:
1768
+ #
1769
+ # - <tt>'r'</tt>: Read.
1770
+ # - <tt>'w'</tt>: Write.
1771
+ # - <tt>'x'</tt>: Execute (search, for a directory).
1772
+ # - <tt>'X'</tt>: Search (for a directories only;
1773
+ # must be used with <tt>'+'</tt>)
1774
+ # - <tt>'s'</tt>: Uid or gid.
1775
+ # - <tt>'t'</tt>: Sticky bit.
1776
+ #
1777
+ # Examples:
1778
+ #
1779
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
1780
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
1781
+ #
1782
+ # Keyword arguments:
1783
+ #
1784
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1785
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1786
+ #
1787
+ # Bundler::FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
1788
+ # Bundler::FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1789
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
1790
+ # Bundler::FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
1791
+ #
1792
+ # Output:
1793
+ #
1794
+ # chmod 755 src0.txt
1795
+ # chmod 644 src0.txt src0.dat
1796
+ # chmod u=wrx,go=rx src1.txt
1797
+ # chmod u=wrx,go=rx /usr/bin/ruby
1798
+ #
1799
+ # Related: Bundler::FileUtils.chmod_R.
1800
+ #
1801
+ def chmod(mode, list, noop: nil, verbose: nil)
1802
+ list = fu_list(list)
1803
+ fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
1804
+ return if noop
1805
+ list.each do |path|
1806
+ Entry_.new(path).chmod(fu_mode(mode, path))
1807
+ end
1808
+ end
1809
+ module_function :chmod
1810
+
1811
+ # Like Bundler::FileUtils.chmod, but changes permissions recursively.
1812
+ #
1813
+ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
1814
+ list = fu_list(list)
1815
+ fu_output_message sprintf('chmod -R%s %s %s',
1816
+ (force ? 'f' : ''),
1817
+ mode_to_s(mode), list.join(' ')) if verbose
1818
+ return if noop
1819
+ list.each do |root|
1820
+ Entry_.new(root).traverse do |ent|
1821
+ begin
1822
+ ent.chmod(fu_mode(mode, ent.path))
1823
+ rescue
1824
+ raise unless force
1825
+ end
1826
+ end
1827
+ end
1828
+ end
1829
+ module_function :chmod_R
1830
+
1831
+ # Changes the owner and group on the entries at the paths given in +list+
1832
+ # (a single path or an array of paths)
1833
+ # to the given +user+ and +group+;
1834
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise:
1835
+ #
1836
+ # - Modifies each entry that is a regular file using
1837
+ # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
1838
+ # - Modifies each entry that is a symbolic link using
1839
+ # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
1840
+ #
1841
+ # Argument +list+ or its elements
1842
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1843
+ #
1844
+ # User and group:
1845
+ #
1846
+ # - Argument +user+ may be a user name or a user id;
1847
+ # if +nil+ or +-1+, the user is not changed.
1848
+ # - Argument +group+ may be a group name or a group id;
1849
+ # if +nil+ or +-1+, the group is not changed.
1850
+ # - The user must be a member of the group.
1851
+ #
1852
+ # Examples:
1853
+ #
1854
+ # # One path.
1855
+ # # User and group as string names.
1856
+ # File.stat('src0.txt').uid # => 1004
1857
+ # File.stat('src0.txt').gid # => 1004
1858
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt')
1859
+ # File.stat('src0.txt').uid # => 1006
1860
+ # File.stat('src0.txt').gid # => 1005
1861
+ #
1862
+ # # User and group as uid and gid.
1863
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt')
1864
+ # File.stat('src0.txt').uid # => 1004
1865
+ # File.stat('src0.txt').gid # => 1004
1866
+ #
1867
+ # # Array of paths.
1868
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
1869
+ #
1870
+ # # Directory (not recursive).
1871
+ # Bundler::FileUtils.chown('user2', 'group1', '.')
1872
+ #
1873
+ # Keyword arguments:
1874
+ #
1875
+ # - <tt>noop: true</tt> - does not change permissions; returns +nil+.
1876
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1877
+ #
1878
+ # Bundler::FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
1879
+ # Bundler::FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
1880
+ # Bundler::FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
1881
+ # Bundler::FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
1882
+ # Bundler::FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
1883
+ #
1884
+ # Output:
1885
+ #
1886
+ # chown user2:group1 src0.txt
1887
+ # chown 1004:1004 src0.txt
1888
+ # chown 1006:1005 src0.txt src0.dat
1889
+ # chown user2:group1 src0.txt
1890
+ # chown user2:group1 .
1891
+ #
1892
+ # Related: Bundler::FileUtils.chown_R.
1893
+ #
1894
+ def chown(user, group, list, noop: nil, verbose: nil)
1895
+ list = fu_list(list)
1896
+ fu_output_message sprintf('chown %s %s',
1897
+ (group ? "#{user}:#{group}" : user || ':'),
1898
+ list.join(' ')) if verbose
1899
+ return if noop
1900
+ uid = fu_get_uid(user)
1901
+ gid = fu_get_gid(group)
1902
+ list.each do |path|
1903
+ Entry_.new(path).chown uid, gid
1904
+ end
1905
+ end
1906
+ module_function :chown
1907
+
1908
+ # Like Bundler::FileUtils.chown, but changes owner and group recursively.
1909
+ #
1910
+ def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
1911
+ list = fu_list(list)
1912
+ fu_output_message sprintf('chown -R%s %s %s',
1913
+ (force ? 'f' : ''),
1914
+ (group ? "#{user}:#{group}" : user || ':'),
1915
+ list.join(' ')) if verbose
1916
+ return if noop
1917
+ uid = fu_get_uid(user)
1918
+ gid = fu_get_gid(group)
1919
+ list.each do |root|
1920
+ Entry_.new(root).traverse do |ent|
1921
+ begin
1922
+ ent.chown uid, gid
1923
+ rescue
1924
+ raise unless force
1925
+ end
1926
+ end
1927
+ end
1928
+ end
1929
+ module_function :chown_R
1930
+
1931
+ def fu_get_uid(user) #:nodoc:
1932
+ return nil unless user
1933
+ case user
1934
+ when Integer
1935
+ user
1936
+ when /\A\d+\z/
1937
+ user.to_i
1938
+ else
1939
+ require 'etc'
1940
+ Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
1941
+ end
1942
+ end
1943
+ private_module_function :fu_get_uid
1944
+
1945
+ def fu_get_gid(group) #:nodoc:
1946
+ return nil unless group
1947
+ case group
1948
+ when Integer
1949
+ group
1950
+ when /\A\d+\z/
1951
+ group.to_i
1952
+ else
1953
+ require 'etc'
1954
+ Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
1955
+ end
1956
+ end
1957
+ private_module_function :fu_get_gid
1958
+
1959
+ # Updates modification times (mtime) and access times (atime)
1960
+ # of the entries given by the paths in +list+
1961
+ # (a single path or an array of paths);
1962
+ # returns +list+ if it is an array, <tt>[list]</tt> otherwise.
1963
+ #
1964
+ # By default, creates an empty file for any path to a non-existent entry;
1965
+ # use keyword argument +nocreate+ to raise an exception instead.
1966
+ #
1967
+ # Argument +list+ or its elements
1968
+ # should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
1969
+ #
1970
+ # Examples:
1971
+ #
1972
+ # # Single path.
1973
+ # f = File.new('src0.txt') # Existing file.
1974
+ # f.atime # => 2022-06-10 11:11:21.200277 -0700
1975
+ # f.mtime # => 2022-06-10 11:11:21.200277 -0700
1976
+ # Bundler::FileUtils.touch('src0.txt')
1977
+ # f = File.new('src0.txt')
1978
+ # f.atime # => 2022-06-11 08:28:09.8185343 -0700
1979
+ # f.mtime # => 2022-06-11 08:28:09.8185343 -0700
1980
+ #
1981
+ # # Array of paths.
1982
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'])
1983
+ #
1984
+ # Keyword arguments:
1985
+ #
1986
+ # - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
1987
+ # instead of the current time.
1988
+ # - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
1989
+ # - <tt>noop: true</tt> - does not touch entries; returns +nil+.
1990
+ # - <tt>verbose: true</tt> - prints an equivalent command:
1991
+ #
1992
+ # Bundler::FileUtils.touch('src0.txt', noop: true, verbose: true)
1993
+ # Bundler::FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
1994
+ # Bundler::FileUtils.touch(path, noop: true, verbose: true)
1995
+ #
1996
+ # Output:
1997
+ #
1998
+ # touch src0.txt
1999
+ # touch src0.txt src0.dat
2000
+ # touch src0.txt
2001
+ #
2002
+ # Related: Bundler::FileUtils.uptodate?.
2003
+ #
2004
+ def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
2005
+ list = fu_list(list)
2006
+ t = mtime
2007
+ if verbose
2008
+ fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
2009
+ end
2010
+ return if noop
2011
+ list.each do |path|
2012
+ created = nocreate
2013
+ begin
2014
+ File.utime(t, t, path)
2015
+ rescue Errno::ENOENT
2016
+ raise if created
2017
+ File.open(path, 'a') {
2018
+ ;
2019
+ }
2020
+ created = true
2021
+ retry if t
2022
+ end
2023
+ end
2024
+ end
2025
+ module_function :touch
2026
+
2027
+ private
2028
+
2029
+ module StreamUtils_ # :nodoc:
2030
+
2031
+ private
2032
+
2033
+ case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
2034
+ when /mswin|mingw/
2035
+ def fu_windows?; true end #:nodoc:
2036
+ else
2037
+ def fu_windows?; false end #:nodoc:
2038
+ end
2039
+
2040
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
2041
+ IO.copy_stream(src, dest)
2042
+ end
2043
+
2044
+ def fu_stream_blksize(*streams) #:nodoc:
2045
+ streams.each do |s|
2046
+ next unless s.respond_to?(:stat)
2047
+ size = fu_blksize(s.stat)
2048
+ return size if size
2049
+ end
2050
+ fu_default_blksize()
2051
+ end
2052
+
2053
+ def fu_blksize(st) #:nodoc:
2054
+ s = st.blksize
2055
+ return nil unless s
2056
+ return nil if s == 0
2057
+ s
2058
+ end
2059
+
2060
+ def fu_default_blksize #:nodoc:
2061
+ 1024
2062
+ end
2063
+ end
2064
+
2065
+ include StreamUtils_
2066
+ extend StreamUtils_
2067
+
2068
+ class Entry_ #:nodoc: internal use only
2069
+ include StreamUtils_
2070
+
2071
+ def initialize(a, b = nil, deref = false)
2072
+ @prefix = @rel = @path = nil
2073
+ if b
2074
+ @prefix = a
2075
+ @rel = b
2076
+ else
2077
+ @path = a
2078
+ end
2079
+ @deref = deref
2080
+ @stat = nil
2081
+ @lstat = nil
2082
+ end
2083
+
2084
+ def inspect
2085
+ "\#<#{self.class} #{path()}>"
2086
+ end
2087
+
2088
+ def path
2089
+ if @path
2090
+ File.path(@path)
2091
+ else
2092
+ join(@prefix, @rel)
2093
+ end
2094
+ end
2095
+
2096
+ def prefix
2097
+ @prefix || @path
2098
+ end
2099
+
2100
+ def rel
2101
+ @rel
2102
+ end
2103
+
2104
+ def dereference?
2105
+ @deref
2106
+ end
2107
+
2108
+ def exist?
2109
+ begin
2110
+ lstat
2111
+ true
2112
+ rescue Errno::ENOENT
2113
+ false
2114
+ end
2115
+ end
2116
+
2117
+ def file?
2118
+ s = lstat!
2119
+ s and s.file?
2120
+ end
2121
+
2122
+ def directory?
2123
+ s = lstat!
2124
+ s and s.directory?
2125
+ end
2126
+
2127
+ def symlink?
2128
+ s = lstat!
2129
+ s and s.symlink?
2130
+ end
2131
+
2132
+ def chardev?
2133
+ s = lstat!
2134
+ s and s.chardev?
2135
+ end
2136
+
2137
+ def blockdev?
2138
+ s = lstat!
2139
+ s and s.blockdev?
2140
+ end
2141
+
2142
+ def socket?
2143
+ s = lstat!
2144
+ s and s.socket?
2145
+ end
2146
+
2147
+ def pipe?
2148
+ s = lstat!
2149
+ s and s.pipe?
2150
+ end
2151
+
2152
+ S_IF_DOOR = 0xD000
2153
+
2154
+ def door?
2155
+ s = lstat!
2156
+ s and (s.mode & 0xF000 == S_IF_DOOR)
2157
+ end
2158
+
2159
+ def entries
2160
+ opts = {}
2161
+ opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
2162
+
2163
+ files = Dir.children(path, **opts)
2164
+
2165
+ untaint = RUBY_VERSION < '2.7'
2166
+ files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
2167
+ end
2168
+
2169
+ def stat
2170
+ return @stat if @stat
2171
+ if lstat() and lstat().symlink?
2172
+ @stat = File.stat(path())
2173
+ else
2174
+ @stat = lstat()
2175
+ end
2176
+ @stat
2177
+ end
2178
+
2179
+ def stat!
2180
+ return @stat if @stat
2181
+ if lstat! and lstat!.symlink?
2182
+ @stat = File.stat(path())
2183
+ else
2184
+ @stat = lstat!
2185
+ end
2186
+ @stat
2187
+ rescue SystemCallError
2188
+ nil
2189
+ end
2190
+
2191
+ def lstat
2192
+ if dereference?
2193
+ @lstat ||= File.stat(path())
2194
+ else
2195
+ @lstat ||= File.lstat(path())
2196
+ end
2197
+ end
2198
+
2199
+ def lstat!
2200
+ lstat()
2201
+ rescue SystemCallError
2202
+ nil
2203
+ end
2204
+
2205
+ def chmod(mode)
2206
+ if symlink?
2207
+ File.lchmod mode, path() if have_lchmod?
2208
+ else
2209
+ File.chmod mode, path()
2210
+ end
2211
+ rescue Errno::EOPNOTSUPP
2212
+ end
2213
+
2214
+ def chown(uid, gid)
2215
+ if symlink?
2216
+ File.lchown uid, gid, path() if have_lchown?
2217
+ else
2218
+ File.chown uid, gid, path()
2219
+ end
2220
+ end
2221
+
2222
+ def link(dest)
2223
+ case
2224
+ when directory?
2225
+ if !File.exist?(dest) and descendant_directory?(dest, path)
2226
+ raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
2227
+ end
2228
+ begin
2229
+ Dir.mkdir dest
2230
+ rescue
2231
+ raise unless File.directory?(dest)
2232
+ end
2233
+ else
2234
+ File.link path(), dest
2235
+ end
2236
+ end
2237
+
2238
+ def copy(dest)
2239
+ lstat
2240
+ case
2241
+ when file?
2242
+ copy_file dest
2243
+ when directory?
2244
+ if !File.exist?(dest) and descendant_directory?(dest, path)
2245
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
2246
+ end
2247
+ begin
2248
+ Dir.mkdir dest
2249
+ rescue
2250
+ raise unless File.directory?(dest)
2251
+ end
2252
+ when symlink?
2253
+ File.symlink File.readlink(path()), dest
2254
+ when chardev?, blockdev?
2255
+ raise "cannot handle device file"
2256
+ when socket?
2257
+ begin
2258
+ require 'socket'
2259
+ rescue LoadError
2260
+ raise "cannot handle socket"
2261
+ else
2262
+ raise "cannot handle socket" unless defined?(UNIXServer)
2263
+ end
2264
+ UNIXServer.new(dest).close
2265
+ File.chmod lstat().mode, dest
2266
+ when pipe?
2267
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
2268
+ File.mkfifo dest, lstat().mode
2269
+ when door?
2270
+ raise "cannot handle door: #{path()}"
2271
+ else
2272
+ raise "unknown file type: #{path()}"
2273
+ end
2274
+ end
2275
+
2276
+ def copy_file(dest)
2277
+ File.open(path()) do |s|
2278
+ File.open(dest, 'wb', s.stat.mode) do |f|
2279
+ IO.copy_stream(s, f)
2280
+ end
2281
+ end
2282
+ end
2283
+
2284
+ def copy_metadata(path)
2285
+ st = lstat()
2286
+ if !st.symlink?
2287
+ File.utime st.atime, st.mtime, path
2288
+ end
2289
+ mode = st.mode
2290
+ begin
2291
+ if st.symlink?
2292
+ begin
2293
+ File.lchown st.uid, st.gid, path
2294
+ rescue NotImplementedError
2295
+ end
2296
+ else
2297
+ File.chown st.uid, st.gid, path
2298
+ end
2299
+ rescue Errno::EPERM, Errno::EACCES
2300
+ # clear setuid/setgid
2301
+ mode &= 01777
2302
+ end
2303
+ if st.symlink?
2304
+ begin
2305
+ File.lchmod mode, path
2306
+ rescue NotImplementedError, Errno::EOPNOTSUPP
2307
+ end
2308
+ else
2309
+ File.chmod mode, path
2310
+ end
2311
+ end
2312
+
2313
+ def remove
2314
+ if directory?
2315
+ remove_dir1
2316
+ else
2317
+ remove_file
2318
+ end
2319
+ end
2320
+
2321
+ def remove_dir1
2322
+ platform_support {
2323
+ Dir.rmdir path().chomp(?/)
2324
+ }
2325
+ end
2326
+
2327
+ def remove_file
2328
+ platform_support {
2329
+ File.unlink path
2330
+ }
2331
+ end
2332
+
2333
+ def platform_support
2334
+ return yield unless fu_windows?
2335
+ first_time_p = true
2336
+ begin
2337
+ yield
2338
+ rescue Errno::ENOENT
2339
+ raise
2340
+ rescue => err
2341
+ if first_time_p
2342
+ first_time_p = false
2343
+ begin
2344
+ File.chmod 0700, path() # Windows does not have symlink
2345
+ retry
2346
+ rescue SystemCallError
2347
+ end
2348
+ end
2349
+ raise err
2350
+ end
2351
+ end
2352
+
2353
+ def preorder_traverse
2354
+ stack = [self]
2355
+ while ent = stack.pop
2356
+ yield ent
2357
+ stack.concat ent.entries.reverse if ent.directory?
2358
+ end
2359
+ end
2360
+
2361
+ alias traverse preorder_traverse
2362
+
2363
+ def postorder_traverse
2364
+ if directory?
2365
+ begin
2366
+ children = entries()
2367
+ rescue Errno::EACCES
2368
+ # Failed to get the list of children.
2369
+ # Assuming there is no children, try to process the parent directory.
2370
+ yield self
2371
+ return
2372
+ end
2373
+
2374
+ children.each do |ent|
2375
+ ent.postorder_traverse do |e|
2376
+ yield e
2377
+ end
2378
+ end
2379
+ end
2380
+ yield self
2381
+ end
2382
+
2383
+ def wrap_traverse(pre, post)
2384
+ pre.call self
2385
+ if directory?
2386
+ entries.each do |ent|
2387
+ ent.wrap_traverse pre, post
2388
+ end
2389
+ end
2390
+ post.call self
2391
+ end
2392
+
2393
+ private
2394
+
2395
+ @@fileutils_rb_have_lchmod = nil
2396
+
2397
+ def have_lchmod?
2398
+ # This is not MT-safe, but it does not matter.
2399
+ if @@fileutils_rb_have_lchmod == nil
2400
+ @@fileutils_rb_have_lchmod = check_have_lchmod?
2401
+ end
2402
+ @@fileutils_rb_have_lchmod
2403
+ end
2404
+
2405
+ def check_have_lchmod?
2406
+ return false unless File.respond_to?(:lchmod)
2407
+ File.lchmod 0
2408
+ return true
2409
+ rescue NotImplementedError
2410
+ return false
2411
+ end
2412
+
2413
+ @@fileutils_rb_have_lchown = nil
2414
+
2415
+ def have_lchown?
2416
+ # This is not MT-safe, but it does not matter.
2417
+ if @@fileutils_rb_have_lchown == nil
2418
+ @@fileutils_rb_have_lchown = check_have_lchown?
2419
+ end
2420
+ @@fileutils_rb_have_lchown
2421
+ end
2422
+
2423
+ def check_have_lchown?
2424
+ return false unless File.respond_to?(:lchown)
2425
+ File.lchown nil, nil
2426
+ return true
2427
+ rescue NotImplementedError
2428
+ return false
2429
+ end
2430
+
2431
+ def join(dir, base)
2432
+ return File.path(dir) if not base or base == '.'
2433
+ return File.path(base) if not dir or dir == '.'
2434
+ begin
2435
+ File.join(dir, base)
2436
+ rescue EncodingError
2437
+ if fu_windows?
2438
+ File.join(dir.encode(::Encoding::UTF_8), base.encode(::Encoding::UTF_8))
2439
+ else
2440
+ raise
2441
+ end
2442
+ end
2443
+ end
2444
+
2445
+ if File::ALT_SEPARATOR
2446
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
2447
+ else
2448
+ DIRECTORY_TERM = "(?=/|\\z)"
2449
+ end
2450
+
2451
+ def descendant_directory?(descendant, ascendant)
2452
+ if File::FNM_SYSCASE.nonzero?
2453
+ File.expand_path(File.dirname(descendant)).casecmp(File.expand_path(ascendant)) == 0
2454
+ else
2455
+ File.expand_path(File.dirname(descendant)) == File.expand_path(ascendant)
2456
+ end
2457
+ end
2458
+ end # class Entry_
2459
+
2460
+ def fu_list(arg) #:nodoc:
2461
+ [arg].flatten.map {|path| File.path(path) }
2462
+ end
2463
+ private_module_function :fu_list
2464
+
2465
+ def fu_each_src_dest(src, dest) #:nodoc:
2466
+ fu_each_src_dest0(src, dest) do |s, d|
2467
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
2468
+ yield s, d
2469
+ end
2470
+ end
2471
+ private_module_function :fu_each_src_dest
2472
+
2473
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
2474
+ if tmp = Array.try_convert(src)
2475
+ unless target_directory or tmp.size <= 1
2476
+ tmp = tmp.map {|f| File.path(f)} # A workaround for RBS
2477
+ raise ArgumentError, "extra target #{tmp}"
2478
+ end
2479
+ tmp.each do |s|
2480
+ s = File.path(s)
2481
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
2482
+ end
2483
+ else
2484
+ src = File.path(src)
2485
+ if target_directory and File.directory?(dest)
2486
+ yield src, File.join(dest, File.basename(src))
2487
+ else
2488
+ yield src, File.path(dest)
2489
+ end
2490
+ end
2491
+ end
2492
+ private_module_function :fu_each_src_dest0
2493
+
2494
+ def fu_same?(a, b) #:nodoc:
2495
+ File.identical?(a, b)
2496
+ end
2497
+ private_module_function :fu_same?
2498
+
2499
+ def fu_output_message(msg) #:nodoc:
2500
+ output = @fileutils_output if defined?(@fileutils_output)
2501
+ output ||= $stdout
2502
+ if defined?(@fileutils_label)
2503
+ msg = @fileutils_label + msg
2504
+ end
2505
+ output.puts msg
2506
+ end
2507
+ private_module_function :fu_output_message
2508
+
2509
+ def fu_split_path(path) #:nodoc:
2510
+ path = File.path(path)
2511
+ list = []
2512
+ until (parent, base = File.split(path); parent == path or parent == ".")
2513
+ if base != '..' and list.last == '..' and !(fu_have_symlink? && File.symlink?(path))
2514
+ list.pop
2515
+ else
2516
+ list << base
2517
+ end
2518
+ path = parent
2519
+ end
2520
+ list << path
2521
+ list.reverse!
2522
+ end
2523
+ private_module_function :fu_split_path
2524
+
2525
+ def fu_common_components(target, base) #:nodoc:
2526
+ i = 0
2527
+ while target[i]&.== base[i]
2528
+ i += 1
2529
+ end
2530
+ i
2531
+ end
2532
+ private_module_function :fu_common_components
2533
+
2534
+ def fu_clean_components(*comp) #:nodoc:
2535
+ comp.shift while comp.first == "."
2536
+ return comp if comp.empty?
2537
+ clean = [comp.shift]
2538
+ path = File.join(*clean, "") # ending with File::SEPARATOR
2539
+ while c = comp.shift
2540
+ if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
2541
+ clean.pop
2542
+ path.sub!(%r((?<=\A|/)[^/]+/\z), "")
2543
+ else
2544
+ clean << c
2545
+ path << c << "/"
2546
+ end
2547
+ end
2548
+ clean
2549
+ end
2550
+ private_module_function :fu_clean_components
2551
+
2552
+ if fu_windows?
2553
+ def fu_starting_path?(path) #:nodoc:
2554
+ path&.start_with?(%r(\w:|/))
2555
+ end
2556
+ else
2557
+ def fu_starting_path?(path) #:nodoc:
2558
+ path&.start_with?("/")
2559
+ end
2560
+ end
2561
+ private_module_function :fu_starting_path?
2562
+
2563
+ # This hash table holds command options.
2564
+ OPT_TABLE = {} #:nodoc: internal use only
2565
+ (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
2566
+ (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact!
2567
+ tbl
2568
+ }
2569
+
2570
+ public
2571
+
2572
+ # Returns an array of the string names of \Bundler::FileUtils methods
2573
+ # that accept one or more keyword arguments:
2574
+ #
2575
+ # Bundler::FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
2576
+ #
2577
+ def self.commands
2578
+ OPT_TABLE.keys
2579
+ end
2580
+
2581
+ # Returns an array of the string keyword names:
2582
+ #
2583
+ # Bundler::FileUtils.options.take(3) # => ["noop", "verbose", "force"]
2584
+ #
2585
+ def self.options
2586
+ OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
2587
+ end
2588
+
2589
+ # Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
2590
+ # the arguments may be strings or symbols:
2591
+ #
2592
+ # Bundler::FileUtils.have_option?(:chmod, :noop) # => true
2593
+ # Bundler::FileUtils.have_option?('chmod', 'secure') # => false
2594
+ #
2595
+ def self.have_option?(mid, opt)
2596
+ li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
2597
+ li.include?(opt)
2598
+ end
2599
+
2600
+ # Returns an array of the string keyword name for method +mid+;
2601
+ # the argument may be a string or a symbol:
2602
+ #
2603
+ # Bundler::FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
2604
+ # Bundler::FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
2605
+ #
2606
+ def self.options_of(mid)
2607
+ OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
2608
+ end
2609
+
2610
+ # Returns an array of the string method names of the methods
2611
+ # that accept the given keyword option +opt+;
2612
+ # the argument must be a symbol:
2613
+ #
2614
+ # Bundler::FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
2615
+ #
2616
+ def self.collect_method(opt)
2617
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
2618
+ end
2619
+
2620
+ private
2621
+
2622
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern) # :nodoc:
2623
+ module LowMethods # :nodoc: internal use only
2624
+ private
2625
+ def _do_nothing(*)end
2626
+ ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
2627
+ end
2628
+
2629
+ METHODS = singleton_methods() - [:private_module_function, # :nodoc:
2630
+ :commands, :options, :have_option?, :options_of, :collect_method]
2631
+
2632
+ #
2633
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
2634
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to
2635
+ # methods in Bundler::FileUtils.
2636
+ #
2637
+ module Verbose
2638
+ include Bundler::FileUtils
2639
+ names = ::Bundler::FileUtils.collect_method(:verbose)
2640
+ names.each do |name|
2641
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
2642
+ def #{name}(*args, **options)
2643
+ super(*args, **options, verbose: true)
2644
+ end
2645
+ EOS
2646
+ end
2647
+ private(*names)
2648
+ extend self
2649
+ class << self
2650
+ public(*::Bundler::FileUtils::METHODS)
2651
+ end
2652
+ end
2653
+
2654
+ #
2655
+ # This module has all methods of Bundler::FileUtils module, but never changes
2656
+ # files/directories. This equates to passing the <tt>:noop</tt> flag
2657
+ # to methods in Bundler::FileUtils.
2658
+ #
2659
+ module NoWrite
2660
+ include Bundler::FileUtils
2661
+ include LowMethods
2662
+ names = ::Bundler::FileUtils.collect_method(:noop)
2663
+ names.each do |name|
2664
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
2665
+ def #{name}(*args, **options)
2666
+ super(*args, **options, noop: true)
2667
+ end
2668
+ EOS
2669
+ end
2670
+ private(*names)
2671
+ extend self
2672
+ class << self
2673
+ public(*::Bundler::FileUtils::METHODS)
2674
+ end
2675
+ end
2676
+
2677
+ #
2678
+ # This module has all methods of Bundler::FileUtils module, but never changes
2679
+ # files/directories, with printing message before acting.
2680
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
2681
+ # to methods in Bundler::FileUtils.
2682
+ #
2683
+ module DryRun
2684
+ include Bundler::FileUtils
2685
+ include LowMethods
2686
+ names = ::Bundler::FileUtils.collect_method(:noop)
2687
+ names.each do |name|
2688
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
2689
+ def #{name}(*args, **options)
2690
+ super(*args, **options, noop: true, verbose: true)
2691
+ end
2692
+ EOS
2693
+ end
2694
+ private(*names)
2695
+ extend self
2696
+ class << self
2697
+ public(*::Bundler::FileUtils::METHODS)
2698
+ end
2699
+ end
2700
+
2701
+ end