appl 1.6.2 → 1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +4 -4
  2. data/doc/demoappl +3 -3
  3. data/doc/demofan +90 -0
  4. data/lib/appl.rb +62 -72
  5. data/lib/applfan.rb +74 -0
  6. metadata +9 -10
  7. data/bin/intar +0 -147
  8. data/lib/intar.rb +0 -447
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3006d9d18efb0699ffb9cce15b9f5ebad26dbe0d62e723b982ef010f0667d2fd
4
- data.tar.gz: f13bdd180f26807607895846f19130a3e927b1cbb97bdc54beab2fc6f2d62762
3
+ metadata.gz: 9e3306b330c4d69ef88e6b75511d7b0ab1e030f990aac1800305dcfa4afd0f65
4
+ data.tar.gz: eff477635d40b6ff9c443b0fe1256107c85d31bf6aa7c0a2088f26ffa239111c
5
5
  SHA512:
6
- metadata.gz: 76f086e3cacbd25034ce77d242c0eae9d1b8c161f02a7cce354796f9d344751ce9425613357e945f9ef50e854ff7bbd84f8255b111cdecd21acc53b0be0b214c
7
- data.tar.gz: 701155765e67cf6519b5cb91a8b528e65b14f2175bc6dd53dbb76d232c7a6c41bfa8a0e7467eb2bbed373fbde3ca9437e65f73a763658ba430487565e31d9e87
6
+ metadata.gz: 6ae84d8de622e2e2b4763ef8fbda40b9210edd326d48358707364f4a4c99b0fbee05c1ed1dc5b31d83d3f7429e74922a81af5eb22de7572a1a3c6ad7bec99925
7
+ data.tar.gz: ab2ea5f6b9f7850bd62e741201cc10bdaa1c1c6be549c0a45fa435bb7ee55779fabfade33a0801fd931d7313d0b90f3a991b1ec38ee6749db19562ee6969afe2
data/doc/demoappl CHANGED
@@ -46,9 +46,9 @@ class DemoAppl < Application
46
46
  define_option "V", :version, "show version information"
47
47
  alias_option "V", "version"
48
48
 
49
- UNKNOWN = "Sorry, unknown option"
50
- STOPOPT = "no more options"
51
- UNPROCA = "Warning: unprocessed arguments left"
49
+ STOPOPT = "Stop option processing"
50
+ UNKNOWN = "Not a valid option: `%s'."
51
+ UNPROCA = "Warning. Unprocessed arguments: %s"
52
52
 
53
53
  def run
54
54
  puts inspect
data/doc/demofan ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # demofan -- Test the Application class with commands
5
+ #
6
+
7
+ require "applfan"
8
+
9
+
10
+ class DemoFan < ApplicationFan
11
+
12
+ NAME = "demofan"
13
+ VERSION = "1.0"
14
+ SUMMARY = "Test the Application class with commands"
15
+ COPYRIGHT = "(C) 2020 Bertram Scharpf"
16
+ LICENSE = "For internal use only"
17
+ AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
18
+
19
+ DESCRIPTION = <<~EOT
20
+ This is an example how to define an Application subclass
21
+ with commands inside.
22
+ EOT
23
+
24
+ attr_bang :verbose
25
+ define_option "v", :verbose!, "verbose mode"
26
+
27
+ define_option "h", :help, "show this options list"
28
+ alias_option "h", "help"
29
+ define_option "V", :version, "show version information"
30
+ alias_option "V", "version"
31
+
32
+ def run
33
+ super do |cmd|
34
+ cmd.verbose! if @verbose
35
+ end
36
+ end
37
+
38
+ class Init < Command
39
+ NAME = "init"
40
+ SUMMARY = "Initialize"
41
+ DESCRIPTION = <<~EOT
42
+ Initialization of nothing.
43
+ EOT
44
+ define_option "h", :help, "show init's options list"
45
+ alias_option "h", "help"
46
+ def verbose! ; end
47
+ def run
48
+ puts "Initializing...done."
49
+ end
50
+ end
51
+
52
+ class Run < Command
53
+ NAME = "run"
54
+ SUMMARY = "Run a job"
55
+ DESCRIPTION = <<~EOT
56
+ Running nothing.
57
+ EOT
58
+ define_option "h", :help, "show run's options list"
59
+ alias_option "h", "help"
60
+ def verbose! ; @verbose = true ; end
61
+ def run
62
+ if @verbose then
63
+ puts "Starting..."
64
+ puts "Running..."
65
+ puts "Done."
66
+ else
67
+ puts "Running...done."
68
+ end
69
+ end
70
+ end
71
+
72
+ class Cleanup < Command
73
+ NAME = "cleanup"
74
+ SUMMARY = "Cleanup"
75
+ DESCRIPTION = <<~EOT
76
+ Cleanup of nothing.
77
+ EOT
78
+ define_option "h", :help, "show cleanup's options list"
79
+ alias_option "h", "help"
80
+ def verbose! ; end
81
+ def run
82
+ puts "Cleaning up...done."
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+
89
+ DemoFan.run
90
+
data/lib/appl.rb CHANGED
@@ -5,19 +5,24 @@
5
5
 
