naksh 0.2.0

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