appl 1.6 → 1.12

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 (8) hide show
  1. checksums.yaml +4 -4
  2. data/doc/demoappl +7 -8
  3. data/doc/demofan +90 -0
  4. data/lib/appl.rb +50 -70
  5. data/lib/applfan.rb +79 -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: d312ea2daf699e2d23d8733654e4ad8014c15dcbc27d8d3008e9832457ff48d1
4
- data.tar.gz: 92b2213d100a74618c1ac62341bc291a1eb095461a9ec188e71f1f77b7272b46
3
+ metadata.gz: 840b8916d8f8d5b79f0f87bdee61772a9f6e00bffa3cc3c69aeaad93b7b950fe
4
+ data.tar.gz: 570c63f80082520339691d6fe3c1aad68a953e7eee90866b21dfe50c9705af80
5
5
  SHA512:
6
- metadata.gz: 136ac972d7e584da9e0dbf4478516825a79f97f7f6f8755ad1c7301ea71a4c6e2ec15fe055067908566a3008b69ecc061e7e029e04d8ac41f63181fd1764b3ca
7
- data.tar.gz: 7c08a7c5346e846c1efaed6cceafa90212d712bcb7165e85fc9010e1fc92d47ef21a658e87fdd308e404f0db7ea80a66c063c8e7390750fc24d23111f5a9d8d0
6
+ metadata.gz: d634ae0936e200f45b741a8ed2dce2900c7ad548993e08da923104b1f95612c0a2e94b72482d41a5041b754f68a6231a8e101986771052ee24818d5273d475e8
7
+ data.tar.gz: 0ed10689b122791eef9c7f8468881a8449740e0858209f5d0867241b67b64f1ef4f26db7703c7586a3f06371075b66a6f577f2056effcf5907b7862dc6b2628f
@@ -15,11 +15,10 @@ class DemoAppl < Application
15
15
  LICENSE = "For internal use only"
16
16
  AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
17
17
 
18
- DESCRIPTION = <<EOT
19
- This is an example how to define an Application subclass.
20
- The program just prints out its parameters.
21
-
22
- EOT
18
+ DESCRIPTION = <<~EOT
19
+ This is an example how to define an Application subclass.
20
+ The program just prints out its parameters.
21
+ EOT
23
22
 
24
23
  OPTIONS_ENV = "DEMO_OPTS"
25
24
 
@@ -47,9 +46,9 @@ EOT
47
46
  define_option "V", :version, "show version information"
48
47
  alias_option "V", "version"
49
48
 
50
- UNKNOWN = "Sorry, unknown option"
51
- STOPOPT = "no more options"
52
- UNPROCA = "Warning: unprocessed arguments left"
49
+ STOPOPT = "Stop option processing"
50
+ UNKNOWN = "Not a valid option: `%s'."
51
+ UNPROCA = "Warning. Unprocessed arguments: %s"
53
52
 
54
53
  def run
55
54
  puts inspect
@@ -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
+
@@ -5,13 +5,16 @@
5
5
 
6
6
  class Application
7
7
 
8
- APPL_VERSION = "1.6".freeze
8
+ APPL_VERSION = "1.12".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
+ W_OPTS = 10
17
+ W_ARGS = 16
15
18
 
16
19
  class OptionError < StandardError ; end
17
20
  class Done < Exception ; end
@@ -46,45 +49,33 @@ class Application
46
49
  end
47
50
 
48
51
  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
52
+ self.class.help
59
53
  raise Done
60
54
  end
61
55
 
62
56
  def version
63
- self.class.show_version
57
+ self.class.version
64
58
  raise Done
65
59
  end
66
60
 
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
61
  def run
75
62
  end
76
63
 
77
- def execute
78
- run
64
+ def warn_unprocessed
79
65
  if @args.any? then
80
- u = @args.join " "
81
- puts "#{self.class::UNPROCA}: #{u}"
66
+ $stderr.puts self.class.root::UNPROCA % (@args.join " ")
82
67
  end
83
- 0
68
+ end
69
+
70
+ def execute
71
+ r = run
72
+ r = 0 unless Integer === r
73
+ warn_unprocessed
74
+ r
84
75
  rescue SignalException
85
76
  raise if @debug
86
77
  self.class.show_message $!.inspect
87
- 128 + ($!.signo||2) # Ruby 1.8 returns signo +nil+; assume SIGINT
78
+ 128 + $!.signo
88
79
  rescue
89
80
  raise if @debug
90
81
  self.class.show_message "Error: #$!", "(#{$!.class})"
@@ -98,14 +89,6 @@ This base class does nothing by default.
98
89
  exit e
99
90
  end
100
91
 
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
92
  private
110
93
 
111
94
  def execute args = nil
@@ -119,7 +102,9 @@ This base class does nothing by default.
119
102
  end
120
103
 
121
104
  def inherited sub
122
- sub.instance_eval { @options, @aliases = {}, {} }
105
+ sub.name or return
106
+ o, a = @options.dup.to_h, @aliases.dup.to_h
107
+ sub.instance_eval { @options, @aliases = o, a }
123
108
  end
124
109
 
125
110
  def attr_bang *syms
@@ -162,16 +147,12 @@ This base class does nothing by default.
162
147
  end
163
148
 
164
149
  def delete_option opt
165
- self < Application or return
166
- superclass.delete_option opt
167
- @options.delete opt
168
150
  @aliases.reject! { |k,v| v == opt }