6
6
  class Application
7
7
 
8
- APPL_VERSION = "1.6.2".freeze
8
+ APPL_VERSION = "1.13".freeze
9
9
 
10
10
  OPTIONS_ENV = nil
11
11
 
12
12
  STOPOPT = "stop option processing"
13
- UNKNOWN = "Unknown option"
14
- UNPROCA = "Warning: unprocessed arguments"
13
+ UNKNOWN = "Unknown option: `%s'."
14
+ UNPROCA = "Warning: unprocessed arguments: %s"
15
+
16
+ ALIASES = [].freeze
17
+
18
+ W_OPTS = 10
19
+ W_ARGS = 16
15
20
 
16
21
  class OptionError < StandardError ; end
17
22
  class Done < Exception ; end
18
23
 
19
- def initialize args = nil
20
- @args = args||self.class.cmdline_arguments
24
+ def initialize name = nil, args = nil
25
+ @name, @args = name, args
21
26
  self.class.each_option { |opt,desc,arg,dfl,act|
22
27
  begin
23
28
  send act, dfl if dfl
@@ -46,41 +51,29 @@ class Application
46
51
  end
47
52
 
48
53
  def help
49
- c = self.class
50
- puts c.version
51
- puts
52
- puts c::DESCRIPTION
53
- puts
54
- c.show_options
55
- if block_given? then
56
- puts
57
- yield
58
- end
54
+ self.class.help
59
55
  raise Done
60
56
  end
61
57
 
62
58
  def version
63
- self.class.show_version
59
+ self.class.version
64
60
  raise Done
65
61
  end
66
62
 
67
- VERSION = "0"
68
- NAME = "appl"
69
- SUMMARY = "Dummy application"
70
- DESCRIPTION = <<~EOT
71
- This base class does nothing by default.
72
- EOT
73
-
74
63
  def run
75
64
  end
76
65
 
77
- def execute
78
- run
66
+ def warn_unprocessed
79
67
  if @args.any? then
80
- u = @args.join " "
81
- puts "#{self.class::UNPROCA}: #{u}"
68
+ $stderr.puts self.class.root::UNPROCA % (@args.join " ")
82
69
  end
83
- 0
70
+ end
71
+
72
+ def execute
73
+ r = run
74
+ r = 0 unless Integer === r
75
+ warn_unprocessed
76
+ r
84
77
  rescue SignalException
85
78
  raise if @debug
86
79
  self.class.show_message $!.inspect
@@ -98,18 +91,11 @@ class Application
98
91
  exit e
99
92
  end
100
93
 
101
- def version
102
- if self::VERSION =~ %r/\s/ then
103
- self::VERSION
104
- else
105
- "#{self::NAME} #{self::VERSION} -- #{self::SUMMARY}"
106
- end
107
- end
108
-
109
94
  private
110
95
 
111
96
  def execute args = nil
112
- i = new args
97
+ n = File.basename $0
98
+ i = new n, args||cmdline_arguments
113
99
  i.execute
114
100
  rescue Done
115
101
  0
@@ -119,7 +105,9 @@ class Application
119
105
  end
120
106
 
121
107
  def inherited sub
122
- sub.instance_eval { @options, @aliases = {}, {} }
108
+ sub.name or return
109
+ o, a = @options.dup.to_h, @aliases.dup.to_h
110
+ sub.instance_eval { @options, @aliases = o, a }
123
111
  end
124
112
 
125
113
  def attr_bang *syms
@@ -162,16 +150,12 @@ class Application
162
150
  end
163
151
 
164
152
  def delete_option opt
165
- self < Application or return
166
- superclass.delete_option opt
167
- @options.delete opt
168
153
  @aliases.reject! { |k,v| v == opt }
154
+ @options.delete opt
169
155
  nil
170
156
  end
171
157
 
172
158
  def unalias_option opt
173
- self < Application or return
174
- superclass.unalias_option opt
175
159
  @aliases.delete opt
176
160
  nil
177
161
  end
@@ -179,32 +163,12 @@ class Application
179
163
  protected
180
164
 
181
165
  def find_option_act opt
182
- self < Application or return
183
- @options[ opt] || @options[ @aliases[ opt]] ||
184
- (superclass.find_option_act opt)
185
- end
186
-
187
- def all_options
188
- if self < Application then
189
- r = superclass.all_options
190
- r.update @options
191
- else
192
- {}
193
- end
194
- end
195
-
196
- def all_aliases
197
- if self < Application then
198
- r = superclass.all_aliases
199
- r.update @aliases
200
- else
201
- {}
202
- end
166
+ @options[ opt] || @options[ @aliases[ opt]]
203
167
  end
204
168
 
205
169
  def options_desc &block
206
170
  a = Hash.new do |h,k| h[ k] = [] end
207
- all_aliases.each { |k,v|
171
+ @aliases.each { |k,v|
208
172
  a[ v].push k
209
173
  }
210
174
  each_option { |opt,desc,arg,dfl,|
@@ -213,14 +177,13 @@ class Application
213
177
  yield l, nil, nil, nil
214
178
  }
215
179
  }
216
- yield "", nil, nil, self::STOPOPT
180
+ yield "", nil, nil, root::STOPOPT
217
181
  end
218
182
 
219
183
  public
220
184
 
221
185
  def each_option
222
- o = all_options
223
- o.each { |opt,(desc,arg,dfl,act)|
186
+ @options.each { |opt,(desc,arg,dfl,act)|
224
187
  case dfl
225
188
  when Symbol then dfl = const_get dfl
226
189
  end
@@ -230,7 +193,7 @@ class Application
230
193
 
231
194
  def option_act args, opt, rest
232
195
  dada = find_option_act opt
233
- dada or raise OptionError, "#{self::UNKNOWN}: `#{opt}'."
196
+ dada or raise OptionError, root::UNKNOWN % opt
234
197
  desc, arg, dfl, act = *dada
235
198
  r = [ act]
236
199
  if arg then
@@ -246,12 +209,39 @@ class Application
246
209
  arg &&= "#{arg}"
247
210
  dfl &&= "[#{dfl}]"
248
211
  arg << dfl if arg && dfl
249
- puts " %-10s %-12s %s" % [ opt, arg, desc]
212
+ puts " %-*s %-*s %s" % [ root::W_OPTS, opt, root::W_ARGS, arg, desc]
250
213
  end
251
214
  end
252
215
 
253
- def show_version
254
- puts version
216
+ def root
217
+ self
218
+ end
219
+
220
+ def all_names
221
+ [ self::NAME, *self::ALIASES].join "|"
222
+ end
223
+
224
+ def help
225
+ n = []
226
+ s = self
227
+ begin
228
+ l = s.all_names
229
+ n.unshift l
230
+ s = s.root
231
+ end while s != root
232
+ puts "#{n.join ' '} -- #{self::SUMMARY}"
233
+ puts
234
+ puts self::DESCRIPTION
235
+ puts
236
+ show_options
237
+ if block_given? then
238
+ puts
239
+ yield
240
+ end
241
+ end
242
+
243
+ def version
244
+ puts "#{self::NAME} #{self::VERSION} -- #{self::SUMMARY}"
255
245
  puts self::COPYRIGHT if const_defined? :COPYRIGHT
256
246
  puts "License: #{self::LICENSE}" if const_defined? :LICENSE
257
247
  a = []
data/lib/applfan.rb ADDED
@@ -0,0 +1,74 @@
1
+ #
2
+ # lib/applfan.rb -- ApplicationFan class
3
+ #
4
+
5
+ require "appl"
6
+
7
+
8
+ class ApplicationFan < Application
9
+
10
+ AVAILCMDS = "Available commands (say -h after one for help)"
11
+ NOCOMMAND = "No command given. Say -h for a list."
12
+ UNKNWNCMD = "Unknown command: `%s'. Say -h for a list."
13
+
14
+ W_CMDS = 16
15
+
16
+ class CommandError < StandardError ; end
17
+
18
+ class <<self
19
+
20
+ attr_accessor :commands
21
+
22
+ def find_command name
23
+ @commands.find { |c| c::NAME == name or c::ALIASES.include? name }
24
+ end
25
+
26
+
27
+ def help
28
+ super do
29
+ if block_given? then
30
+ yield
31
+ puts
32
+ end
33
+ puts self::AVAILCMDS
34
+ puts
35
+ @commands.each { |c|
36
+ puts " %-*s %s" % [ self::W_CMDS, c.all_names, c::SUMMARY]
37
+ }
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def inherited sub
44
+ sub.instance_eval do
45
+ @commands = []
46
+ const_set :Command, (
47
+ Class.new Application do
48
+ define_singleton_method :inherited do |c|
49
+ sub.commands.push c
50
+ super c
51
+ end
52
+ define_singleton_method :root do
53
+ sub.root
54
+ end
55
+ end
56
+ )
57
+ end
58
+ super
59
+ end
60
+
61
+ end
62
+
63
+ def run
64
+ c = @args.shift
65
+ c or raise CommandError, self.class::NOCOMMAND
66
+ cmd = self.class.find_command c
67
+ cmd or raise CommandError, self.class::UNKNWNCMD % c
68
+ sub = cmd.new c, (@args.slice! 0, @args.length)
69
+ yield sub if block_given?
70
+ sub.execute
71
+ end
72
+
73
+ end
74
+
metadata CHANGED
@@ -1,33 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: '1.13'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bertram Scharpf
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-31 00:00:00.000000000 Z
11
+ date: 2021-04-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  A base class for command line applications doing options parsing
15
15
  and generating exit codes.
16
16
  email: "<software@bertram-scharpf.de>"
17
- executables:
18
- - intar
17
+ executables: []
19
18
  extensions: []
20
19
  extra_rdoc_files: []
21
20
  files:
22
- - bin/intar
23
21
  - doc/demoappl
22
+ - doc/demofan
24
23
  - lib/appl.rb
25
- - lib/intar.rb
24
+ - lib/applfan.rb
26
25
  homepage: http://www.bertram-scharpf.de/software/appl
27
26
  licenses:
28
27
  - BSD-2-Clause
29
28
  metadata: {}
30
- post_install_message:
29
+ post_install_message:
31
30
  rdoc_options: []
32
31
  require_paths:
33
32
  - lib
@@ -43,8 +42,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
42
  version: '0'
44
43
  requirements:
45
44
  - Just Ruby
46
- rubygems_version: 3.0.6
47
- signing_key:
45
+ rubygems_version: 3.0.8
46
+ signing_key:
48
47
  specification_version: 4
49
48
  summary: Easy option parsing
50
49
  test_files: []
data/bin/intar DELETED
@@ -1,147 +0,0 @@
1
- #!/usr/local/bin/ruby
2
-
3
- #
4
- # intar -- Interactive Ruby evaluation
5
- #
6
-
7
- require "intar"
8
- require "appl"
9
-
10
-
11
- class IntarApp < Application
12
-
13
- NAME = "intar"
14
- VERSION = APPL_VERSION
15
- SUMMARY = "Interactive Ruby"
16
- COPYRIGHT = "(C) 2008-2016 Bertram Scharpf <software@bertram-scharpf.de>"
17
- LICENSE = "BSD"
18
- AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
19
-
20
- DESCRIPTION = <<~EOT
21
- Prompt for Ruby statements, evaluate them. This is a replacement
22
- for "irb". The underlying library may be entered from inside any
23
- Ruby program.
24
-
25
- Example:
26
-
27
- $ intar -p '%(33 1)c%t%c%> '
28
-
29
- EOT
30
-
31
- def quiet!
32
- Intar.show = nil
33
- end
34
- def show= s
35
- Intar.show = s.to_i
36
- end
37
- def prompt= p
38
- Intar.prompt = p if p
39
- end
40
- def bw!
41
- Intar.colour = false
42
- end
43
- def catch_exit!
44
- Intar.catch_exit = (Intar.catch_exit||0) + 3
45
- end
46
- def histall!
47
- Intar.histhid = false
48
- end
49
- def histfile= h
50
- Intar.histfile = nil_if_none h
51
- end
52
- def histmax= m
53
- Intar.histmax = (Integer m rescue m.to_i)
54
- end
55
- def configfile= c
56
- @configfile = nil_if_none c
57
- end
58
-
59
- define_option "p", :prompt=, "STR",
60
- "prompt - see source code for % escapes"
61
- alias_option "p", "prompt"
62
-
63
- define_option "q", :quiet!, "don't show results"
64
- alias_option "q", "quiet"
65
-
66
- define_option "S", :show=, "N", 1, "show result line limit (0=all)"
67
- alias_option "S", "show"
68
-
69
- define_option "r", :require, "FILE", "Ruby require"
70
- alias_option "r", "require"
71
-
72
- define_option "bw", :bw!, "black & white"
73
-
74
- define_option "c", :configfile=, "FILE", ".intarrc",
75
- "config file, NONE means none"
76
- alias_option "c", "configfile"
77
-
78
- define_option "H", :histfile=, "FILE", ".intar_history",
79
- "history file, NONE means none"
80
- alias_option "H", "histfile"
81
-
82
- define_option "m", :histmax=, "NUM",
83
- "maximum history entries to save"
84
- alias_option "m", "histmax"
85
-
86
- define_option "A", :histall!,
87
- "pass lines starting with blanks to history"
88
- alias_option "A", "histall"
89
-
90
- define_option "E", :encoding=, "ENC", "set encoding (like ruby -E)"
91
- alias_option "E", "encoding"
92
-
93
- define_option "x", :catch_exit!,
94
- "On exit exception: Wait 3 seconds for interrupt"
95
- alias_option "x", "catchexit"
96
- alias_option "x", "catch-exit"
97
-
98
- define_option "h", :help, "show options"
99
- alias_option "h", "help"
100
- define_option "V", :version, "show version"
101
- alias_option "V", "version"
102
-
103
- def run
104
- # @debug = true # Only development.
105
- read_cfg
106
- main = eval "self", TOPLEVEL_BINDING
107
- Intar.open main do |i|
108
- i.run *(@args.shift @args.length)
109
- end
110
- end
111
-
112
- private
113
-
114
- def encoding= ei
115
- e, i = ei.split ":"
116
- Encoding.default_external = e if e and not e.empty?
117
- Encoding.default_internal = i if i and not i.empty?
118
- [ $stdin, $stdout, $stderr].each do |io|
119
- io.set_encoding e, i
120
- end
121
- end
122
-
123
- def read_cfg
124
- return unless @configfile
125
- h = "~" unless @configfile[ File::SEPARATOR]
126
- c = File.expand_path @configfile, h
127
- return unless File.exists? c
128
- load c
129
- rescue Exception
130
- $@.pop 2
131
- e = $@.shift
132
- puts "#{e}: #$! (#{$!.class})"
133
- $@.each { |l| puts "\t#{l}" }
134
- raise "Error in config file #{c}"
135
- end
136
-
137
- def nil_if_none var
138
- case var
139
- when "", "NONE" then nil
140
- else var
141
- end
142
- end
143
-
144
- end
145
-
146
- IntarApp.run
147
-
data/lib/intar.rb DELETED
@@ -1,447 +0,0 @@
1
- #
2
- # intar.rb -- Interactive Ruby evaluation
3
- #
4
-
5
-
6
- =begin rdoc
7
-
8
- This could be opened not only by the Intar executable but also
9
- everywhere inside your Ruby program.
10
-
11
- = Example 1
12
-
13
- require "intar"
14
-
15
- Intar.prompt = "str(%(length)i):%03n%> "
16
- a = "hello"
17
- Intar.run a
18
-
19
-
20
- = Example 2
21
-
22
- require "intar"
23
-
24
- class C
25
- end
26
-
27
- class IntarC < Intar
28
- @show = 3
29
- @prompt = "%(33 1)c%t%c%> "
30
- @histfile = ".intarc_history"
31
-
32
- class <<self
33
- def open
34
- super C.new
35
- end
36
- end
37
- end
38
-
39
- IntarC.open do |ia| ia.run end
40
-
41
- =end
42
-
43
-
44
- require "readline"
45
- require "supplement"
46
- require "supplement/terminal"
47
-
48
-
49
- class Object
50
- def intar_binding
51
- binding
52
- end
53
- end
54
-
55
- class Intar
56
-
57
- class History
58
-
59
- IND = " "
60
- IND_RE = /^#{IND}/
61
-
62
- def initialize filename
63
- @filename = filename
64
- return unless @filename
65
- h = "~" unless @filename[ File::SEPARATOR]
66
- @filename = File.expand_path @filename, h
67
- File.exists? @filename and File.open @filename do |h|
68
- c = []
69
- h.each { |l|
70
- case l
71
- when IND_RE then c.push $'
72
- else push c.join.chomp ; c.clear
73
- end
74
- }
75
- push c.join.chomp
76
- end
77
- @num = Readline::HISTORY.length
78
- end
79
-
80
- def finish max
81
- return unless @filename
82
- @num.times { Readline::HISTORY.shift }
83
- File.open @filename, "a" do |h|
84
- a = []
85
- while (c = Readline::HISTORY.shift) do a.push c end
86
- if a.any? then
87
- h.puts "# #{Time.now}"
88
- a.each { |c|
89
- c.each_line { |l| h.puts IND + l }
90
- h.puts "-"
91
- }
92
- i = a.length
93
- h.puts "# #{i} #{entry_str i} added"
94
- end
95
- end
96
- n = File.open @filename do |h|
97
- h.inject 0 do |i,l| i += 1 if l =~ IND_RE ; i end
98
- end
99
- i = max - n
100
- if i < 0 then
101
- f = nil
102
- File.open @filename do |h|
103
- h.each_line { |l|
104
- f.push l if f
105
- case l
106
- when IND_RE then i += 1
107
- else f ||= [] if i >= 0
108
- end
109
- }
110
- end
111
- f and File.open @filename, "w" do |h| h.puts f end
112
- end
113
- rescue Errno
114
- # Forget it if there isn't enough disk space.
115
- end
116
-
117
- def push l
118
- Readline::HISTORY.push l unless l.empty?
119
- end
120
-
121
- private
122
-
123
- def entry_str i
124
- i == 1 ? "entry" : "entries"
125
- end
126
-
127
- end
128
-
129
- class <<self
130
-
131
- attr_accessor :prompt, :show, :shownil, :colour
132
-
133
- attr_reader :histfile
134
- def histfile= hf
135
- @histfile = hf
136
- if @history then
137
- @history.finish @histmax
138
- @history = History.new @histfile
139
- end
140
- end
141
-
142
- # Maximum number of history entries.
143
- attr_accessor :histmax
144
-
145
- # Whether to hide entries starting with whitespace.
146
- attr_accessor :histhid
147
-
148
- # Shell prefix and Pipe suffix
149
- attr_accessor :sh_pref, :pi_suff
150
-
151
- # Whether <code>Kernel#exit</code> should be caught.
152
- attr_accessor :catch_exit
153
-
154
- private
155
-
156
- def inherited sub
157
- sub.class_eval {
158
- s = superclass
159
- @prompt = s.prompt
160
- @show = s.show
161
- @shownil = s.shownil
162
- @colour = s.colour
163
- @histfile = s.histfile
164
- @histmax = s.histmax
165
- @histhid = s.histhid
166
- @sh_pref = s.sh_pref
167
- @pi_suff = s.pi_suff
168
- @catch_exit = s.catch_exit
169
- }
170
- end
171
-
172
- def history_file
173
- if @history then
174
- yield
175
- else
176
- @history = History.new @histfile
177
- begin
178
- yield
179
- ensure
180
- @history.finish @histmax
181
- @history = nil
182
- end
183
- end
184
- end
185
-
186
- public
187
-
188
- private :new
189
- def open obj
190
- history_file do
191
- i = new obj
192
- yield i
193
- end
194
- end
195
-
196
- def run obj
197
- open obj do |i| i.run end
198
- end
199
-
200
- def hist_add l
201
- return if @histhid and l == /\A[ \t]+/
202
- lst = Readline::HISTORY[-1] if Readline::HISTORY.length > 0
203
- @history.push l unless l == lst
204
- end
205
-
206
- end
207
-
208
- self.prompt = "%(32)c%i%c:%1c%03n%c%> "
209
- self.show = 1
210
- self.shownil = false
211
- self.colour = true
212
- self.histfile = nil
213
- self.histmax = 500
214
- self.histhid = true
215
- self.sh_pref = "."
216
- self.pi_suff = " |"
217
- self.catch_exit = nil
218
-
219
- private
220
-
221
- def initialize obj
222
- @obj = obj
223
- @n = 0
224
- end
225
-
226
- OLDSET = <<~EOT
227
- _, __, ___ = nil, nil, nil
228
- proc { |r,n|
229
- Array === __ or __ = []
230
- Hash === ___ or ___ = {}
231
- unless r.nil? or r.equal? __ or r.equal? ___ then
232
- _ = r
233
- __.delete r rescue nil
234
- __.unshift r
235
- ___[ n] = r
236
- end
237
- }
238
- EOT
239
-
240
- autoload :Etc, "etc"
241
- autoload :Socket, "socket"
242
-
243
- def cur_prompt prev
244
- t = Time.now
245
- self.class.prompt.gsub /%(?:
246
- \(([^\)]+)?\)
247
- |
248
- ([+-]?[0-9]+(?:\.[0-9]+)?)
249
- )?(.)/nx do
250
- case $3
251
- when "s" then @obj.to_s
252
- when "i" then $1 ? (@obj.send $1) : @obj.inspect
253
- when "n" then "%#$2d" % @n
254
- when "t" then t.strftime $1||"%X"
255
- when "u" then Etc.getpwuid.name
256
- when "h" then Socket.gethostname
257
- when "w" then cwd_short
258
- when "W" then File.basename cwd_short
259
- when "c" then (colour *($1 || $2 || "").split.map { |x| x.to_i }).to_s
260
- when ">" then prev ? "." : Process.uid == 0 ? "#" : ">"
261
- when "%" then $3
262
- else $&
263
- end
264
- end
265
- end
266
-
267
- def colour *c
268
- if self.class.colour then
269
- s = c.map { |i| "%d" % i }.join ";"
270
- "\e[#{s}m"
271
- end
272
- end
273
-
274
- def switchcolour *c
275
- s = colour *c
276
- print s if s
277
- end
278
-
279
- def cwd_short
280
- r = Dir.pwd
281
- h = Etc.getpwuid.dir
282
- r[ 0, h.length] == h and r[ 0, h.length] = "~"
283
- r
284
- end
285
-
286
- def readline
287
- r, @previous = @previous, nil
288
- r or @n += 1
289
- begin
290
- cp = cur_prompt r
291
- begin
292
- l = Readline.readline cp
293
- rescue Interrupt
294
- puts "^C -- #{$!.inspect}"
295
- retry
296
- end
297
- return if l.nil?
298
- if r then
299
- r << $/ << l
300
- else
301
- r = l unless l.empty?
302
- end
303
- self.class.hist_add l
304
- cp.strip!
305
- cp.gsub! /\e\[[0-9]*(;[0-9]*)*m/, ""
306
- @file = "#{self.class}/#{cp}"
307
- end until r
308
- r
309
- end
310
-
311
- # :stopdoc:
312
- ARROW = "=> "
313
- ELLIPSIS = "..."
314
- # :startdoc:
315
-
316
- def display r
317
- return if r.nil? and not self.class.shownil
318
- show = (self.class.show or return)
319
- i = ARROW.dup
320
- i << r.inspect
321
- if show > 0 then
322
- siz, = $stdout.winsize
323
- siz *= show
324
- if i.length > siz then
325
- i.cut! siz-ELLIPSIS.length
326
- i << ELLIPSIS
327
- end
328
- end
329
- puts i
330
- end
331
-
332
- def pager doit
333
- if doit then
334
- IO.popen ENV[ "PAGER"]||"more", "w" do |pg|
335
- begin
336
- stdout = $stdout.dup
337
- $stdout.reopen pg
338
- yield
339
- ensure
340
- $stdout.reopen stdout
341
- end
342
- end
343
- else
344
- yield
345
- end
346
- end
347
-
348
- public
349
-
350
- class Exit < Exception ; end
351
- class CmdFailed < Exception ; end
352
-
353
- def run *precmds
354
- bind = @obj.intar_binding
355
- precmds.each { |l| eval l, bind }
356
- oldset = eval OLDSET, bind
357
- @cl = (eval "caller.length", bind) + 2 # 2 = eval + run()
358
- while l = readline do
359
- re_sh_pref = /\A#{Regexp.quote self.class.sh_pref}/
360
- re_pi_suff = /#{Regexp.quote self.class.pi_suff}\z/
361
- switchcolour
362
- begin
363
- pg = l.slice! re_pi_suff
364
- r = pager pg do
365
- unless l =~ re_sh_pref then
366
- eval l, bind, @file
367
- else
368
- call_system $', bind
369
- end
370
- end
371
- oldset.call r, @n
372
- display r
373
- rescue Exit
374
- wait_exit and break
375
- rescue CmdFailed
376
- oldset.call $?, @n
377
- switchcolour 33
378
- puts "Exit code: #{$?.exitstatus}"
379
- rescue LoadError
380
- oldset.call $!, @n
381
- show_exception
382
- rescue SyntaxError
383
- if l.end_with? $/ then
384
- switchcolour 33
385
- puts $!
386
- else
387
- @previous = l
388
- end
389
- rescue SystemExit
390
- break if wait_exit
391
- oldset.call $!, @n
392
- show_exception
393
- rescue Exception
394
- oldset.call $!, @n
395
- show_exception
396
- ensure
397
- switchcolour
398
- end
399
- end
400
- puts
401
- ensure
402
- done
403
- end
404
-
405
- protected
406
-
407
- def done
408
- end
409
-
410
- private
411
-
412
- def wait_exit
413
- c = self.class.catch_exit
414
- c and c.times { print "." ; $stdout.flush ; sleep 1 }
415
- true
416
- rescue Interrupt
417
- puts
418
- end
419
-
420
- def show_exception
421
- unless $!.to_s.empty? then
422
- switchcolour 31, 1
423
- print $!
424
- print " " unless $!.to_s =~ /\s\z/
425
- end
426
- switchcolour 31, 22
427
- puts "(#{$!.class})"
428
- switchcolour 33
429
- bt = $@.dup
430
- if bt.length > @cl then
431
- bt.pop @cl
432
- end
433
- puts bt
434
- end
435
-
436
- def call_system l, bind
437
- l.strip!
438
- raise Exit if l.empty?
439
- eot = "EOT0001"
440
- eot.succ! while l[ eot]
441
- l = eval "<<#{eot}\n#{l}\n#{eot}", bind, @file
442
- system l or raise CmdFailed
443
- nil
444
- end
445
-
446
- end
447
-