appl 1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/intar +156 -0
- data/doc/demoappl +69 -0
- data/lib/appl.rb +302 -0
- data/lib/intar.rb +451 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c17ebbb3f574a1d27df9610144f9881c3b8ef910
|
4
|
+
data.tar.gz: 6e68ffdb0dd1e1c227dc371f29e9d93fb7a1abef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c16e0eb7be520d7eace0ab904c838d61ab3eaa29b34d3f2fda9d5c23b35f03721f244ffb814389399bb6f1a175ba68cdd39a2b9fefb7f4cc7bb4f84ce8dbeef0
|
7
|
+
data.tar.gz: 98cf8b5bde3d91115a0763e0561718f04a99d560134d52448f68384662af9814cad6fdc44a283352e00897cf323fd450e4f1c9e77de7c052f9a75ab7dbc4ae51
|
data/bin/intar
ADDED
@@ -0,0 +1,156 @@
|
|
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-2013 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
|
+
|
124
|
+
def read_cfg
|
125
|
+
c = @configfile
|
126
|
+
return unless c
|
127
|
+
unless c[ File::SEPARATOR] or (File.exists? c) then
|
128
|
+
c = File.expand_path c, "~"
|
129
|
+
return unless File.exists? c
|
130
|
+
end
|
131
|
+
r = File.read c
|
132
|
+
r.prepend "$SAFE = 1#$/"
|
133
|
+
Thread.new {
|
134
|
+
begin
|
135
|
+
eval r, TOPLEVEL_BINDING
|
136
|
+
rescue Exception
|
137
|
+
$@.pop 2
|
138
|
+
e = $@.shift
|
139
|
+
puts "#{e}: #$! (#{$!.class})"
|
140
|
+
$@.each { |l| puts "\t#{l}" }
|
141
|
+
raise "Error in config file #{c}"
|
142
|
+
end
|
143
|
+
}.value
|
144
|
+
end
|
145
|
+
|
146
|
+
def nil_if_none var
|
147
|
+
case var
|
148
|
+
when "", "NONE" then nil
|
149
|
+
else var
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
IntarApp.run
|
156
|
+
|
data/doc/demoappl
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# demoappl -- Just test the Application class
|
5
|
+
#
|
6
|
+
|
7
|
+
require "appl"
|
8
|
+
|
9
|
+
class DemoAppl < Application
|
10
|
+
|
11
|
+
NAME = "demoappl"
|
12
|
+
VERSION = "1.0"
|
13
|
+
SUMMARY = "Just test the Application class"
|
14
|
+
COPYRIGHT = "(C) 2011 Bertram Scharpf"
|
15
|
+
LICENSE = "For internal use only"
|
16
|
+
AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
|
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
|
23
|
+
|
24
|
+
OPTIONS_ENV = "DEMO_OPTS"
|
25
|
+
|
26
|
+
attr_writer :param, :default_param
|
27
|
+
attr_bang :switch, :raise, :debug
|
28
|
+
|
29
|
+
define_option "s", :switch!, "dummy switch"
|
30
|
+
alias_option "s", "switch"
|
31
|
+
|
32
|
+
define_option "p", :param=, "PARAM", "dummy parameter"
|
33
|
+
alias_option "p", "param"
|
34
|
+
|
35
|
+
define_option "d", :default_param=, "PARAM:dddd",
|
36
|
+
"dummy parameter with default value"
|
37
|
+
alias_option "d", "default-param"
|
38
|
+
|
39
|
+
define_option "r", :raise!, "raise an exception"
|
40
|
+
alias_option "r", "raise"
|
41
|
+
|
42
|
+
define_option "g", :debug!, "lots of ugly debugging information"
|
43
|
+
alias_option "g", "debug"
|
44
|
+
|
45
|
+
define_option "h", :help, "show this options list"
|
46
|
+
alias_option "h", "help"
|
47
|
+
define_option "V", :version, "show version information"
|
48
|
+
alias_option "V", "version"
|
49
|
+
|
50
|
+
UNKNOWN = "Sorry, unknown option"
|
51
|
+
STOPOPT = "no more options"
|
52
|
+
UNPROCA = "Warning: unprocessed arguments left"
|
53
|
+
|
54
|
+
def run
|
55
|
+
puts inspect
|
56
|
+
raise "stop" if @raise
|
57
|
+
ENV[ OPTIONS_ENV] or
|
58
|
+
puts "Try to set the environment variable #{OPTIONS_ENV}."
|
59
|
+
@args.clear
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
if false then
|
65
|
+
DemoAppl.run %w(-s -p rrrr qqqq -x -y -z)
|
66
|
+
else
|
67
|
+
DemoAppl.run
|
68
|
+
end
|
69
|
+
|
data/lib/appl.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
#
|
2
|
+
# appl.rb -- Application class
|
3
|
+
#
|
4
|
+
|
5
|
+
|
6
|
+
class Application
|
7
|
+
|
8
|
+
APPL_VERSION = "1.2".freeze
|
9
|
+
|
10
|
+
OPTIONS_ENV = nil
|
11
|
+
|
12
|
+
STOPOPT = "stop option processing"
|
13
|
+
UNKNOWN = "Unknown option"
|
14
|
+
UNPROCA = "Warning: unprocessed arguments"
|
15
|
+
|
16
|
+
class OptionError < StandardError ; end
|
17
|
+
class Done < Exception ; end
|
18
|
+
|
19
|
+
def initialize args = nil
|
20
|
+
@args = args||self.class.cmdline_arguments
|
21
|
+
self.class.each_option { |opt,desc,arg,dfl,act|
|
22
|
+
begin
|
23
|
+
send act, dfl if dfl
|
24
|
+
rescue NoMethodError
|
25
|
+
raise OptionError, "Option action `#{act}' is not defined."
|
26
|
+
end
|
27
|
+
}
|
28
|
+
while @args.first =~ /\A-/ do
|
29
|
+
opt = $'
|
30
|
+
@args.shift
|
31
|
+
if opt =~ /\A-/ then
|
32
|
+
break if !$' or $'.empty?
|
33
|
+
opt = $'
|
34
|
+
if opt =~ /^=/ then opt = $` ; @args.unshift $' end
|
35
|
+
act = self.class.option_act @args, opt, nil
|
36
|
+
send *act
|
37
|
+
else
|
38
|
+
until not opt or opt.empty? do
|
39
|
+
c = opt.slice! 0, 1
|
40
|
+
if opt =~ /^=/ then opt = nil ; @args.unshift $' end
|
41
|
+
act = self.class.option_act @args, c, opt
|
42
|
+
send *act
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
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
|
59
|
+
raise Done
|
60
|
+
end
|
61
|
+
|
62
|
+
def version
|
63
|
+
self.class.show_version
|
64
|
+
raise Done
|
65
|
+
end
|
66
|
+
|
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
|
+
def run
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute
|
78
|
+
run
|
79
|
+
if @args.any? then
|
80
|
+
u = @args.join " "
|
81
|
+
puts "#{self.class::UNPROCA}: #{u}"
|
82
|
+
end
|
83
|
+
0
|
84
|
+
rescue SignalException
|
85
|
+
raise if @debug
|
86
|
+
self.class.show_message $!.inspect
|
87
|
+
128 + ($!.signo||2) # Ruby 1.8 returns signo +nil+; assume SIGINT
|
88
|
+
rescue
|
89
|
+
raise if @debug
|
90
|
+
self.class.show_message "Error: #$!", "(#{$!.class})"
|
91
|
+
$!.to_i rescue 1
|
92
|
+
end
|
93
|
+
|
94
|
+
HASH_ORDER = RUBY_VERSION >= "1.9"
|
95
|
+
|
96
|
+
class <<self
|
97
|
+
|
98
|
+
def run args = nil
|
99
|
+
e = execute args
|
100
|
+
exit e
|
101
|
+
end
|
102
|
+
|
103
|
+
def version
|
104
|
+
if self::VERSION =~ %r/\s/ then
|
105
|
+
self::VERSION
|
106
|
+
else
|
107
|
+
"#{self::NAME} #{self::VERSION} -- #{self::SUMMARY}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def execute args = nil
|
114
|
+
i = new args
|
115
|
+
i.execute
|
116
|
+
rescue Done
|
117
|
+
0
|
118
|
+
rescue OptionError
|
119
|
+
show_message $!
|
120
|
+
127
|
121
|
+
end
|
122
|
+
|
123
|
+
def inherited sub
|
124
|
+
sub.instance_eval { @options, @aliases = {}, {} }
|
125
|
+
end
|
126
|
+
|
127
|
+
def attr_bang *syms
|
128
|
+
syms.each { |sym|
|
129
|
+
define_method :"#{sym}!" do
|
130
|
+
instance_variable_set :"@#{sym}", true
|
131
|
+
end
|
132
|
+
}
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
public
|
137
|
+
|
138
|
+
def define_option opt, *param
|
139
|
+
delete_option opt
|
140
|
+
act = param.shift
|
141
|
+
desc = param.pop
|
142
|
+
arg = param.shift
|
143
|
+
if arg then
|
144
|
+
if param.empty? then
|
145
|
+
arg, dfl = arg.split /:/, 2
|
146
|
+
if dfl =~ /\A:/ then
|
147
|
+
dfl = $'.to_sym
|
148
|
+
end
|
149
|
+
else
|
150
|
+
dfl = param.shift
|
151
|
+
end
|
152
|
+
end
|
153
|
+
d = param.map { |x| "#{x}#$/" }.join
|
154
|
+
desc.insert 0, d
|
155
|
+
@options[ opt.to_s] = [ desc, arg, dfl, act]
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
alias def_option define_option
|
159
|
+
|
160
|
+
def alias_option orig, opt
|
161
|
+
unalias_option opt
|
162
|
+
@aliases[ opt.to_s] = orig.to_s
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def delete_option opt
|
167
|
+
self < Application or return
|
168
|
+
superclass.delete_option opt
|
169
|
+
@options.delete opt
|
170
|
+
@aliases.reject! { |k,v| v == opt }
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
174
|
+
def unalias_option opt
|
175
|
+
self < Application or return
|
176
|
+
superclass.unalias_option opt
|
177
|
+
@aliases.delete opt
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
protected
|
182
|
+
|
183
|
+
def find_option_act opt
|
184
|
+
self < Application or return
|
185
|
+
@options[ opt] || @options[ @aliases[ opt]] ||
|
186
|
+
(superclass.find_option_act opt)
|
187
|
+
end
|
188
|
+
|
189
|
+
def all_options
|
190
|
+
if self < Application then
|
191
|
+
r = superclass.all_options
|
192
|
+
r.update @options
|
193
|
+
else
|
194
|
+
{}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def all_aliases
|
199
|
+
if self < Application then
|
200
|
+
r = superclass.all_aliases
|
201
|
+
r.update @aliases
|
202
|
+
else
|
203
|
+
{}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def options_desc &block
|
208
|
+
a = Hash.new do |h,k| h[ k] = [] end
|
209
|
+
all_aliases.each { |k,v|
|
210
|
+
a[ v].push k
|
211
|
+
}
|
212
|
+
a.values.each { |v| v.sort! } unless HASH_ORDER
|
213
|
+
each_option { |opt,desc,arg,dfl,|
|
214
|
+
yield opt, arg, dfl, desc
|
215
|
+
a[ opt].each { |l|
|
216
|
+
yield l, nil, nil, nil
|
217
|
+
}
|
218
|
+
}
|
219
|
+
yield "", nil, nil, self::STOPOPT
|
220
|
+
end
|
221
|
+
|
222
|
+
public
|
223
|
+
|
224
|
+
def each_option
|
225
|
+
o = all_options
|
226
|
+
o = o.sort_by { |k,v| k.swapcase } unless HASH_ORDER
|
227
|
+
o.each { |opt,(desc,arg,dfl,act)|
|
228
|
+
case dfl
|
229
|
+
when Symbol then dfl = const_get dfl
|
230
|
+
end
|
231
|
+
yield opt, desc, arg, dfl, act
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
def option_act args, opt, rest
|
236
|
+
dada = find_option_act opt
|
237
|
+
dada or raise OptionError, "#{self::UNKNOWN}: `#{opt}'."
|
238
|
+
desc, arg, dfl, act = *dada
|
239
|
+
r = [ act]
|
240
|
+
if arg then
|
241
|
+
p = rest.slice! 0, rest.length if rest and not rest.empty?
|
242
|
+
r.push p||args.shift
|
243
|
+
end
|
244
|
+
r
|
245
|
+
end
|
246
|
+
|
247
|
+
def show_options
|
248
|
+
options_desc do |opt,arg,dfl,desc|
|
249
|
+
opt = opt.length == 1 ? "-#{opt}" : "--#{opt}"
|
250
|
+
arg &&= "#{arg}"
|
251
|
+
dfl &&= "[#{dfl}]"
|
252
|
+
arg << dfl if arg && dfl
|
253
|
+
puts " %-10s %-12s %s" % [ opt, arg, desc]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def show_version
|
258
|
+
puts version
|
259
|
+
puts self::COPYRIGHT if const_defined? :COPYRIGHT
|
260
|
+
puts "License: #{self::LICENSE}" if const_defined? :LICENSE
|
261
|
+
a = []
|
262
|
+
a.push self::AUTHOR if const_defined? :AUTHOR
|
263
|
+
a.concat self::AUTHORS if const_defined? :AUTHORS
|
264
|
+
if a.any? then
|
265
|
+
a.flatten!
|
266
|
+
a.uniq!
|
267
|
+
j = a.join ", "
|
268
|
+
h = a.length == 1 ? "Author" : "Authors"
|
269
|
+
puts "#{h}: #{j}"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def show_message msg, extra = nil
|
274
|
+
if $stderr.tty? then
|
275
|
+
msg = "\e[31;1m#{msg}\e[m"
|
276
|
+
if extra then
|
277
|
+
extra = "\e[31m#{extra}\e[m"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
if extra then
|
281
|
+
msg = [ msg, extra].join " "
|
282
|
+
end
|
283
|
+
$stderr.puts msg
|
284
|
+
end
|
285
|
+
|
286
|
+
def cmdline_arguments
|
287
|
+
r = []
|
288
|
+
oe = self::OPTIONS_ENV
|
289
|
+
eo = ENV[ oe] if oe
|
290
|
+
if eo then
|
291
|
+
eo.scan /"((?:\\.|[^"])*")|[^" \t]+/ do
|
292
|
+
r.push $1 ? (eval $1) : $&
|
293
|
+
end
|
294
|
+
end
|
295
|
+
r.concat $*
|
296
|
+
r
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
data/lib/intar.rb
ADDED
@@ -0,0 +1,451 @@
|
|
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 empty_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.starts_with "./"
|
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, :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
|
+
@colour = s.colour
|
162
|
+
@histfile = s.histfile
|
163
|
+
@histmax = s.histmax
|
164
|
+
@histhid = s.histhid
|
165
|
+
@sh_pref = s.sh_pref
|
166
|
+
@pi_suff = s.pi_suff
|
167
|
+
@catch_exit = s.catch_exit
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
def history_file
|
172
|
+
if @history then
|
173
|
+
yield
|
174
|
+
else
|
175
|
+
@history = History.new @histfile
|
176
|
+
begin
|
177
|
+
yield
|
178
|
+
ensure
|
179
|
+
@history.finish @histmax
|
180
|
+
@history = nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
public
|
186
|
+
|
187
|
+
private :new
|
188
|
+
def open obj
|
189
|
+
history_file do
|
190
|
+
i = new obj
|
191
|
+
yield i
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def run obj
|
196
|
+
open obj do |i| i.run end
|
197
|
+
end
|
198
|
+
|
199
|
+
def hist_add l
|
200
|
+
return if @histhid and l == /\A[ \t]+/
|
201
|
+
lst = Readline::HISTORY[-1] if Readline::HISTORY.length > 0
|
202
|
+
@history.push l unless l == lst
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
self.prompt = "%(32)c%i%c:%1c%03n%c%> "
|
208
|
+
self.show = 1
|
209
|
+
self.colour = true
|
210
|
+
self.histfile = nil
|
211
|
+
self.histmax = 500
|
212
|
+
self.histhid = true
|
213
|
+
self.sh_pref = "."
|
214
|
+
self.pi_suff = " |"
|
215
|
+
self.catch_exit = nil
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def initialize obj
|
220
|
+
@obj = obj
|
221
|
+
@n = 0
|
222
|
+
end
|
223
|
+
|
224
|
+
OLDSET = <<-EOT
|
225
|
+
_, __, ___ = nil, nil, nil
|
226
|
+
proc { |r,n|
|
227
|
+
Array === __ or __ = []
|
228
|
+
Hash === ___ or ___ = {}
|
229
|
+
unless r.nil? or r.equal? __ or r.equal? ___ then
|
230
|
+
_ = r
|
231
|
+
__.delete r rescue nil
|
232
|
+
__.unshift r
|
233
|
+
___[ n] = r
|
234
|
+
end
|
235
|
+
}
|
236
|
+
EOT
|
237
|
+
|
238
|
+
autoload :Etc, "etc"
|
239
|
+
autoload :Socket, "socket"
|
240
|
+
|
241
|
+
def cur_prompt
|
242
|
+
t = Time.now
|
243
|
+
self.class.prompt.gsub /%(?:
|
244
|
+
\(([^\)]*)\)
|
245
|
+
|
|
246
|
+
([+-]?[0-9]+(?:\.[0-9]+)?)
|
247
|
+
)?(.)/nx do
|
248
|
+
case $3
|
249
|
+
when "s" then @obj.to_s
|
250
|
+
when "i" then $1.notempty? ? (@obj.send $1) : @obj.inspect
|
251
|
+
when "n" then "%#$2d" % @n
|
252
|
+
when "t" then t.strftime $1||"%X"
|
253
|
+
when "u" then Etc.getpwuid.name
|
254
|
+
when "h" then Socket.gethostname
|
255
|
+
when "w" then cwd_short
|
256
|
+
when "W" then File.basename cwd_short
|
257
|
+
when "c" then (colour *($1 || $2 || "").split.map { |x| x.to_i }).to_s
|
258
|
+
when ">" then Process.uid == 0 ? "#" : ">"
|
259
|
+
when "%" then $3
|
260
|
+
else $&
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def colour *c
|
266
|
+
if self.class.colour then
|
267
|
+
s = c.map { |i| "%d" % i }.join ";"
|
268
|
+
"\e[#{s}m"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def switchcolour *c
|
273
|
+
s = colour *c
|
274
|
+
print s if s
|
275
|
+
end
|
276
|
+
|
277
|
+
def cwd_short
|
278
|
+
r = Dir.pwd
|
279
|
+
h = Etc.getpwuid.dir
|
280
|
+
r[ 0, h.length] == h and r[ 0, h.length] = "~"
|
281
|
+
r
|
282
|
+
end
|
283
|
+
|
284
|
+
def readline
|
285
|
+
r, @previous = @previous, nil
|
286
|
+
r or @n += 1
|
287
|
+
cp = cur_prompt
|
288
|
+
loop do
|
289
|
+
begin
|
290
|
+
l = Readline.readline r ? "" : cp
|
291
|
+
rescue Interrupt
|
292
|
+
puts "^C -- #{$!.inspect}"
|
293
|
+
retry
|
294
|
+
end
|
295
|
+
if r then
|
296
|
+
break if l.nil?
|
297
|
+
r << $/ << l
|
298
|
+
break if l.empty?
|
299
|
+
else
|
300
|
+
return if l.nil?
|
301
|
+
next unless l =~ /\S/
|
302
|
+
r = l
|
303
|
+
break unless l =~ /\\+\z/ and $&.length % 2 != 0
|
304
|
+
end
|
305
|
+
end
|
306
|
+
cp.strip!
|
307
|
+
cp.gsub! /\e\[[0-9]*(;[0-9]*)*m/, ""
|
308
|
+
@file = "#{self.class}/#{cp}"
|
309
|
+
self.class.hist_add r
|
310
|
+
r
|
311
|
+
end
|
312
|
+
|
313
|
+
# :stopdoc:
|
314
|
+
ARROW = "=> "
|
315
|
+
ELLIPSIS = "..."
|
316
|
+
# :startdoc:
|
317
|
+
|
318
|
+
def display r
|
319
|
+
return if r.nil?
|
320
|
+
show = (self.class.show or return)
|
321
|
+
i = r.inspect
|
322
|
+
if show > 0 then
|
323
|
+
siz, = $stdout.winsize
|
324
|
+
siz *= show
|
325
|
+
siz -= ARROW.length
|
326
|
+
if i.length > siz then
|
327
|
+
i.cut! siz-ELLIPSIS.length
|
328
|
+
i << ELLIPSIS
|
329
|
+
end
|
330
|
+
end
|
331
|
+
i.prepend ARROW
|
332
|
+
puts i
|
333
|
+
end
|
334
|
+
|
335
|
+
def pager doit
|
336
|
+
if doit then
|
337
|
+
IO.popen ENV[ "PAGER"]||"more", "w" do |pg|
|
338
|
+
begin
|
339
|
+
stdout = $stdout.dup
|
340
|
+
$stdout.reopen pg
|
341
|
+
yield
|
342
|
+
ensure
|
343
|
+
$stdout.reopen stdout
|
344
|
+
end
|
345
|
+
end
|
346
|
+
else
|
347
|
+
yield
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
public
|
352
|
+
|
353
|
+
class Exit < Exception ; end
|
354
|
+
class CmdFailed < Exception ; end
|
355
|
+
|
356
|
+
def run *precmds
|
357
|
+
bind = @obj.empty_binding
|
358
|
+
precmds.each { |l| eval l, bind }
|
359
|
+
oldset = eval OLDSET, bind
|
360
|
+
@cl = eval "caller.length", bind
|
361
|
+
while l = readline do
|
362
|
+
re_sh_pref = /\A#{Regexp.quote self.class.sh_pref}/
|
363
|
+
re_pi_suff = /#{Regexp.quote self.class.pi_suff}\z/
|
364
|
+
switchcolour
|
365
|
+
begin
|
366
|
+
pg = l.slice! re_pi_suff
|
367
|
+
r = pager pg do
|
368
|
+
unless l =~ re_sh_pref then
|
369
|
+
eval l, bind, @file
|
370
|
+
else
|
371
|
+
call_system $', bind
|
372
|
+
end
|
373
|
+
end
|
374
|
+
oldset.call r, @n
|
375
|
+
display r
|
376
|
+
rescue Exit
|
377
|
+
wait_exit and break
|
378
|
+
rescue CmdFailed
|
379
|
+
oldset.call $?, @n
|
380
|
+
switchcolour 33
|
381
|
+
puts "Exit code: #{$?.exitstatus}"
|
382
|
+
rescue LoadError
|
383
|
+
oldset.call $!, @n
|
384
|
+
show_exception
|
385
|
+
rescue ScriptError
|
386
|
+
if l[ $/] then
|
387
|
+
switchcolour 33
|
388
|
+
puts $!
|
389
|
+
else
|
390
|
+
@previous = l
|
391
|
+
end
|
392
|
+
rescue SystemExit
|
393
|
+
break if wait_exit
|
394
|
+
oldset.call $!, @n
|
395
|
+
show_exception
|
396
|
+
rescue Exception
|
397
|
+
oldset.call $!, @n
|
398
|
+
show_exception
|
399
|
+
ensure
|
400
|
+
switchcolour
|
401
|
+
end
|
402
|
+
end
|
403
|
+
puts
|
404
|
+
ensure
|
405
|
+
done
|
406
|
+
end
|
407
|
+
|
408
|
+
protected
|
409
|
+
|
410
|
+
def done
|
411
|
+
end
|
412
|
+
|
413
|
+
private
|
414
|
+
|
415
|
+
def wait_exit
|
416
|
+
c = self.class.catch_exit
|
417
|
+
c and c.times { print "." ; $stdout.flush ; sleep 1 }
|
418
|
+
true
|
419
|
+
rescue Interrupt
|
420
|
+
puts
|
421
|
+
end
|
422
|
+
|
423
|
+
def show_exception
|
424
|
+
unless $!.to_s.empty? then
|
425
|
+
switchcolour 31, 1
|
426
|
+
print $!
|
427
|
+
print " " unless $!.to_s =~ /\s\z/
|
428
|
+
end
|
429
|
+
switchcolour 31, 22
|
430
|
+
puts "(#{$!.class})"
|
431
|
+
switchcolour 33
|
432
|
+
bt = $@.dup
|
433
|
+
if bt.length > @cl then
|
434
|
+
bt.pop @cl
|
435
|
+
bt.push bt.pop[ /(.*:\d+):.*/, 1]
|
436
|
+
end
|
437
|
+
puts bt
|
438
|
+
end
|
439
|
+
|
440
|
+
def call_system l, bind
|
441
|
+
l.strip!
|
442
|
+
raise Exit if l.empty?
|
443
|
+
eot = "EOT0001"
|
444
|
+
eot.succ! while l[ eot]
|
445
|
+
l = eval "<<#{eot}\n#{l}\n#{eot}", bind, @file
|
446
|
+
system l or raise CmdFailed
|
447
|
+
nil
|
448
|
+
end
|
449
|
+
|
450
|
+
end
|
451
|
+
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.2'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bertram Scharpf
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: supplement
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
27
|
+
description: |
|
28
|
+
A base class for command line applications doing options parsing
|
29
|
+
and generating exit codes.
|
30
|
+
email: "<software@bertram-scharpf.de>"
|
31
|
+
executables:
|
32
|
+
- intar
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- bin/intar
|
37
|
+
- doc/demoappl
|
38
|
+
- lib/appl.rb
|
39
|
+
- lib/intar.rb
|
40
|
+
homepage: http://www.bertram-scharpf.de/software/appl
|
41
|
+
licenses:
|
42
|
+
- BSD
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements:
|
59
|
+
- Just Ruby
|
60
|
+
rubyforge_project: NONE
|
61
|
+
rubygems_version: 2.6.4
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Easy option parsing
|
65
|
+
test_files: []
|