amp 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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