naksh 0.2.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 (137) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +674 -0
  3. data/COPYING-DOCS +15 -0
  4. data/README.md +45 -0
  5. data/Rakefile +129 -0
  6. data/TODO +19 -0
  7. data/bin/naksh +2 -0
  8. data/data/default.mo +0 -0
  9. data/data/default.po +16 -0
  10. data/data/default2.mo +0 -0
  11. data/data/default2.po +27 -0
  12. data/data/starter.pot +29 -0
  13. data/docs/bash_flow.dia +0 -0
  14. data/docs/gtk_manual.txt +13 -0
  15. data/docs/master_plan.dia +0 -0
  16. data/docs/master_plan.png +0 -0
  17. data/docs/master_plan.svg +74 -0
  18. data/docs/master_plan.svgz +0 -0
  19. data/docs/userguide/en/about.xml +42 -0
  20. data/docs/userguide/en/gnu-fdl-1.2.xml +543 -0
  21. data/docs/userguide/en/naksh.xml +881 -0
  22. data/docs/userguide/en/naksh_start_window.png +0 -0
  23. data/docs/userguide/en/old_naksh.xml +180 -0
  24. data/docs/userguide/en/userguide-en.omf +37 -0
  25. data/lib/naksh.rb +165 -0
  26. data/lib/naksh/buffer.rb +153 -0
  27. data/lib/naksh/command_handling.rb +117 -0
  28. data/lib/naksh/commands/bash/README +2 -0
  29. data/lib/naksh/commands/bash/echo.nkc.rb +50 -0
  30. data/lib/naksh/commands/bsd.rb +25 -0
  31. data/lib/naksh/commands/dos.rb +25 -0
  32. data/lib/naksh/commands/fuse.rb +25 -0
  33. data/lib/naksh/commands/gnu.rb +30 -0
  34. data/lib/naksh/commands/gnu/README +2 -0
  35. data/lib/naksh/commands/gnu/cat.nkc.rb +50 -0
  36. data/lib/naksh/commands/gnu/cd.nkc.rb +11 -0
  37. data/lib/naksh/commands/gnu/echo.documentation.json +57 -0
  38. data/lib/naksh/commands/gnu/echo.documentation.rb +60 -0
  39. data/lib/naksh/commands/gnu/echo.nkc.rb +52 -0
  40. data/lib/naksh/commands/gnu/ls.nkc.rb +11 -0
  41. data/lib/naksh/commands/gnu/mv.nkc.rb +15 -0
  42. data/lib/naksh/commands/gnu/pwd.nkc.rb +11 -0
  43. data/lib/naksh/commands/naksh/addpath.nkc.rb +15 -0
  44. data/lib/naksh/commands/naksh/broken.nkc.rb +10 -0
  45. data/lib/naksh/commands/naksh/empty.nkc.rb +4 -0
  46. data/lib/naksh/commands/naksh/exit.nkc.rb +10 -0
  47. data/lib/naksh/commands/naksh/help.nkc.rb +21 -0
  48. data/lib/naksh/commands/naksh/man.nkc.rb +8 -0
  49. data/lib/naksh/commands/ruby.rb +31 -0
  50. data/lib/naksh/commands/unix.rb +131 -0
  51. data/lib/naksh/configuration/gconf.rb +51 -0
  52. data/lib/naksh/configuration/hash.rb +38 -0
  53. data/lib/naksh/defaults.rb +40 -0
  54. data/lib/naksh/history.rb +85 -0
  55. data/lib/naksh/interfaces.rb +59 -0
  56. data/lib/naksh/interfaces/fox.rb +49 -0
  57. data/lib/naksh/interfaces/gtk.rb +103 -0
  58. data/lib/naksh/interfaces/gtk/abtwin.rb +53 -0
  59. data/lib/naksh/interfaces/gtk/gnomeapp.rb +31 -0
  60. data/lib/naksh/interfaces/gtk/helpwin.rb +70 -0
  61. data/lib/naksh/interfaces/gtk/io.rb +94 -0
  62. data/lib/naksh/interfaces/gtk/mainwin.rb +56 -0
  63. data/lib/naksh/interfaces/gtk/mainwin/console.rb +121 -0
  64. data/lib/naksh/interfaces/gtk/mainwin/main_menu.rb +143 -0
  65. data/lib/naksh/interfaces/gtk/optwin.rb +78 -0
  66. data/lib/naksh/interfaces/tab.rb +58 -0
  67. data/lib/naksh/interfaces/wxw.rb +75 -0
  68. data/lib/naksh/interfaces/wxw/abtwin.rb +38 -0
  69. data/lib/naksh/interfaces/wxw/helpwin.rb +40 -0
  70. data/lib/naksh/interfaces/wxw/io.rb +61 -0
  71. data/lib/naksh/interfaces/wxw/mainwin.rb +42 -0
  72. data/lib/naksh/interfaces/wxw/mainwin/main_menu.rb +126 -0
  73. data/lib/naksh/interfaces/wxw/mainwin/sessions.rb +49 -0
  74. data/lib/naksh/interfaces/wxw/optwin.rb +37 -0
  75. data/lib/naksh/path_entry.rb +101 -0
  76. data/lib/naksh/regexp.rb +31 -0
  77. data/lib/naksh/session.rb +72 -0
  78. data/lib/naksh/stdlibext.rb +48 -0
  79. data/lib/naksh/syntax.rb +57 -0
  80. data/lib/naksh/syntax/bash.rb +219 -0
  81. data/lib/naksh/syntax/bash/antlr/bottomup.g +24 -0
  82. data/lib/naksh/syntax/bash/antlr/topdown.g +52 -0
  83. data/lib/naksh/syntax/bash/dhaka/evaluator.rb +353 -0
  84. data/lib/naksh/syntax/bash/dhaka/grammar.rb +71 -0
  85. data/lib/naksh/syntax/bash/dhaka/lexer.rb +52 -0
  86. data/lib/naksh/syntax/bash/treetop/bash.treetop +17 -0
  87. data/lib/old/abbr_call.rb +39 -0
  88. data/lib/old/autocomplete.rb +29 -0
  89. data/lib/old/command.rb +106 -0
  90. data/lib/old/command_holder.rb +75 -0
  91. data/lib/old/commands.rb +24 -0
  92. data/lib/old/old_executor.rb +95 -0
  93. data/lib/old/options.rb +31 -0
  94. data/lib/old/rush.rb +113 -0
  95. data/lib/old/sortissimo.rb +205 -0
  96. data/lib/old/systems.rb +25 -0
  97. data/lib/old/systems/ruby.rb +26 -0
  98. data/lib/old/un.rb +240 -0
  99. data/lib/rust/Rakefile +6 -0
  100. data/lib/rust/commands/builtins.rb +46 -0
  101. data/lib/rust/commands/builtins/cd.rb +17 -0
  102. data/lib/rust/commands/builtins/exit.rb +13 -0
  103. data/lib/rust/commands/builtins/help.rb +14 -0
  104. data/lib/rust/commands/builtins/parser.rb +13 -0
  105. data/lib/rust/commands/builtins/pwd.rb +13 -0
  106. data/lib/rust/commands/builtins/type.rb +13 -0
  107. data/lib/rust/commands/commands/ls.rb +13 -0
  108. data/lib/rust/commands/commands/read.rb +13 -0
  109. data/lib/rust/commands/commands/rm.rb +14 -0
  110. data/lib/rust/commands/commands/test.rb +20 -0
  111. data/lib/rust/helpers/array.rb +10 -0
  112. data/lib/rust/helpers/command_center.rb +78 -0
  113. data/lib/rust/helpers/constants.rb +58 -0
  114. data/lib/rust/helpers/io.rb +132 -0
  115. data/lib/rust/helpers/parser.rb +45 -0
  116. data/lib/rust/helpers/rush_control.rb +40 -0
  117. data/lib/rust/helpers/string.rb +50 -0
  118. data/lib/rust/helpers/trollop.rb +475 -0
  119. data/lib/rust/parsers/bash.rb +220 -0
  120. data/lib/rust/parsers/bash/stdlibext.rb +32 -0
  121. data/lib/rust/parsers/ren.rb +57 -0
  122. data/lib/rust/rust.rb +75 -0
  123. data/lib/rust/syntax/command.rb +23 -0
  124. data/lib/rust/syntax/paths.rb +31 -0
  125. data/lib/rust/syntax/pipes.rb +148 -0
  126. data/naksh.gemspec +55 -0
  127. data/test/naksh/arg_parser.rspec.rb +27 -0
  128. data/test/naksh/commands/bash/echo.rspec.rb +32 -0
  129. data/test/naksh/commands/sortlist +127 -0
  130. data/test/naksh/external_command.rspec.rb +46 -0
  131. data/test/naksh/send_error.rspec.rb +60 -0
  132. data/test/naksh/suggest.rspec.rb +38 -0
  133. data/test/naksh/syntax/bash.rspec.rb +69 -0
  134. data/test/naksh/syntax/bash/execute.rspec.rb +51 -0
  135. data/test/naksh/syntax/rspec.rb +63 -0
  136. data/test/run_rspecs.rb +20 -0
  137. metadata +217 -0
