amp 0.5.0

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 (295) hide show
  1. data/.gitignore +1 -0
  2. data/.hgignore +26 -0
  3. data/AUTHORS +2 -0
  4. data/History.txt +6 -0
  5. data/LICENSE +37 -0
  6. data/MANIFESTO +7 -0
  7. data/Manifest.txt +294 -0
  8. data/README.md +129 -0
  9. data/Rakefile +102 -0
  10. data/SCHEDULE.markdown +12 -0
  11. data/STYLE +27 -0
  12. data/TODO.markdown +149 -0
  13. data/ampfile.rb +47 -0
  14. data/bin/amp +30 -0
  15. data/bin/amp1.9 +30 -0
  16. data/ext/amp/bz2/README.txt +39 -0
  17. data/ext/amp/bz2/bz2.c +1582 -0
  18. data/ext/amp/bz2/extconf.rb +77 -0
  19. data/ext/amp/bz2/mkmf.log +29 -0
  20. data/ext/amp/mercurial_patch/extconf.rb +5 -0
  21. data/ext/amp/mercurial_patch/mpatch.c +405 -0
  22. data/ext/amp/priority_queue/extconf.rb +5 -0
  23. data/ext/amp/priority_queue/priority_queue.c +947 -0
  24. data/ext/amp/support/extconf.rb +5 -0
  25. data/ext/amp/support/support.c +250 -0
  26. data/lib/amp.rb +200 -0
  27. data/lib/amp/commands/command.rb +507 -0
  28. data/lib/amp/commands/command_support.rb +137 -0
  29. data/lib/amp/commands/commands/config.rb +143 -0
  30. data/lib/amp/commands/commands/help.rb +29 -0
  31. data/lib/amp/commands/commands/init.rb +10 -0
  32. data/lib/amp/commands/commands/templates.rb +137 -0
  33. data/lib/amp/commands/commands/version.rb +7 -0
  34. data/lib/amp/commands/commands/workflow.rb +28 -0
  35. data/lib/amp/commands/commands/workflows/git/add.rb +65 -0
  36. data/lib/amp/commands/commands/workflows/git/copy.rb +27 -0
  37. data/lib/amp/commands/commands/workflows/git/mv.rb +23 -0
  38. data/lib/amp/commands/commands/workflows/git/rm.rb +60 -0
  39. data/lib/amp/commands/commands/workflows/hg/add.rb +53 -0
  40. data/lib/amp/commands/commands/workflows/hg/addremove.rb +86 -0
  41. data/lib/amp/commands/commands/workflows/hg/annotate.rb +46 -0
  42. data/lib/amp/commands/commands/workflows/hg/archive.rb +126 -0
  43. data/lib/amp/commands/commands/workflows/hg/branch.rb +28 -0
  44. data/lib/amp/commands/commands/workflows/hg/branches.rb +30 -0
  45. data/lib/amp/commands/commands/workflows/hg/bundle.rb +115 -0
  46. data/lib/amp/commands/commands/workflows/hg/clone.rb +95 -0
  47. data/lib/amp/commands/commands/workflows/hg/commit.rb +42 -0
  48. data/lib/amp/commands/commands/workflows/hg/copy.rb +31 -0
  49. data/lib/amp/commands/commands/workflows/hg/debug/dirstate.rb +32 -0
  50. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +36 -0
  51. data/lib/amp/commands/commands/workflows/hg/default.rb +9 -0
  52. data/lib/amp/commands/commands/workflows/hg/diff.rb +30 -0
  53. data/lib/amp/commands/commands/workflows/hg/forget.rb +11 -0
  54. data/lib/amp/commands/commands/workflows/hg/heads.rb +25 -0
  55. data/lib/amp/commands/commands/workflows/hg/identify.rb +23 -0
  56. data/lib/amp/commands/commands/workflows/hg/import.rb +135 -0
  57. data/lib/amp/commands/commands/workflows/hg/incoming.rb +85 -0
  58. data/lib/amp/commands/commands/workflows/hg/info.rb +18 -0
  59. data/lib/amp/commands/commands/workflows/hg/log.rb +21 -0
  60. data/lib/amp/commands/commands/workflows/hg/manifest.rb +13 -0
  61. data/lib/amp/commands/commands/workflows/hg/merge.rb +53 -0
  62. data/lib/amp/commands/commands/workflows/hg/move.rb +28 -0
  63. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +61 -0
  64. data/lib/amp/commands/commands/workflows/hg/pull.rb +74 -0
  65. data/lib/amp/commands/commands/workflows/hg/push.rb +20 -0
  66. data/lib/amp/commands/commands/workflows/hg/remove.rb +45 -0
  67. data/lib/amp/commands/commands/workflows/hg/resolve.rb +83 -0
  68. data/lib/amp/commands/commands/workflows/hg/revert.rb +53 -0
  69. data/lib/amp/commands/commands/workflows/hg/root.rb +13 -0
  70. data/lib/amp/commands/commands/workflows/hg/serve.rb +38 -0
  71. data/lib/amp/commands/commands/workflows/hg/status.rb +116 -0
  72. data/lib/amp/commands/commands/workflows/hg/tag.rb +69 -0
  73. data/lib/amp/commands/commands/workflows/hg/tags.rb +27 -0
  74. data/lib/amp/commands/commands/workflows/hg/tip.rb +13 -0
  75. data/lib/amp/commands/commands/workflows/hg/update.rb +27 -0
  76. data/lib/amp/commands/commands/workflows/hg/verify.rb +9 -0
  77. data/lib/amp/commands/commands/workflows/hg/view.rb +36 -0
  78. data/lib/amp/commands/dispatch.rb +181 -0
  79. data/lib/amp/commands/hooks.rb +81 -0
  80. data/lib/amp/dependencies/amp_support.rb +1 -0
  81. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +103 -0
  82. data/lib/amp/dependencies/minitar.rb +979 -0
  83. data/lib/amp/dependencies/priority_queue.rb +18 -0
  84. data/lib/amp/dependencies/priority_queue/c_priority_queue.rb +1 -0
  85. data/lib/amp/dependencies/priority_queue/poor_priority_queue.rb +46 -0
  86. data/lib/amp/dependencies/priority_queue/ruby_priority_queue.rb +525 -0
  87. data/lib/amp/dependencies/python_config.rb +211 -0
  88. data/lib/amp/dependencies/trollop.rb +713 -0
  89. data/lib/amp/dependencies/zip/ioextras.rb +155 -0
  90. data/lib/amp/dependencies/zip/stdrubyext.rb +111 -0
  91. data/lib/amp/dependencies/zip/tempfile_bugfixed.rb +186 -0
  92. data/lib/amp/dependencies/zip/zip.rb +1850 -0
  93. data/lib/amp/dependencies/zip/zipfilesystem.rb +609 -0
  94. data/lib/amp/dependencies/zip/ziprequire.rb +90 -0
  95. data/lib/amp/encoding/base85.rb +97 -0
  96. data/lib/amp/encoding/binary_diff.rb +82 -0
  97. data/lib/amp/encoding/difflib.rb +166 -0
  98. data/lib/amp/encoding/mercurial_diff.rb +378 -0
  99. data/lib/amp/encoding/mercurial_patch.rb +1 -0
  100. data/lib/amp/encoding/patch.rb +292 -0
  101. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +123 -0
  102. data/lib/amp/extensions/ditz.rb +41 -0
  103. data/lib/amp/extensions/lighthouse.rb +167 -0
  104. data/lib/amp/graphs/ancestor.rb +147 -0
  105. data/lib/amp/graphs/copies.rb +261 -0
  106. data/lib/amp/merges/merge_state.rb +164 -0
  107. data/lib/amp/merges/merge_ui.rb +322 -0
  108. data/lib/amp/merges/simple_merge.rb +450 -0
  109. data/lib/amp/profiling_hacks.rb +36 -0
  110. data/lib/amp/repository/branch_manager.rb +234 -0
  111. data/lib/amp/repository/dir_state.rb +950 -0
  112. data/lib/amp/repository/journal.rb +203 -0
  113. data/lib/amp/repository/lock.rb +207 -0
  114. data/lib/amp/repository/repositories/bundle_repository.rb +214 -0
  115. data/lib/amp/repository/repositories/http_repository.rb +377 -0
  116. data/lib/amp/repository/repositories/local_repository.rb +2661 -0
  117. data/lib/amp/repository/repository.rb +94 -0
  118. data/lib/amp/repository/store.rb +485 -0
  119. data/lib/amp/repository/tag_manager.rb +319 -0
  120. data/lib/amp/repository/updatable.rb +532 -0
  121. data/lib/amp/repository/verification.rb +431 -0
  122. data/lib/amp/repository/versioned_file.rb +475 -0
  123. data/lib/amp/revlogs/bundle_revlogs.rb +246 -0
  124. data/lib/amp/revlogs/changegroup.rb +217 -0
  125. data/lib/amp/revlogs/changelog.rb +338 -0
  126. data/lib/amp/revlogs/changeset.rb +521 -0
  127. data/lib/amp/revlogs/file_log.rb +165 -0
  128. data/lib/amp/revlogs/index.rb +493 -0
  129. data/lib/amp/revlogs/manifest.rb +195 -0
  130. data/lib/amp/revlogs/node.rb +18 -0
  131. data/lib/amp/revlogs/revlog.rb +1032 -0
  132. data/lib/amp/revlogs/revlog_support.rb +126 -0
  133. data/lib/amp/server/amp_user.rb +44 -0
  134. data/lib/amp/server/extension/amp_extension.rb +396 -0
  135. data/lib/amp/server/extension/authorization.rb +201 -0
  136. data/lib/amp/server/fancy_http_server.rb +252 -0
  137. data/lib/amp/server/fancy_views/_browser.haml +28 -0
  138. data/lib/amp/server/fancy_views/_diff_file.haml +13 -0
  139. data/lib/amp/server/fancy_views/_navbar.haml +17 -0
  140. data/lib/amp/server/fancy_views/changeset.haml +31 -0
  141. data/lib/amp/server/fancy_views/commits.haml +32 -0
  142. data/lib/amp/server/fancy_views/file.haml +35 -0
  143. data/lib/amp/server/fancy_views/file_diff.haml +23 -0
  144. data/lib/amp/server/fancy_views/harshcss/all_hallows_eve.css +72 -0
  145. data/lib/amp/server/fancy_views/harshcss/amy.css +147 -0
  146. data/lib/amp/server/fancy_views/harshcss/twilight.css +138 -0
  147. data/lib/amp/server/fancy_views/stylesheet.sass +175 -0
  148. data/lib/amp/server/http_server.rb +140 -0
  149. data/lib/amp/server/repo_user_management.rb +287 -0
  150. data/lib/amp/support/amp_config.rb +164 -0
  151. data/lib/amp/support/amp_ui.rb +287 -0
  152. data/lib/amp/support/docs.rb +54 -0
  153. data/lib/amp/support/generator.rb +78 -0
  154. data/lib/amp/support/ignore.rb +144 -0
  155. data/lib/amp/support/loaders.rb +93 -0
  156. data/lib/amp/support/logger.rb +103 -0
  157. data/lib/amp/support/match.rb +151 -0
  158. data/lib/amp/support/multi_io.rb +87 -0
  159. data/lib/amp/support/openers.rb +121 -0
  160. data/lib/amp/support/ruby_19_compatibility.rb +66 -0
  161. data/lib/amp/support/support.rb +1095 -0
  162. data/lib/amp/templates/blank.commit.erb +23 -0
  163. data/lib/amp/templates/blank.log.erb +18 -0
  164. data/lib/amp/templates/default.commit.erb +23 -0
  165. data/lib/amp/templates/default.log.erb +26 -0
  166. data/lib/amp/templates/template.rb +165 -0
  167. data/site/Rakefile +24 -0
  168. data/site/src/about/ampfile.haml +57 -0
  169. data/site/src/about/commands.haml +106 -0
  170. data/site/src/about/index.haml +33 -0
  171. data/site/src/about/performance.haml +31 -0
  172. data/site/src/about/workflows.haml +34 -0
  173. data/site/src/contribute/index.haml +65 -0
  174. data/site/src/contribute/style.haml +297 -0
  175. data/site/src/css/active4d.css +114 -0
  176. data/site/src/css/all_hallows_eve.css +72 -0
  177. data/site/src/css/all_themes.css +3299 -0
  178. data/site/src/css/amp.css +260 -0
  179. data/site/src/css/amy.css +147 -0
  180. data/site/src/css/blackboard.css +88 -0
  181. data/site/src/css/brilliance_black.css +605 -0
  182. data/site/src/css/brilliance_dull.css +599 -0
  183. data/site/src/css/cobalt.css +149 -0
  184. data/site/src/css/cur_amp.css +185 -0
  185. data/site/src/css/dawn.css +121 -0
  186. data/site/src/css/eiffel.css +121 -0
  187. data/site/src/css/espresso_libre.css +109 -0
  188. data/site/src/css/idle.css +62 -0
  189. data/site/src/css/iplastic.css +80 -0
  190. data/site/src/css/lazy.css +73 -0
  191. data/site/src/css/mac_classic.css +123 -0
  192. data/site/src/css/magicwb_amiga.css +104 -0
  193. data/site/src/css/pastels_on_dark.css +188 -0
  194. data/site/src/css/reset.css +55 -0
  195. data/site/src/css/slush_poppies.css +85 -0
  196. data/site/src/css/spacecadet.css +51 -0
  197. data/site/src/css/sunburst.css +180 -0
  198. data/site/src/css/twilight.css +137 -0
  199. data/site/src/css/zenburnesque.css +91 -0
  200. data/site/src/get/index.haml +32 -0
  201. data/site/src/helpers.rb +121 -0
  202. data/site/src/images/amp_logo.png +0 -0
  203. data/site/src/images/carbonica.png +0 -0
  204. data/site/src/images/revolution.png +0 -0
  205. data/site/src/images/tab-bg.png +0 -0
  206. data/site/src/images/tab-sliding-left.png +0 -0
  207. data/site/src/images/tab-sliding-right.png +0 -0
  208. data/site/src/include/_footer.haml +22 -0
  209. data/site/src/include/_header.haml +17 -0
  210. data/site/src/index.haml +104 -0
  211. data/site/src/learn/index.haml +46 -0
  212. data/site/src/scripts/jquery-1.3.2.min.js +19 -0
  213. data/site/src/scripts/jquery.cookie.js +96 -0
  214. data/tasks/stats.rake +155 -0
  215. data/tasks/yard.rake +171 -0
  216. data/test/dirstate_tests/dirstate +0 -0
  217. data/test/dirstate_tests/hgrc +5 -0
  218. data/test/dirstate_tests/test_dir_state.rb +192 -0
  219. data/test/functional_tests/resources/.hgignore +2 -0
  220. data/test/functional_tests/resources/STYLE.txt +25 -0
  221. data/test/functional_tests/resources/command.rb +372 -0
  222. data/test/functional_tests/resources/commands/annotate.rb +57 -0
  223. data/test/functional_tests/resources/commands/experimental/lolcats.rb +17 -0
  224. data/test/functional_tests/resources/commands/heads.rb +22 -0
  225. data/test/functional_tests/resources/commands/manifest.rb +12 -0
  226. data/test/functional_tests/resources/commands/status.rb +90 -0
  227. data/test/functional_tests/resources/version2/.hgignore +5 -0
  228. data/test/functional_tests/resources/version2/STYLE.txt +25 -0
  229. data/test/functional_tests/resources/version2/command.rb +372 -0
  230. data/test/functional_tests/resources/version2/commands/annotate.rb +45 -0
  231. data/test/functional_tests/resources/version2/commands/experimental/lolcats.rb +17 -0
  232. data/test/functional_tests/resources/version2/commands/heads.rb +22 -0
  233. data/test/functional_tests/resources/version2/commands/manifest.rb +12 -0
  234. data/test/functional_tests/resources/version2/commands/status.rb +90 -0
  235. data/test/functional_tests/resources/version3/.hgignore +5 -0
  236. data/test/functional_tests/resources/version3/STYLE.txt +31 -0
  237. data/test/functional_tests/resources/version3/command.rb +376 -0
  238. data/test/functional_tests/resources/version3/commands/annotate.rb +45 -0
  239. data/test/functional_tests/resources/version3/commands/experimental/lolcats.rb +17 -0
  240. data/test/functional_tests/resources/version3/commands/heads.rb +22 -0
  241. data/test/functional_tests/resources/version3/commands/manifest.rb +12 -0
  242. data/test/functional_tests/resources/version3/commands/status.rb +90 -0
  243. data/test/functional_tests/resources/version4/.hgignore +5 -0
  244. data/test/functional_tests/resources/version4/STYLE.txt +31 -0
  245. data/test/functional_tests/resources/version4/command.rb +376 -0
  246. data/test/functional_tests/resources/version4/commands/experimental/lolcats.rb +17 -0
  247. data/test/functional_tests/resources/version4/commands/heads.rb +22 -0
  248. data/test/functional_tests/resources/version4/commands/manifest.rb +12 -0
  249. data/test/functional_tests/resources/version4/commands/stats.rb +25 -0
  250. data/test/functional_tests/resources/version4/commands/status.rb +90 -0
  251. data/test/functional_tests/resources/version5_1/.hgignore +5 -0
  252. data/test/functional_tests/resources/version5_1/STYLE.txt +2 -0
  253. data/test/functional_tests/resources/version5_1/command.rb +374 -0
  254. data/test/functional_tests/resources/version5_1/commands/experimental/lolcats.rb +17 -0
  255. data/test/functional_tests/resources/version5_1/commands/heads.rb +22 -0
  256. data/test/functional_tests/resources/version5_1/commands/manifest.rb +12 -0
  257. data/test/functional_tests/resources/version5_1/commands/stats.rb +25 -0
  258. data/test/functional_tests/resources/version5_1/commands/status.rb +90 -0
  259. data/test/functional_tests/resources/version5_2/.hgignore +5 -0
  260. data/test/functional_tests/resources/version5_2/STYLE.txt +14 -0
  261. data/test/functional_tests/resources/version5_2/command.rb +376 -0
  262. data/test/functional_tests/resources/version5_2/commands/experimental/lolcats.rb +17 -0
  263. data/test/functional_tests/resources/version5_2/commands/manifest.rb +12 -0
  264. data/test/functional_tests/resources/version5_2/commands/newz.rb +12 -0
  265. data/test/functional_tests/resources/version5_2/commands/stats.rb +25 -0
  266. data/test/functional_tests/resources/version5_2/commands/status.rb +90 -0
  267. data/test/functional_tests/test_functional.rb +604 -0
  268. data/test/localrepo_tests/test_local_repo.rb +121 -0
  269. data/test/localrepo_tests/testrepo.tar.gz +0 -0
  270. data/test/manifest_tests/00manifest.i +0 -0
  271. data/test/manifest_tests/test_manifest.rb +72 -0
  272. data/test/merge_tests/base.txt +10 -0
  273. data/test/merge_tests/expected.local.txt +16 -0
  274. data/test/merge_tests/local.txt +11 -0
  275. data/test/merge_tests/remote.txt +11 -0
  276. data/test/merge_tests/test_merge.rb +26 -0
  277. data/test/revlog_tests/00changelog.i +0 -0
  278. data/test/revlog_tests/revision_added_changelog.i +0 -0
  279. data/test/revlog_tests/test_adding_index.i +0 -0
  280. data/test/revlog_tests/test_revlog.rb +333 -0
  281. data/test/revlog_tests/testindex.i +0 -0
  282. data/test/store_tests/store.tar.gz +0 -0
  283. data/test/store_tests/test_fncache_store.rb +122 -0
  284. data/test/test_amp.rb +9 -0
  285. data/test/test_base85.rb +14 -0
  286. data/test/test_bdiff.rb +42 -0
  287. data/test/test_commands.rb +122 -0
  288. data/test/test_difflib.rb +50 -0
  289. data/test/test_helper.rb +15 -0
  290. data/test/test_journal.rb +29 -0
  291. data/test/test_match.rb +134 -0
  292. data/test/test_mdiff.rb +74 -0
  293. data/test/test_mpatch.rb +14 -0
  294. data/test/test_support.rb +24 -0
  295. metadata +385 -0