151
+ @options.delete opt
169
152
  nil
170
153
  end
171
154
 
172
155
  def unalias_option opt
173
- self < Application or return
174
- superclass.unalias_option opt
175
156
  @aliases.delete opt
176
157
  nil
177
158
  end
@@ -179,32 +160,12 @@ This base class does nothing by default.
179
160
  protected
180
161
 
181
162
  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
163
+ @options[ opt] || @options[ @aliases[ opt]]
203
164
  end
204
165
 
205
166
  def options_desc &block
206
167
  a = Hash.new do |h,k| h[ k] = [] end
207
- all_aliases.each { |k,v|
168
+ @aliases.each { |k,v|
208
169
  a[ v].push k
209
170
  }
210
171
  each_option { |opt,desc,arg,dfl,|
@@ -213,14 +174,13 @@ This base class does nothing by default.
213
174
  yield l, nil, nil, nil
214
175
  }
215
176
  }
216
- yield "", nil, nil, self::STOPOPT
177
+ yield "", nil, nil, root::STOPOPT
217
178
  end
218
179
 
219
180
  public
220
181
 
221
182
  def each_option
222
- o = all_options
223
- o.each { |opt,(desc,arg,dfl,act)|
183
+ @options.each { |opt,(desc,arg,dfl,act)|
224
184
  case dfl
225
185
  when Symbol then dfl = const_get dfl
226
186
  end
@@ -230,7 +190,7 @@ This base class does nothing by default.
230
190
 
231
191
  def option_act args, opt, rest
232
192
  dada = find_option_act opt
233
- dada or raise OptionError, "#{self::UNKNOWN}: `#{opt}'."
193
+ dada or raise OptionError, root::UNKNOWN % opt
234
194
  desc, arg, dfl, act = *dada
235
195
  r = [ act]
236
196
  if arg then
@@ -246,12 +206,32 @@ This base class does nothing by default.
246
206
  arg &&= "#{arg}"
247
207
  dfl &&= "[#{dfl}]"
248
208
  arg << dfl if arg && dfl
249
- puts " %-10s %-12s %s" % [ opt, arg, desc]
209
+ puts " %-*s %-*s %s" % [ root::W_OPTS, opt, root::W_ARGS, arg, desc]
210
+ end
211
+ end
212
+
213
+ def root
214
+ self
215
+ end
216
+
217
+ def applname
218
+ self::NAME
219
+ end
220
+
221
+ def help
222
+ puts "#{applname} -- #{self::SUMMARY}"
223
+ puts
224
+ puts self::DESCRIPTION
225
+ puts
226
+ show_options
227
+ if block_given? then
228
+ puts
229
+ yield
250
230
  end
251
231
  end
252
232
 
253
- def show_version
254
- puts version
233
+ def version
234
+ puts "#{applname} #{self::VERSION} -- #{self::SUMMARY}"
255
235
  puts self::COPYRIGHT if const_defined? :COPYRIGHT
256
236
  puts "License: #{self::LICENSE}" if const_defined? :LICENSE
257
237
  a = []
@@ -0,0 +1,79 @@
1
+ #
2
+ # lib/applfan.rb -- ApplicationFan class
3
+ #
4
+
5
+ require "appl"
6
+
7
+
8
+ ApplicationFan = Class.new Application
9
+
10
+ class ApplicationFan
11
+
12
+ AVAILCMDS = "Available commands (say -h after one for help)"
13
+ NOCOMMAND = "No command given. Say -h for a list."
14
+ UNKNWNCMD = "Unknown command: `%s'. Say -h for a list."
15
+
16
+ W_CMDS = 16
17
+
18
+ class CommandError < StandardError ; end
19
+
20
+ class <<self
21
+
22
+ attr_accessor :commands
23
+
24
+ def find_command name
25
+ @commands.find { |c| c::NAME == name }
26
+ end
27
+
28
+
29
+ def help
30
+ super do
31
+ if block_given? then
32
+ yield
33
+ puts
34
+ end
35
+ puts self::AVAILCMDS
36
+ puts
37
+ @commands.each { |c|
38
+ puts " %-*s %s" % [ self::W_CMDS, c::NAME, c::SUMMARY]
39
+ }
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def inherited sub
46
+ sub.instance_eval do
47
+ @commands = []
48
+ const_set :Command, (
49
+ Class.new Application do
50
+ define_singleton_method :inherited do |c|
51
+ sub.commands.push c
52
+ super c
53
+ end
54
+ define_singleton_method :root do
55
+ sub
56
+ end
57
+ define_singleton_method :applname do
58
+ "#{root::NAME} #{self::NAME}"
59
+ end
60
+ end
61
+ )
62
+ end
63
+ super
64
+ end
65
+
66
+ end
67
+
68
+ def run
69
+ c = @args.shift
70
+ c or raise CommandError, self.class::NOCOMMAND
71
+ cmd = self.class.find_command c
72
+ cmd or raise CommandError, self.class::UNKNWNCMD % c
73
+ sub = cmd.new @args.slice! 0, @args.length
74
+ yield sub if block_given?
75
+ sub.execute
76
+ end
77
+
78
+ end
79
+
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'
4
+ version: '1.12'
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-06-07 00:00:00.000000000 Z
11
+ date: 2020-11-19 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.3
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
-
@@ -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
-