@@ -0,0 +1,45 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2011-12-23.
4
+ # Copyright (c) 2011. All pwnage reserved.
5
+
6
+ module Parser
7
+
8
+ def run(*arr)
9
+ parts, styles = arr[0..-2], arr.last
10
+ res = []
11
+ parts.each_with_index do |part, i|
12
+ cmd = part[0]
13
+ args = part[1]
14
+ res << [cmd, args.join(' ')].join(' ').strip
15
+ end
16
+ run_standard(res, styles)
17
+ end
18
+
19
+ def run_standard(parts, styles)
20
+ # Please note that this will
21
+ # work for even single commands!
22
+ # :stding is STDIN and :stdout is STDOUT
23
+
24
+ # this is the actual pipage
25
+ inp = STDIN
26
+ res = []
27
+ output = nil
28
+ parts.each_with_index do |part, i|
29
+ if builtins.include? part.split(' ').shift.to_sym
30
+ ::CommandCenter.run_as_internal(part)
31
+ else
32
+ # pipe_to returns the pid, in, out, and err
33
+ pipes = ::Pipes.pipe_to(part, :stdin => inp, :style => styles[i])
34
+ inp = pipes[2]
35
+ res.concat pipes[1..-1]
36
+ end
37
+ end
38
+ output = res[-2].read if res[-2] # only pipes get put here, so need to check for that
39
+ # close them all off
40
+ res.each {|pipe| pipe.close unless pipe.closed? }
41
+
42
+ output.to_s# + "\n"
43
+ end
44
+
45
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2011-12-12.
4
+ # Copyright (c) 2011. All pwnage reserved.
5
+
6
+ ##################
7
+ # Here are the .rustrc command things
8
+ # its a little bit rusty, I know...
9
+ ##################
10
+ module CommandCenter
11
+ class << self
12
+ $keys ||= {}
13
+
14
+ #######
15
+ public
16
+ #######
17
+
18
+ def keys
19
+ $keys
20
+ end
21
+
22
+ def capture(keystroke, &b)
23
+ $keys ||= {}
24
+ if keystroke =~ /C-(\w)/
25
+ key_trap = $1
26
+ end
27
+ $keys[key_trap.unpack('c')[0] - 96] = b
28
+ end
29
+
30
+ def add_to_buffer(text)
31
+ Rust.io.line += text
32
+ Rust.io.backspace_limit += text.length
33
+ print text
34
+ end
35
+
36
+ $prompt = "[] "
37
+ $path = ['/usr/local/bin', '/bin', '/usr/bin']
38
+ $parser = ::Ren # ::Naksh::Syntax::Bash
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2011-12-07.
4
+ # Copyright (c) 2011. All pwnage reserved.
5
+
6
+ #require File.join(File.dirname(__FILE__), "../syntax/pipes.rb")
7
+
8
+ class String
9
+
10
+ # takes "command1 | command2" and makes it
11
+ # rubyish, like Command.new(command2) | Command.new(command2)
12
+ # OBSOLETE
13
+ def repipe!
14
+ commands = self.split('|')
15
+ commands.map! {|com| "Command.new('#{com.strip.sub(' ', '\', \'')}')"}
16
+ self.replace(commands.reverse.join(' | ').strip)
17
+ end
18
+
19
+ # OBSOLETE
20
+ def to_commands!
21
+ splits = self.split(' ')
22
+ self.replace "Command.new(#{splits[0]}, #{splits[1..-1]})"
23
+ end
24
+
25
+
26
+
27
+ end
28
+
29
+ # "ls" | "wc"
30
+ =begin
31
+ Pipes.pipe_to(self.to_s) do |p, i, o, e|
32
+ Pipes.pipe_to(other.to_s, :stdin => o) {|pid, inp, out, err|
33
+ Pipes.pipe_to(third.to_s, :stdin => inp) {|pid2, inp2, out2, err2| puts out2.read }
34
+ }
35
+ end
36
+
37
+
38
+
39
+
40
+ def |(other)
41
+ (self =~ /(.+)\s\|\s(.+)/ ? $1 | $2 : self) + ' -> ' + other
42
+ end
43
+
44
+ def |(others, stdin=STDIN)
45
+ ::Pipes.pipe_to(self.to_s) do |p, i, o, e|
46
+ ::Pipes.pipe_to(others.to_s, :stdin => o) {|pid, inp, out, err| puts out.read }
47
+ end
48
+ end
49
+ public :|
50
+ =end
@@ -0,0 +1,475 @@
1
+ ## lib/trollop.rb -- trollop command-line processing library
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2011 William Morgan
4
+ ## License:: GNU GPL version 2
5
+
6
+ module Trollop
7
+
8
+ VERSION = "1.7"
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 which case only
30
+ ## the methods #opt, #banner and #version will be called.
31
+ class Parser
32
+ ## The set of values specifiable as the :type parameter to #opt.
33
+ TYPES = [:flag, :boolean, :bool, :int, :integer, :string, :double, :float]
34
+
35
+ ## The values from the commandline that were not interpreted by #parse.
36
+ attr_reader :leftovers
37
+
38
+ ## The complete configuration hashes for each option. (Mainly useful
39
+ ## for testing.)
40
+ attr_reader :specs
41
+
42
+ ## Initializes the parser, and instance-evaluates any block given.
43
+ def initialize *a, &b
44
+ @version = nil
45
+ @leftovers = []
46
+ @specs = {}
47
+ @long = {}
48
+ @short = {}
49
+ @order = []
50
+ @constraints = []
51
+
52
+ #instance_eval(&b) if b # can't take arguments
53
+ cloaker(&b).bind(self).call(*a) if b
54
+ end
55
+
56
+ ## Add an option. 'name' is the argument name, a unique identifier
57
+ ## for the option that you will use internally. 'desc' a string
58
+ ## description which will be displayed in help messages. Takes the
59
+ ## following optional arguments:
60
+ ##
61
+ ## * :long: Specify the long form of the argument, i.e. the form
62
+ ## with two dashes. If unspecified, will be automatically derived
63
+ ## based on the argument name.
64
+ ## * :short: Specify the short form of the argument, i.e. the form
65
+ ## with one dash. If unspecified, will be automatically derived
66
+ ## based on the argument name.
67
+ ## * :type: Require that the argument take a parameter of type
68
+ ## 'type'. Can by any member of the TYPES constant or a
69
+ ## corresponding class (e.g. Integer for :int). If unset, the
70
+ ## default argument type is :flag, meaning the argument does not
71
+ ## take a parameter. Not necessary if :default: is specified.
72
+ ## * :default: Set the default value for an argument. Without a
73
+ ## default value, the hash returned by #parse (and thus
74
+ ## Trollop#options) will not contain the argument unless it is
75
+ ## given on the commandline. The argument type is derived
76
+ ## automatically from the class of the default value given, if
77
+ ## any. Specifying a :flag argument on the commandline whose
78
+ ## default value is true will change its value to false.
79
+ ## * :required: if set to true, the argument must be provided on the
80
+ ## commandline.
81
+ def opt name, desc="", opts={}
82
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
83
+
84
+ ## fill in :type
85
+ opts[:type] =
86
+ case opts[:type]
87
+ when :flag, :boolean, :bool: :flag
88
+ when :int, :integer: :int
89
+ when :string: :string
90
+ when :double, :float: :float
91
+ when Class
92
+ case opts[:type].to_s # sigh... there must be a better way to do this
93
+ when 'TrueClass', 'FalseClass': :flag
94
+ when 'String': :string
95
+ when 'Integer': :int
96
+ when 'Float': :float
97
+ else
98
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
99
+ end
100
+ when nil: nil
101
+ else
102
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
103
+ end
104
+
105
+ type_from_default =
106
+ case opts[:default]
107
+ when Integer: :int
108
+ when Numeric: :float
109
+ when TrueClass, FalseClass: :flag
110
+ when String: :string
111
+ when nil: nil
112
+ else
113
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
114
+ end
115
+
116
+ raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
117
+
118
+ opts[:type] = (opts[:type] || type_from_default || :flag)
119
+
120
+ ## fill in :long
121
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
122
+ opts[:long] =
123
+ case opts[:long]
124
+ when /^--([^-].*)$/
125
+ $1
126
+ when /^[^-]/
127
+ opts[:long]
128
+ else
129
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
130
+ end
131
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
132
+
133
+ ## fill in :short
134
+ opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
135
+ opts[:short] =
136
+ case opts[:short]
137
+ when nil
138
+ c = opts[:long].split(//).find { |c| c !~ /[\d]/ && !@short.member?(c) }
139
+ raise ArgumentError, "can't generate a short option name for #{opts[:long].inspect}: out of unique characters" unless c
140
+ c
141
+ when /^-(.)$/
142
+ $1
143
+ when /^.$/
144
+ opts[:short]
145
+ when :none
146
+ nil
147
+ else
148
+ raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
149
+ end
150
+ if opts[:short]
151
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
152
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ /[\d-]/
153
+ end
154
+
155
+ ## fill in :default for flags
156
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
157
+
158
+ opts[:desc] ||= desc
159
+ @long[opts[:long]] = name
160
+ @short[opts[:short]] = name if opts[:short]
161
+ @specs[name] = opts
162
+ @order << [:opt, name]
163
+ end
164
+
165
+ ## Sets the version string. If set, the user can request the version
166
+ ## on the commandline. Should be of the form "<program name>
167
+ ## <version number>".
168
+ def version s=nil; @version = s if s; @version end
169
+
170
+ ## Adds text to the help display.
171
+ def banner s; @order << [:text, s] end
172
+ alias :text :banner
173
+
174
+ ## Marks two (or more!) options as requiring each other. Only
175
+ ## handles undirected dependcies. Directed dependencies are better
176
+ ## modeled with #die.
177
+ def depends *syms
178
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
179
+ @constraints << [:depends, syms]
180
+ end
181
+
182
+ ## Marks two (or more!) options as conflicting.
183
+ def conflicts *syms
184
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
185
+ @constraints << [:conflicts, syms]
186
+ end
187
+
188
+ ## yield successive arg, parameter pairs
189
+ def each_arg args # :nodoc:
190
+ remains = []
191
+ i = 0
192
+
193
+ until i >= args.length
194
+ case args[i]
195
+ when /^--$/ # arg terminator
196
+ remains += args[(i + 1) .. -1]
197
+ break
198
+ when /^--(\S+?)=(\S+)$/ # long argument with equals
199
+ yield "--#{$1}", $2
200
+ i += 1
201
+ when /^--(\S+)$/ # long argument
202
+ if args[i + 1] && args[i + 1] !~ PARAM_RE
203
+ remains << args[i + 1] unless yield args[i], args[i + 1]
204
+ i += 2
205
+ else # long argument no parameter
206
+ yield args[i], nil
207
+ i += 1
208
+ end
209
+ when /^-(\S+)$/ # one or more short arguments
210
+ shortargs = $1.split(//)
211
+ shortargs.each_with_index do |a, j|
212
+ if j == (shortargs.length - 1) && args[i + 1] && args[i + 1] !~ PARAM_RE
213
+ remains << args[i + 1] unless yield "-#{a}", args[i + 1]
214
+ i += 1 # once more below
215
+ else
216
+ yield "-#{a}", nil
217
+ end
218
+ end
219
+ i += 1
220
+ else
221
+ remains << args[i]
222
+ i += 1
223
+ end
224
+ end
225
+ remains
226
+ end
227
+
228
+ def parse cmdline #:nodoc:
229
+ vals = {}
230
+ required = {}
231
+ found = {}
232
+
233
+ opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
234
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
235
+
236
+ @specs.each do |sym, opts|
237
+ required[sym] = true if opts[:required]
238
+ vals[sym] = opts[:default]
239
+ end
240
+
241
+ ## resolve symbols
242
+ args = []
243
+ @leftovers = each_arg cmdline do |arg, param|
244
+ sym =
245
+ case arg
246
+ when /^-([^-])$/
247
+ @short[$1]
248
+ when /^--([^-]\S*)$/
249
+ @long[$1]
250
+ else
251
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
252
+ end
253
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
254
+ raise CommandlineError, "option '#{arg}' specified multiple times" if found[sym]
255
+ args << [sym, arg, param]
256
+ found[sym] = true
257
+
258
+ @specs[sym][:type] != :flag # take params on all except flags
259
+ end
260
+
261
+ ## check for version and help args
262
+ raise VersionNeeded if args.any? { |sym, *a| sym == :version }
263
+ raise HelpNeeded if args.any? { |sym, *a| sym == :help }
264
+
265
+ ## check constraint satisfaction
266
+ @constraints.each do |type, syms|
267
+ constraint_sym = syms.find { |sym| found[sym] }
268
+ next unless constraint_sym
269
+
270
+ case type
271
+ when :depends
272
+ syms.each { |sym| raise CommandlineError, "--#{@long[constraint_sym]} requires --#{@long[sym]}" unless found[sym] }
273
+ when :conflicts
274
+ syms.each { |sym| raise CommandlineError, "--#{@long[constraint_sym]} conflicts with --#{@long[sym]}" if found[sym] && sym != constraint_sym }
275
+ end
276
+ end
277
+
278
+ required.each do |sym, val|
279
+ raise CommandlineError, "option '#{sym}' must be specified" unless found[sym]
280
+ end
281
+
282
+ ## parse parameters
283
+ args.each do |sym, arg, param|
284
+ opts = @specs[sym]
285
+
286
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param || opts[:type] == :flag
287
+
288
+ case opts[:type]
289
+ when :flag
290
+ vals[sym] = !opts[:default]
291
+ when :int
292
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
293
+ vals[sym] = param.to_i
294
+ when :float
295
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
296
+ vals[sym] = param.to_f
297
+ when :string
298
+ vals[sym] = param.to_s
299
+ end
300
+ end
301
+
302
+ vals
303
+ end
304
+
305
+ def width #:nodoc:
306
+ @width ||=
307
+ if $stdout.tty?
308
+ begin
309
+ require 'curses'
310
+ Curses::init_screen
311
+ x = Curses::cols
312
+ Curses::close_screen
313
+ x
314
+ rescue Exception
315
+ 80
316
+ end
317
+ else
318
+ 80
319
+ end
320
+ end
321
+
322
+ ## Print the help message to 'stream'.
323
+ def educate stream=$stdout
324
+ width # just calculate it now; otherwise we have to be careful not to
325
+ # call this unless the cursor's at the beginning of a line.
326
+
327
+ left = {}
328
+ @specs.each do |name, spec|
329
+ left[name] = "--#{spec[:long]}" +
330
+ (spec[:short] ? ", -#{spec[:short]}" : "") +
331
+ case spec[:type]
332
+ when :flag
333
+ ""
334
+ when :int
335
+ " <i>"
336
+ when :string
337
+ " <s>"
338
+ when :float
339
+ " <f>"
340
+ end
341
+ end
342
+
343
+ leftcol_width = left.values.map { |s| s.length }.max || 0
344
+ rightcol_start = leftcol_width + 6 # spaces
345
+
346
+ unless @order.size > 0 && @order.first.first == :text
347
+ stream.puts "#@version\n" if @version
348
+ stream.puts "Options:"
349
+ end
350
+
351
+ @order.each do |what, opt|
352
+ if what == :text
353
+ stream.puts wrap(opt)
354
+ next
355
+ end
356
+
357
+ spec = @specs[opt]
358
+ stream.printf " %#{leftcol_width}s: ", left[opt]
359
+ desc = spec[:desc] +
360
+ if spec[:default]
361
+ if spec[:desc] =~ /\.$/
362
+ " (Default: #{spec[:default]})"
363
+ else
364
+ " (default: #{spec[:default]})"
365
+ end
366
+ else
367
+ ""
368
+ end
369
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
370
+ end
371
+ end
372
+
373
+ def wrap_line str, opts={} # :nodoc:
374
+ prefix = opts[:prefix] || 0
375
+ width = opts[:width] || (self.width - 1)
376
+ start = 0
377
+ ret = []
378
+ until start > str.length
379
+ nextt =
380
+ if start + width >= str.length
381
+ str.length
382
+ else
383
+ x = str.rindex(/\s/, start + width)
384
+ x = str.index(/\s/, start) if x && x < start
385
+ x || str.length
386
+ end
387
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
388
+ start = nextt + 1
389
+ end
390
+ ret
391
+ end
392
+
393
+ def wrap str, opts={} # :nodoc:
394
+ if str == ""
395
+ [""]
396
+ else
397
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
398
+ end
399
+ end
400
+
401
+ ## instance_eval but with ability to handle block arguments
402
+ ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
403
+ def cloaker &b #:nodoc:
404
+ (class << self; self; end).class_eval do
405
+ define_method :cloaker_, &b
406
+ meth = instance_method :cloaker_
407
+ remove_method :cloaker_
408
+ meth
409
+ end
410
+ end
411
+ end
412
+
413
+ ## The top-level entry method into Trollop. Creates a Parser object,
414
+ ## passes the block to it, then parses ARGV with it, handling any
415
+ ## errors or requests for help or version information appropriately
416
+ ## (and then exiting). Modifies ARGV in place. Returns a hash of
417
+ ## option values.
418
+ ##
419
+ ## The block passed in should contain one or more calls to #opt
420
+ ## (Parser#opt), one or more calls to text (Parser#text), and
421
+ ## probably a call to version (Parser#version).
422
+ ##
423
+ ## See the synopsis in README.txt for examples.
424
+ def options *a, &b
425
+ arr = a.shift
426
+ @p = Parser.new(*a, &b)
427
+ begin
428
+ vals = @p.parse arr
429
+ arr.clear
430
+ @p.leftovers.each { |l| arr << l }
431
+ vals
432
+ rescue CommandlineError => e
433
+ $stderr.puts "Error: #{e.message}."
434
+ $stderr.puts "Try --help for help."
435
+ exit(-1)
436
+ rescue HelpNeeded
437
+ @p.educate
438
+ exit
439
+ rescue VersionNeeded
440
+ puts @p.version
441
+ exit
442
+ end
443
+ end
444
+
445
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
446
+ ## 'msg', and dies. Example:
447
+ ##
448
+ ## options do
449
+ ## opt :volume, :default => 0.0
450
+ ## end
451
+ ##
452
+ ## die :volume, "too loud" if opts[:volume] > 10.0
453
+ ## die :volume, "too soft" if opts[:volume] < 0.1
454
+ ##
455
+ ## In the one-argument case, simply print that message, a notice
456
+ ## about -h, and die. Example:
457
+ ##
458
+ ## options do
459
+ ## opt :whatever # ...
460
+ ## end
461
+ ##
462
+ ## Trollop::die "need at least one filename" if ARGV.empty?
463
+ def die arg, msg=nil
464
+ if msg
465
+ $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
466
+ else
467
+ $stderr.puts "Error: #{arg}."
468
+ end
469
+ $stderr.puts "Try --help for help."
470
+ exit(-1)
471
+ end
472
+
473
+ module_function :options, :die
474
+
475
+ end # module