@@ -0,0 +1,211 @@
1
+ require 'delegate'
2
+
3
+ Boolean = :bool unless defined? Boolean
4
+
5
+ ##
6
+ # = PythonConfig
7
+ # Class for parsing and writing Python configuration files created by the
8
+ # ConfigParser classes in Python. These files are structured like this:
9
+ # [Section Name]
10
+ # key = value
11
+ # otherkey: othervalue
12
+ #
13
+ # [Other Section]
14
+ # key: value3
15
+ # otherkey = value4
16
+ #
17
+ # Leading whitespace before values are trimmed, and the key must be the at the
18
+ # start of the line - no leading whitespace there. You can use : or = .
19
+ #
20
+ # Multiline values are supported, as long as the second (or third, etc.) lines
21
+ # start with whitespace:
22
+ #
23
+ # [Section]
24
+ # bigstring: This is a very long string, so I'm not sure I'll be
25
+ # able to fit it on one line, but as long as
26
+ # there is one space before each line, I'm ok. Tabs work too.
27
+ #
28
+ # Also, this class supports interpolation:
29
+ # [Awards]
30
+ # output: Congratulations for winning %(prize)!
31
+ # prize: the lottery
32
+ # Will result in:
33
+ # config.sections["Awards"]["output"] == "Congratulations for winning the lottery!"
34
+ #
35
+ # You can also access the sections with the dot operator, but only with all-lowercase:
36
+ # [Awards]
37
+ # key:value
38
+ # [prizes]
39
+ # lottery=3.2 million
40
+ #
41
+ # config.awards["key"] #=> "value"
42
+ # config.prizes["lottery"] #=> "3.2 million"
43
+ #
44
+ # You can modify any values you want, though to add sections, you should use the add_section
45
+ # method.
46
+ # config.sections["prizes"]["lottery"] = "100 dollars" # someone hit the jackpot
47
+ # config.add_section("Candies")
48
+ # config.candies["green"] = "tasty"
49
+ # When you want to output a configuration, just call its +to_s+ method.
50
+ # File.open("output.ini","w") do |out|
51
+ # out.write config.to_s
52
+ # end
53
+ module PythonConfig
54
+ VERSION = '1.0.1'
55
+ MAX_INTERPOLATION_DEPTH = 200
56
+ # Don't make recursive interpolating values!
57
+ class InterpolationTooDeepError < StandardError; end
58
+ # This is the main class that handles configurations. You parse, modify, and output
59
+ # through this class. See the README for tons of examples.
60
+ class ConfigParser
61
+ attr_reader :sections
62
+ SECTION_REGEXP = /\[([^\[\]]*)\]/
63
+ ASSIGNMENT_REGEXP = /([^:=\s]+)\s*[:=]\s*([^\n]*?)$/
64
+ LONG_HEADER_REGEXP = /^([ \t]+)([^\n]+)$/
65
+ # Creates a new ConfigParser. If +io+ is provided, the configuration file is read
66
+ # from the io.
67
+ def initialize(io = nil)
68
+ @sections = {}
69
+ io.each do |line|
70
+ parse_line line
71
+ end unless io.nil?
72
+ end
73
+
74
+ def clone
75
+ newconfig = ConfigParser.new
76
+ @sections.each do |key, val|
77
+ newconfig.add_section key, val.source_hash.dup
78
+ end
79
+ newconfig
80
+ end
81
+ alias_method :dup, :clone
82
+
83
+ def parse_line(line) #:nodoc:
84
+
85
+ if line =~ SECTION_REGEXP
86
+ section_name = $1
87
+ @cursection = add_section section_name
88
+ elsif line =~ ASSIGNMENT_REGEXP
89
+ @cursection[$1] = $2
90
+ @cur_assignment = $1
91
+ elsif line =~ LONG_HEADER_REGEXP
92
+ @cursection[@cur_assignment] += " " + $2
93
+ end
94
+ end
95
+
96
+ # Returns the names of all the sections, which can be used for keys into the sections
97
+ def section_names
98
+ @sections.keys
99
+ end
100
+
101
+ # Creates a new section, with the values as provided by the (optional) values parameter
102
+ def add_section(section_name, values={})
103
+ newsection = ConfigSection.new(values)
104
+ @sections[section_name] = newsection
105
+ self.instance_eval %Q{
106
+ def #{section_name.downcase.gsub('-','_')}
107
+ @sections["#{section_name}"]
108
+ end
109
+ }
110
+ newsection
111
+ end
112
+
113
+ # Returns the section given by +section+
114
+ def [](section)
115
+ result = (@sections[section] || add_section(section))
116
+ result
117
+ end
118
+
119
+ # Returns the configuration as a string that can be output to a file. Does not perform
120
+ # interpolation before writing.
121
+ def to_s
122
+ output = ""
123
+ @sections.each do |k,v|
124
+ output << "[#{k}]\n"
125
+ output << v.to_s
126
+ end
127
+ output
128
+ end
129
+
130
+ # Writes the configuration to a given file.
131
+ def write(file)
132
+ File.open(file, "w") do |out|
133
+ out.write self.to_s
134
+ end
135
+ end
136
+ alias_method :save, :write
137
+
138
+ def merge! other_config
139
+ other_config.sections.each do |name, other_section|
140
+ newsection = (@sections[name] || add_section(name))
141
+ other_section.each do |key, value|
142
+ newsection[key] = value # avoid interpolation
143
+ end
144
+ end
145
+ end
146
+
147
+ end
148
+ # = ConfigSection
149
+ # This is a simple section in a config document - treat it exactly like a hash,
150
+ # whose keys are the keys in the config, and whose value are the values in the config.
151
+ #
152
+ # This is a separate class entirely because it has to handle the magical interpolation
153
+ # that allows this ini file:
154
+ # [Awards]
155
+ # output: Congratulations for winning %(prize)!
156
+ # prize: the lottery
157
+ # To result in:
158
+ # config.sections["Awards"]["output"] == "Congratulations for winning the lottery!"
159
+ #
160
+ class ConfigSection < DelegateClass(Hash)
161
+ attr_reader :source_hash
162
+ def initialize(source)
163
+ @source_hash = source
164
+ super(@source_hash)
165
+ end
166
+
167
+ def [](*args) #:nodoc:
168
+ raise ArgumentError.new("Must provide either 1 or 2 args") unless (1..3).include? args.size
169
+ key = args[0]
170
+ str = @source_hash[key]
171
+ str = interpolate str
172
+ if args.size == 3 && str.nil? || str == ""
173
+ return args[2]
174
+ end
175
+ return str if args.size == 1
176
+
177
+ type = args[1]
178
+ if type == Integer || type == Fixnum || type == Bignum
179
+ result = str.to_i
180
+ elsif type == Boolean
181
+ result = str && (str.downcase == "true")
182
+ elsif type == Float
183
+ result = str.to_f
184
+ elsif type == Array
185
+ result = str.split(",").map {|s| s.strip}
186
+ elsif type == String
187
+ result = str
188
+ end
189
+ return result if args.size == 2
190
+
191
+
192
+ (str.nil? || str.strip == "") ? args[2] : result
193
+ end
194
+
195
+ def interpolate(str, cur_depth=0) #:nodoc:
196
+ raise InterpolationTooDeepError.new("Interpolation too deep!") if cur_depth > PythonConfig::MAX_INTERPOLATION_DEPTH
197
+ nextval = str
198
+ nextval = str.gsub(/\%\((.*)\)/,@source_hash[$1]) if str =~ /%\((.*)\)/
199
+ nextval = interpolate(nextval,cur_depth+1) if nextval =~ /%\((.*)\)/
200
+ nextval
201
+ end
202
+
203
+ def to_s #:nodoc:
204
+ output = ""
205
+ @source_hash.each do |k,v|
206
+ output << "#{k} = #{v.to_s.gsub(/\n/,"\n ")}" << "\n"
207
+ end
208
+ output
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,713 @@
1
+ ## lib/trollop.rb -- trollop command-line processing library
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2007 William Morgan
4
+ ## License:: GNU GPL version 2
5
+
6
+ module Trollop
7
+
8
+ VERSION = "1.13"
9
+
10
+ ## Thrown by Parser in the event of a commandline error. Not needed if
11
+ ## you're using the Trollop::options entry.
12
+ class CommandlineError < StandardError; end
13
+
14
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
15
+ ## automatically by Trollop#options.
16
+ class HelpNeeded < StandardError; end
17
+
18
+ ## Thrown by Parser if the user passes in '-h' or '--version'. Handled
19
+ ## automatically by Trollop#options.
20
+ class VersionNeeded < StandardError; end
21
+
22
+ ## Regex for floating point numbers
23
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))$/
24
+
25
+ ## Regex for parameters
26
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
27
+
28
+ ## The commandline parser. In typical usage, the methods in this class
29
+ ## will be handled internally by Trollop::options. In this case, only the
30
+ ## #opt, #banner and #version, #depends, and #conflicts methods will
31
+ ## typically be called.
32
+ ##
33
+ ## If it's necessary to instantiate this class (for more complicated
34
+ ## argument-parsing situations), be sure to call #parse to actually
35
+ ## produce the output hash.
36
+ class Parser
37
+
38
+ ## The set of values that indicate a flag option when passed as the
39
+ ## +:type+ parameter of #opt.
40
+ FLAG_TYPES = [:flag, :bool, :boolean]
41
+
42
+ ## The set of values that indicate a single-parameter option when
43
+ ## passed as the +:type+ parameter of #opt.
44
+ ##
45
+ ## A value of +io+ corresponds to a readable IO resource, including
46
+ ## a filename, URI, or the strings 'stdin' or '-'.
47
+ SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io]
48
+
49
+ ## The set of values that indicate a multiple-parameter option when
50
+ ## passed as the +:type+ parameter of #opt.
51
+ MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios]
52
+
53
+ ## The complete set of legal values for the +:type+ parameter of #opt.
54
+ TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
55
+
56
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
57
+
58
+ ## The values from the commandline that were not interpreted by #parse.
59
+ attr_reader :leftovers
60
+
61
+ ## The complete configuration hashes for each option. (Mainly useful
62
+ ## for testing.)
63
+ attr_reader :specs
64
+
65
+ ## Initializes the parser, and instance-evaluates any block given.
66
+ def initialize *a, &b
67
+ @version = nil
68
+ @leftovers = []
69
+ @specs = {}
70
+ @long = {}
71
+ @short = {}
72
+ @order = []
73
+ @constraints = []
74
+ @stop_words = []
75
+ @stop_on_unknown = false
76
+
77
+ #instance_eval(&b) if b # can't take arguments
78
+ cloaker(&b).bind(self).call(*a) if b
79
+ end
80
+
81
+ ## Define an option. +name+ is the option name, a unique identifier
82
+ ## for the option that you will use internally, which should be a
83
+ ## symbol or a string. +desc+ is a string description which will be
84
+ ## displayed in help messages.
85
+ ##
86
+ ## Takes the following optional arguments:
87
+ ##
88
+ ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
89
+ ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+.
90
+ ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
91
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
92
+ ## [+:required+] If set to +true+, the argument must be provided on the commandline.
93
+ ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
94
+ ##
95
+ ## Note that there are two types of argument multiplicity: an argument
96
+ ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
97
+ ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
98
+ ##
99
+ ## Arguments that take multiple values should have a +:type+ parameter
100
+ ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
101
+ ## value of an array of the correct type (e.g. [String]). The
102
+ ## value of this argument will be an array of the parameters on the
103
+ ## commandline.
104
+ ##
105
+ ## Arguments that can occur multiple times should be marked with
106
+ ## +:multi+ => +true+. The value of this argument will also be an array.
107
+ ##
108
+ ## These two attributes can be combined (e.g. +:type+ => +:strings+,
109
+ ## +:multi+ => +true+), in which case the value of the argument will be
110
+ ## an array of arrays.
111
+ ##
112
+ ## There's one ambiguous case to be aware of: when +:multi+: is true and a
113
+ ## +:default+ is set to an array (of something), it's ambiguous whether this
114
+ ## is a multi-value argument as well as a multi-occurrence argument.
115
+ ## In thise case, Trollop assumes that it's not a multi-value argument.
116
+ ## If you want a multi-value, multi-occurrence argument with a default
117
+ ## value, you must specify +:type+ as well.
118
+
119
+ def opt name, desc="", opts={}
120
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
121
+
122
+ ## fill in :type
123
+ opts[:type] = # normalize
124
+ case opts[:type]
125
+ when :boolean, :bool; :flag
126
+ when :integer; :int
127
+ when :integers; :ints
128
+ when :double; :float
129
+ when :doubles; :floats
130
+ when Class
131
+ case opts[:type].to_s # sigh... there must be a better way to do this
132
+ when 'TrueClass', 'FalseClass'; :flag
133
+ when 'String'; :string
134
+ when 'Integer'; :int
135
+ when 'Float'; :float
136
+ when 'IO'; :io
137
+ else
138
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
139
+ end
140
+ when nil; nil
141
+ else
142
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
143
+ opts[:type]
144
+ end
145
+
146
+ ## for options with :multi => true, an array default doesn't imply
147
+ ## a multi-valued argument. for that you have to specify a :type
148
+ ## as well. (this is how we disambiguate an ambiguous situation;
149
+ ## see the docs for Parser#opt for details.)
150
+ disambiguated_default =
151
+ if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
152
+ opts[:default].first
153
+ else
154
+ opts[:default]
155
+ end
156
+
157
+ type_from_default =
158
+ case disambiguated_default
159
+ when Integer; :int
160
+ when Numeric; :float
161
+ when TrueClass, FalseClass; :flag
162
+ when String; :string
163
+ when IO; :io
164
+ when Array
165
+ if opts[:default].empty?
166
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
167
+ end
168
+ case opts[:default][0] # the first element determines the types
169
+ when Integer; :ints
170
+ when Numeric; :floats
171
+ when String; :strings
172
+ when IO; :ios
173
+ else
174
+ raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
175
+ end
176
+ when nil; nil
177
+ else
178
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
179
+ end
180
+
181
+ raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
182
+
183
+ opts[:type] = opts[:type] || type_from_default || :flag
184
+
185
+ ## fill in :long
186
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
187
+ opts[:long] =
188
+ case opts[:long]
189
+ when /^--([^-].*)$/
190
+ $1
191
+ when /^[^-]/
192
+ opts[:long]
193
+ else
194
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
195
+ end
196
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
197
+
198
+ ## fill in :short
199
+ opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
200
+ opts[:short] = case opts[:short]
201
+ when /^-(.)$/; $1
202
+ when nil, :none, /^.$/; opts[:short]
203
+ else raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
204
+ end
205
+
206
+ if opts[:short]
207
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
208
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
209
+ end
210
+
211
+ ## fill in :default for flags
212
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
213
+
214
+ ## autobox :default for :multi (multi-occurrence) arguments
215
+ opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
216
+
217
+ ## fill in :multi
218
+ opts[:multi] ||= false
219
+
220
+ opts[:desc] ||= desc
221
+ @long[opts[:long]] = name
222
+ @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
223
+ @specs[name] = opts
224
+ @order << [:opt, name]
225
+ end
226
+
227
+ ## Sets the version string. If set, the user can request the version
228
+ ## on the commandline. Should probably be of the form "<program name>
229
+ ## <version number>".
230
+ def version s=nil; @version = s if s; @version end
231
+
232
+ ## Adds text to the help display. Can be interspersed with calls to
233
+ ## #opt to build a multi-section help page.
234
+ def banner s; @order << [:text, s] end
235
+ alias :text :banner
236
+
237
+ ## Marks two (or more!) options as requiring each other. Only handles
238
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
239
+ ## better modeled with Trollop::die.
240
+ def depends *syms
241
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
242
+ @constraints << [:depends, syms]
243
+ end
244
+
245
+ ## Marks two (or more!) options as conflicting.
246
+ def conflicts *syms
247
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
248
+ @constraints << [:conflicts, syms]
249
+ end
250
+
251
+ ## Defines a set of words which cause parsing to terminate when
252
+ ## encountered, such that any options to the left of the word are
253
+ ## parsed as usual, and options to the right of the word are left
254
+ ## intact.
255
+ ##
256
+ ## A typical use case would be for subcommand support, where these
257
+ ## would be set to the list of subcommands. A subsequent Trollop
258
+ ## invocation would then be used to parse subcommand options, after
259
+ ## shifting the subcommand off of ARGV.
260
+ def stop_on *words
261
+ @stop_words = [*words].flatten
262
+ end
263
+
264
+ ## Similar to #stop_on, but stops on any unknown word when encountered
265
+ ## (unless it is a parameter for an argument). This is useful for
266
+ ## cases where you don't know the set of subcommands ahead of time,
267
+ ## i.e., without first parsing the global options.
268
+ def stop_on_unknown
269
+ @stop_on_unknown = true
270
+ end
271
+
272
+ ## Parses the commandline. Typically called by Trollop::options.
273
+ def parse cmdline=ARGV
274
+ vals = {}
275
+ required = {}
276
+
277
+ opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
278
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
279
+
280
+ @specs.each do |sym, opts|
281
+ required[sym] = true if opts[:required]
282
+ vals[sym] = opts[:default]
283
+ end
284
+
285
+ resolve_default_short_options
286
+
287
+ ## resolve symbols
288
+ given_args = {}
289
+ @leftovers = each_arg cmdline do |arg, params|
290
+ sym = case arg
291
+ when /^-([^-])$/
292
+ @short[$1]
293
+ when /^--([^-]\S*)$/
294
+ @long[$1]
295
+ else
296
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
297
+ end
298
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
299
+
300
+ if given_args.include?(sym) && !@specs[sym][:multi]
301
+ raise CommandlineError, "option '#{arg}' specified multiple times"
302
+ end
303
+
304
+ given_args[sym] ||= {}
305
+
306
+ given_args[sym][:arg] = arg
307
+ given_args[sym][:params] ||= []
308
+
309
+ # The block returns the number of parameters taken.
310
+ num_params_taken = 0
311
+
312
+ unless params.nil?
313
+ if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
314
+ given_args[sym][:params] << params[0, 1] # take the first parameter
315
+ num_params_taken = 1
316
+ elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
317
+ given_args[sym][:params] << params # take all the parameters
318
+ num_params_taken = params.size
319
+ end
320
+ end
321
+
322
+ num_params_taken
323
+ end
324
+
325
+ ## check for version and help args
326
+ raise VersionNeeded if given_args.include? :version
327
+ raise HelpNeeded if given_args.include? :help
328
+
329
+ ## check constraint satisfaction
330
+ @constraints.each do |type, syms|
331
+ constraint_sym = syms.find { |sym| given_args[sym] }
332
+ next unless constraint_sym
333
+
334
+ case type
335
+ when :depends
336
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
337
+ when :conflicts
338
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
339
+ end
340
+ end
341
+
342
+ required.each do |sym, val|
343
+ raise CommandlineError, "option '#{sym}' must be specified" unless given_args.include? sym
344
+ end
345
+
346
+ ## parse parameters
347
+ given_args.each do |sym, given_data|
348
+ arg = given_data[:arg]
349
+ params = given_data[:params]
350
+
351
+ opts = @specs[sym]
352
+ raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
353
+
354
+ vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
355
+
356
+ case opts[:type]
357
+ when :flag
358
+ vals[sym] = !opts[:default]
359
+ when :int, :ints
360
+ vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
361
+ when :float, :floats
362
+ vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
363
+ when :string, :strings
364
+ vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
365
+ when :io, :ios
366
+ vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
367
+ end
368
+
369
+ if SINGLE_ARG_TYPES.include?(opts[:type])
370
+ unless opts[:multi] # single parameter
371
+ vals[sym] = vals[sym][0][0]
372
+ else # multiple options, each with a single parameter
373
+ vals[sym] = vals[sym].map { |p| p[0] }
374
+ end
375
+ elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
376
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
377
+ end
378
+ # else: multiple options, with multiple parameters
379
+ end
380
+
381
+ ## allow openstruct-style accessors
382
+ class << vals
383
+ def method_missing(m, *args)
384
+ self[m] || self[m.to_s]
385
+ end
386
+ end
387
+ vals
388
+ end
389
+
390
+ ## Print the help message to +stream+.
391
+ def educate stream=$stdout
392
+ width # just calculate it now; otherwise we have to be careful not to
393
+ # call this unless the cursor's at the beginning of a line.
394
+
395
+ left = {}
396
+ @specs.each do |name, spec|
397
+ left[name] = "--#{spec[:long]}" +
398
+ (spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
399
+ case spec[:type]
400
+ when :flag; ""
401
+ when :int; " <i>"
402
+ when :ints; " <i+>"
403
+ when :string; " <s>"
404
+ when :strings; " <s+>"
405
+ when :float; " <f>"
406
+ when :floats; " <f+>"
407
+ when :io; " <filename/uri>"
408
+ when :ios; " <filename/uri+>"
409
+ end
410
+ end
411
+
412
+ leftcol_width = left.values.map { |s| s.length }.max || 0
413
+ rightcol_start = leftcol_width + 6 # spaces
414
+
415
+ unless @order.size > 0 && @order.first.first == :text
416
+ stream.puts "#@version\n" if @version
417
+ stream.puts "Options:"
418
+ end
419
+
420
+ @order.each do |what, opt|
421
+ if what == :text
422
+ stream.puts wrap(opt)
423
+ next
424
+ end
425
+
426
+ spec = @specs[opt]
427
+ stream.printf " %#{leftcol_width}s: ", left[opt]
428
+ desc = spec[:desc] + begin
429
+ default_s = case spec[:default]
430
+ when $stdout; "<stdout>"
431
+ when $stdin; "<stdin>"
432
+ when $stderr; "<stderr>"
433
+ when Array
434
+ spec[:default].join(", ")
435
+ else
436
+ spec[:default].to_s
437
+ end
438
+
439
+ if spec[:default]
440
+ if spec[:desc] =~ /\.$/
441
+ " (Default: #{default_s})"
442
+ else
443
+ " (default: #{default_s})"
444
+ end
445
+ else
446
+ ""
447
+ end
448
+ end
449
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
450
+ end
451
+ end
452
+
453
+ def width #:nodoc:
454
+ @width ||= if $stdout.tty?
455
+ begin
456
+ require 'curses'
457
+ Curses::init_screen
458
+ x = Curses::cols
459
+ Curses::close_screen
460
+ x
461
+ rescue Exception
462
+ 80
463
+ end
464
+ else
465
+ 80
466
+ end
467
+ end
468
+
469
+ def wrap str, opts={} # :nodoc:
470
+ if str == ""
471
+ [""]
472
+ else
473
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
474
+ end
475
+ end
476
+
477
+ private
478
+
479
+ ## yield successive arg, parameter pairs
480
+ def each_arg args
481
+ remains = []
482
+ i = 0
483
+
484
+ until i >= args.length
485
+ if @stop_words.member? args[i]
486
+ remains += args[i .. -1]
487
+ return remains
488
+ end
489
+ case args[i]
490
+ when /^--$/ # arg terminator
491
+ remains += args[(i + 1) .. -1]
492
+ return remains
493
+ when /^--(\S+?)=(.*)$/ # long argument with equals
494
+ yield "--#{$1}", [$2]
495
+ i += 1
496
+ when /^--(\S+)$/ # long argument
497
+ params = collect_argument_parameters(args, i + 1)
498
+ unless params.empty?
499
+ num_params_taken = yield args[i], params
500
+ unless num_params_taken
501
+ if @stop_on_unknown
502
+ remains += args[i + 1 .. -1]
503
+ return remains
504
+ else
505
+ remains += params
506
+ end
507
+ end
508
+ i += 1 + num_params_taken
509
+ else # long argument no parameter
510
+ yield args[i], nil
511
+ i += 1
512
+ end
513
+ when /^-(\S+)$/ # one or more short arguments
514
+ shortargs = $1.split(//)
515
+ shortargs.each_with_index do |a, j|
516
+ if j == (shortargs.length - 1)
517
+ params = collect_argument_parameters(args, i + 1)
518
+ unless params.empty?
519
+ num_params_taken = yield "-#{a}", params
520
+ unless num_params_taken
521
+ if @stop_on_unknown
522
+ remains += args[i + 1 .. -1]
523
+ return remains
524
+ else
525
+ remains += params
526
+ end
527
+ end
528
+ i += 1 + num_params_taken
529
+ else # argument no parameter
530
+ yield "-#{a}", nil
531
+ i += 1
532
+ end
533
+ else
534
+ yield "-#{a}", nil
535
+ end
536
+ end
537
+ else
538
+ if @stop_on_unknown
539
+ remains += args[i .. -1]
540
+ return remains
541
+ else
542
+ remains << args[i]
543
+ i += 1
544
+ end
545
+ end
546
+ end
547
+
548
+ remains
549
+ end
550
+
551
+ def parse_integer_parameter param, arg
552
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
553
+ param.to_i
554
+ end
555
+
556
+ def parse_float_parameter param, arg
557
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
558
+ param.to_f
559
+ end
560
+
561
+ def parse_io_parameter param, arg
562
+ case param
563
+ when /^(stdin|-)$/i; $stdin
564
+ else
565
+ require 'open-uri'
566
+ begin
567
+ open param
568
+ rescue SystemCallError => e
569
+ raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}"
570
+ end
571
+ end
572
+ end
573
+
574
+ def collect_argument_parameters args, start_at
575
+ params = []
576
+ pos = start_at
577
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
578
+ params << args[pos]
579
+ pos += 1
580
+ end
581
+ params
582
+ end
583
+
584
+ def resolve_default_short_options
585
+ @order.each do |type, name|
586
+ next unless type == :opt
587
+ opts = @specs[name]
588
+ next if opts[:short]
589
+
590
+ c = opts[:long].split(//).find { |c| c !~ INVALID_SHORT_ARG_REGEX && !@short.member?(c) }
591
+ raise ArgumentError, "can't generate a default short option name for #{opts[:long].inspect}: out of unique characters" unless c
592
+
593
+ opts[:short] = c
594
+ @short[c] = name
595
+ end
596
+ end
597
+
598
+ def wrap_line str, opts={}
599
+ prefix = opts[:prefix] || 0
600
+ width = opts[:width] || (self.width - 1)
601
+ start = 0
602
+ ret = []
603
+ until start > str.length
604
+ nextt =
605
+ if start + width >= str.length
606
+ str.length
607
+ else
608
+ x = str.rindex(/\s/, start + width)
609
+ x = str.index(/\s/, start) if x && x < start
610
+ x || str.length
611
+ end
612
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
613
+ start = nextt + 1
614
+ end
615
+ ret
616
+ end
617
+
618
+ ## instance_eval but with ability to handle block arguments
619
+ ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
620
+ def cloaker &b
621
+ (class << self; self; end).class_eval do
622
+ define_method :cloaker_, &b
623
+ meth = instance_method :cloaker_
624
+ remove_method :cloaker_
625
+ meth
626
+ end
627
+ end
628
+ end
629
+
630
+ ## The top-level entry method into Trollop. Creates a Parser object,
631
+ ## passes the block to it, then parses +args+ with it, handling any
632
+ ## errors or requests for help or version information appropriately (and
633
+ ## then exiting). Modifies +args+ in place. Returns a hash of option
634
+ ## values.
635
+ ##
636
+ ## The block passed in should contain zero or more calls to +opt+
637
+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
638
+ ## probably a call to +version+ (Parser#version).
639
+ ##
640
+ ## The returned block contains a value for every option specified with
641
+ ## +opt+. The value will be the value given on the commandline, or the
642
+ ## default value if the option was not specified on the commandline. For
643
+ ## every option specified on the commandline, a key "<option
644
+ ## name>_given" will also be set in the hash.
645
+ ##
646
+ ## Example:
647
+ ##
648
+ ## require 'trollop'
649
+ ## opts = Trollop::options do
650
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
651
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
652
+ ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
653
+ ## opt :num_thumbs, "Number of thumbs", :type => :int # an integer --num-thumbs <i>, defaulting to nil
654
+ ## end
655
+ ##
656
+ ## ## if called with no arguments
657
+ ## p opts # => { :monkey => false, :goat => true, :num_limbs => 4, :num_thumbs => nil }
658
+ ##
659
+ ## ## if called with --monkey
660
+ ## p opts # => {:monkey_given=>true, :monkey=>true, :goat=>true, :num_limbs=>4, :help=>false, :num_thumbs=>nil}
661
+ ##
662
+ ## See more examples at http://trollop.rubyforge.org.
663
+ def options args = ARGV, *a, &b
664
+ @p = Parser.new(*a, &b)
665
+ begin
666
+ vals = @p.parse args
667
+ args.clear
668
+ @p.leftovers.each { |l| args << l }
669
+ [vals, @p]
670
+ rescue CommandlineError => e
671
+ $stderr.puts "Error: #{e.message}."
672
+ $stderr.puts "Try --help for help."
673
+ exit(-1)
674
+ rescue HelpNeeded
675
+ @p.educate
676
+ exit
677
+ rescue VersionNeeded
678
+ puts @p.version
679
+ exit
680
+ end
681
+ end
682
+
683
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
684
+ ## 'msg', and dies. Example:
685
+ ##
686
+ ## options do
687
+ ## opt :volume, :default => 0.0
688
+ ## end
689
+ ##
690
+ ## die :volume, "too loud" if opts[:volume] > 10.0
691
+ ## die :volume, "too soft" if opts[:volume] < 0.1
692
+ ##
693
+ ## In the one-argument case, simply print that message, a notice
694
+ ## about -h, and die. Example:
695
+ ##
696
+ ## options do
697
+ ## opt :whatever # ...
698
+ ## end
699
+ ##
700
+ ## Trollop::die "need at least one filename" if ARGV.empty?
701
+ def die arg, msg=nil
702
+ if msg
703
+ $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
704
+ else
705
+ $stderr.puts "Error: #{arg}."
706
+ end
707
+ $stderr.puts "Try --help for help."
708
+ exit(-1)
709
+ end
710
+
711
+ module_function :options, :die
712
+
713
+ end # module