intar 2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ebb1f8448fc1b1975a50bbc99cac9e601dfc5c8c949fc9c6bb3e2e9ccec5d556
4
+ data.tar.gz: ac7e7ac5915357dd60db8e958a4c58fc15050c3b04d52746a0d3e3d05d971d98
5
+ SHA512:
6
+ metadata.gz: 5e983cbc3b2773a4f3fc8297407f59aa56fd5156adaa5af2c2e7657e5e3523f8b16bfa193e5f088e1a5d4b259838f63444b7dea35a564c8133e85969ec055205
7
+ data.tar.gz: bfeb03f9854a50f9f5c7d1ef7c658bd871a4f0896448009236c27f1125e2c6517e563c84b1c6a294c5289a7588e5f180e348af04b89e2949bcacc721be7fddcc
@@ -0,0 +1,145 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ #
4
+ # intar -- Interactive Ruby evaluation
5
+ #
6
+
7
+ require "intar"
8
+ require "appl"
9
+
10
+
11
+ class Intar::Appl < Application
12
+
13
+ NAME = "intar"
14
+ VERSION = Intar::VERSION
15
+ SUMMARY = "Interactive Ruby"
16
+ COPYRIGHT = "(C) 2008-2020 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
+ attr_writer :show, :prompt, :histmax
32
+ def histfile= f ; @histfile = nil_if_none f ; end
33
+ def configfile= f ; @configfile = nil_if_none f ; end
34
+ attr_bang :quiet, :bw, :catch_exit, :histall
35
+
36
+ define_option "p", :prompt=, "STR", "prompt - see source code for % escapes"
37
+ alias_option "p", "prompt"
38
+
39
+ define_option "q", :quiet!, "don't show results"
40
+ alias_option "q", "quiet"
41
+
42
+ define_option "s", :show=, "N", "show result line limit (0=all)"
43
+ alias_option "s", "show"
44
+
45
+ define_option "r", :require, "FILE", "Ruby require"
46
+ alias_option "r", "require"
47
+
48
+ define_option "bw", :bw!, "black & white"
49
+
50
+ define_option "c", :configfile=, "FILE", ".intarrc",
51
+ "config file, NONE means none"
52
+ alias_option "c", "configfile"
53
+
54
+ define_option "H", :histfile=, "FILE", ".intar_history",
55
+ "history file, NONE means none"
56
+ alias_option "H", "histfile"
57
+
58
+ define_option "m", :histmax=, "NUM",
59
+ "maximum history entries to save"
60
+ alias_option "m", "histmax"
61
+
62
+ define_option "A", :histall!,
63
+ "pass lines starting with blanks to history"
64
+ alias_option "A", "histall"
65
+
66
+ define_option "E", :encoding=, "ENC", "set encoding (like ruby -E)"
67
+ alias_option "E", "encoding"
68
+
69
+ define_option "x", :catch_exit!, "Don't quit on SystemExit exception"
70
+ alias_option "x", "catchexit"
71
+ alias_option "x", "catch-exit"
72
+
73
+ define_option "h", :help, "show options"
74
+ alias_option "h", "help"
75
+ define_option "V", :version, "show version"
76
+ alias_option "V", "version"
77
+
78
+ @params = {}
79
+ class <<self
80
+ attr_reader :params
81
+ def set **kwargs
82
+ @params.update **kwargs
83
+ end
84
+ end
85
+
86
+ def run
87
+ # @debug = true # Only development.
88
+ read_cfg
89
+ Intar.open **params do |i|
90
+ i.execute @args.shift while @args.notempty?
91
+ i.run
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def params
98
+ p = self.class.params
99
+ if @quiet then
100
+ p[ :show] = 0
101
+ elsif @show then
102
+ p[ :show] = Integer @show
103
+ end
104
+ p[ :prompt ] = @prompt if @prompt
105
+ p[ :color ] = false if @bw
106
+ p[ :catch_exit] = true if @catch_exit
107
+ p[ :histhid ] = false if @histall
108
+ p[ :histfile ] = @histfile if @histfile
109
+ p[ :histmax ] = Integer @histmax if @histmax
110
+ p
111
+ end
112
+
113
+ def encoding= ei
114
+ e, i = ei.split ":"
115
+ Encoding.default_external = e if e and not e.empty?
116
+ Encoding.default_internal = i if i and not i.empty?
117
+ [ $stdin, $stdout, $stderr].each do |io|
118
+ io.set_encoding e, i
119
+ end
120
+ end
121
+
122
+ def read_cfg
123
+ return unless @configfile
124
+ c = File.expand_path @configfile, "~"
125
+ return unless File.exists? c
126
+ load c
127
+ rescue Exception
128
+ $@.pop 2
129
+ e = $@.shift
130
+ puts "#{e}: #$! (#{$!.class})"
131
+ $@.each { |l| puts "\t#{l}" }
132
+ raise "Error in config file #{c}"
133
+ end
134
+
135
+ def nil_if_none var
136
+ case var
137
+ when "", "NONE" then nil
138
+ else var
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ Intar::Appl.run
145
+
@@ -0,0 +1,564 @@
1
+ #
2
+ # intar.rb -- Interactive Ruby evaluation
3
+ #
4
+
5
+ require "supplement"
6
+ require "supplement/terminal"
7
+ require "intar/version"
8
+ require "intar/prompt"
9
+ require "intar/redirect"
10
+
11
+
12
+ =begin rdoc
13
+
14
+ This could be opened not only by the Intar executable but also
15
+ everywhere inside your Ruby program.
16
+
17
+ = Example 1
18
+
19
+ require "intar"
20
+ Intar.run
21
+
22
+
23
+ = Example 2
24
+
25
+ require "intar"
26
+ a = "hello"
27
+ Intar.run a, prompt: "str(%(length)i):%03n%> "
28
+
29
+
30
+ = Example 3
31
+
32
+ require "intar"
33
+ Intar.open show: 3, histfile: ".intar_history-example" do |i|
34
+ i.execute "puts inspect"
35
+ i.run
36
+ end
37
+
38
+
39
+ =end
40
+
41
+
42
+ class Object
43
+ def intar_binding
44
+ binding
45
+ end
46
+ end
47
+
48
+
49
+ class Intar
50
+
51
+ class <<self
52
+
53
+ def open obj = nil, **params
54
+ i = new obj, **params
55
+ yield i
56
+ end
57
+
58
+ def run obj = nil, **params
59
+ open obj, **params do |i| i.run end
60
+ end
61
+
62
+ end
63
+
64
+
65
+ DEFAULTS = {
66
+ prompt: "%(32)c%i%c:%1c%03n%c%> ",
67
+ color: true,
68
+ show: 1,
69
+ shownil: false,
70
+ pager: nil,
71
+ catch_exit: false,
72
+ histhid: true,
73
+ histfile: nil,
74
+ histmax: 500,
75
+ }
76
+
77
+ def initialize obj = nil, **params
78
+ @obj = obj.nil? ? (eval "self", TOPLEVEL_BINDING) : obj
79
+ @params = DEFAULTS.dup.update params
80
+ @binding = @obj.intar_binding
81
+ @n = 0
82
+ @prompt = Prompt.new
83
+ end
84
+
85
+
86
+ class Quit < Exception ; end
87
+ class Failed < Exception ; end
88
+
89
+ def run
90
+ prompt_load_history
91
+ oldset = eval OLDSET, @binding
92
+ while l = readline do
93
+ begin
94
+ @redir = find_redirect l
95
+ r = if l =~ /\A\\(\w+|.)\s*(.*?)\s*\Z/ then
96
+ m = get_metacommand $1
97
+ send m.method, (eval_param $2)
98
+ else
99
+ begin
100
+ @redir.redirect_output do eval l, @binding, @file end
101
+ rescue SyntaxError
102
+ raise if l.end_with? $/
103
+ @previous = l
104
+ next
105
+ end
106
+ end
107
+ rescue Quit
108
+ break
109
+ rescue Failed
110
+ switchcolor 31, 1
111
+ puts $!
112
+ switchcolor
113
+ r = $!
114
+ rescue Exception
115
+ raise if SystemExit === $! and not @params[ :catch_exit]
116
+ show_exception
117
+ r = $!
118
+ else
119
+ display r
120
+ end
121
+ oldset.call r, @n
122
+ end
123
+ prompt_save_history
124
+ end
125
+
126
+ def execute code
127
+ eval code, @binding, "#{self.class}/execute"
128
+ end
129
+
130
+
131
+ private
132
+
133
+ def find_redirect line
134
+ RedirectPipe.detect line, @params[ :pager] or
135
+ RedirectFile.detect line, @params[ :output] or
136
+ RedirectNone.new
137
+ end
138
+
139
+
140
+ OLDSET = <<~EOT
141
+ _, __, ___ = nil, nil, nil
142
+ proc { |r,n|
143
+ Array === __ or __ = []
144
+ Hash === ___ or ___ = {}
145
+ unless r.nil? or r.equal? __ or r.equal? ___ then
146
+ _ = r
147
+ __.unshift r
148
+ ___[ n] = r
149
+ end
150
+ }
151
+ EOT
152
+
153
+ autoload :Etc, "etc"
154
+ autoload :Socket, "socket"
155
+
156
+ def cur_prompt prev
157
+ p = @params[ :prompt].to_s
158
+ p.gsub /%(?:
159
+ \(([^\)]+)?\)
160
+ |
161
+ ([+-]?[0-9]+(?:\.[0-9]+)?)
162
+ )?(.)/nx do
163
+ case $3
164
+ when "s" then @obj.to_s
165
+ when "i" then $1 ? (@obj.send $1) : @obj.inspect
166
+ when "n" then "%#$2d" % @n
167
+ when "t" then Time.now.strftime $1||"%X"
168
+ when "u" then Etc.getpwuid.name
169
+ when "h" then Socket.gethostname
170
+ when "w" then cwd_short
171
+ when "W" then File.basename cwd_short
172
+ when "c" then color *($1 || $2 || "").split.map { |x| x.to_i }
173
+ when ">" then prev ? "." : Process.uid == 0 ? "#" : ">"
174
+ when "%" then $3
175
+ else $&
176
+ end
177
+ end
178
+ end
179
+
180
+ def color *c
181
+ if @params[ :color] then
182
+ s = c.map { |i| "%d" % i }.join ";"
183
+ "\e[#{s}m"
184
+ end
185
+ end
186
+
187
+ def switchcolor *c
188
+ s = color *c
189
+ print s
190
+ end
191
+
192
+ def cwd_short
193
+ r = Dir.pwd
194
+ h = Etc.getpwuid.dir
195
+ r[ 0, h.length] == h and r[ 0, h.length] = "~"
196
+ r
197
+ end
198
+
199
+ def readline
200
+ r, @previous = @previous, nil
201
+ r or @n += 1
202
+ begin
203
+ cp = cur_prompt r
204
+ l = @prompt.ask cp
205
+ return if l.nil?
206
+ @prompt.push l unless !r and @params[ :histhid] and l =~ /\A[ \t]+/
207
+ if r then
208
+ r << $/ << l
209
+ else
210
+ r = l unless l.empty?
211
+ end
212
+ cp.strip!
213
+ cp.gsub! /\e\[[0-9]*(;[0-9]*)*m/, ""
214
+ @file = "#{self.class}/#{cp}"
215
+ end until r
216
+ switchcolor
217
+ r
218
+ end
219
+
220
+ # :stopdoc:
221
+ ARROW = "=> "
222
+ ELLIPSIS = "..."
223
+ # :startdoc:
224
+
225
+ def display r
226
+ return if r.nil? and not @params[ :shownil]
227
+ s = @params[ :show]
228
+ s or return
229
+ s = s.to_i rescue 0
230
+ i = ARROW.dup
231
+ i << r.inspect
232
+ if s > 0 then
233
+ siz, = $stdout.wingeom
234
+ siz *= s
235
+ if i.length > siz then
236
+ i.cut! siz-ELLIPSIS.length
237
+ i << ELLIPSIS
238
+ end
239
+ end
240
+ puts i
241
+ end
242
+
243
+ def show_exception
244
+ unless $!.to_s.empty? then
245
+ switchcolor 31, 1
246
+ print $!
247
+ print " " unless $!.to_s =~ /\s\z/
248
+ end
249
+ switchcolor 31, 22
250
+ puts "(#{$!.class})"
251
+ switchcolor 33
252
+ $@.each { |b|
253
+ r = b.starts_with? __FILE__
254
+ break if r and (b.rest r) =~ /\A:\d+:/
255
+ puts b
256
+ }
257
+ switchcolor
258
+ end
259
+
260
+ def eval_param l
261
+ eot = "EOT0001"
262
+ eot.succ! while l[ eot]
263
+ l = eval "<<#{eot}\n#{l}\n#{eot}", @binding, @file
264
+ l.strip!
265
+ l.notempty?
266
+ end
267
+
268
+ def prompt_load_history
269
+ @prompt.load_history @params[ :histfile]
270
+ end
271
+ def prompt_save_history
272
+ @prompt.save_history @params[ :histfile], @params[ :histmax]
273
+ end
274
+ def prompt_scan_history
275
+ @prompt.scan_history do |l|
276
+ next if l =~ /\A\\/
277
+ yield l
278
+ end
279
+ end
280
+
281
+
282
+
283
+ Metacmds = Struct[ :method, :summary, :description]
284
+ @metacmds = {}
285
+ class <<self
286
+ attr_reader :metacmds
287
+ private
288
+ def method_added sym
289
+ if @mcd then
290
+ names, summary, desc = *@mcd
291
+ m = Metacmds[ sym, summary, desc]
292
+ names.each { |n|
293
+ @metacmds[ n] = m
294
+ }
295
+ @mcd = nil
296
+ end
297
+ end
298
+ def metacmd names, summary, desc
299
+ @mcd = [ names, summary, desc]
300
+ end
301
+ end
302
+
303
+ def get_metacommand name
304
+ self.class.metacmds[ name] or raise Failed, "Unknown Metacommand: #{name}"
305
+ end
306
+
307
+ metacmd %w(? h help), "Help for metacommands", <<~EOT
308
+ List of Metacommands or help on a specific command, if given.
309
+ EOT
310
+ def cmd_help x
311
+ @redir.redirect_output do
312
+ if x then
313
+ mc = get_metacommand x
314
+ names = cmds_list[ mc]
315
+ puts "Metacommand: #{names.join ' '}"
316
+ puts "Summary: #{mc.summary}"
317
+ puts "Description:"
318
+ puts mc.description
319
+ else
320
+ l = cmds_list.map { |k,v| [v,k] }
321
+ puts "Metacommands:"
322
+ l.each { |names,mc|
323
+ puts " %-20s %s" % [ (names.join " "), mc.summary]
324
+ }
325
+ end
326
+ end
327
+ nil
328
+ end
329
+ def cmds_list
330
+ l = Hash.new { |h,k| h[k] = [] }
331
+ self.class.metacmds.each_pair { |k,v|
332
+ l[ v].push k
333
+ }
334
+ l
335
+ end
336
+
337
+ metacmd %w(v version), "Version information", <<~EOT
338
+ Print version number.
339
+ EOT
340
+ def cmd_version x
341
+ @redir.redirect_output do
342
+ puts "#{self.class} #{VERSION}"
343
+ VERSION
344
+ end
345
+ end
346
+
347
+ metacmd %w(q x quit exit), "Quit Intar", <<~EOT
348
+ Leave Intar.
349
+ EOT
350
+ def cmd_quit x
351
+ raise Quit
352
+ end
353
+
354
+ metacmd %w(cd), "Change directory", <<~EOT
355
+ Switch to a different working directory.
356
+ Former directories will be kept in a stack.
357
+
358
+ %[N] exchange with stack item #N
359
+ =[N] drop and set to stack item #N
360
+ -[N] drop stack item #N
361
+
362
+ Default N is 1.
363
+ EOT
364
+ def cmd_cd x
365
+ @wds ||= []
366
+ y = Dir.getwd
367
+ if x =~ /\A([%=-])?(\d+)?\z/ then
368
+ x = $2 ? (@wds.delete_at -$2.to_i) : @wds.pop
369
+ x or raise Failed, ($2 ? "No directory ##$2." : "No last directory.")
370
+ case $1
371
+ when "-" then x = nil
372
+ when "=" then y = nil
373
+ end
374
+ end
375
+ if x then
376
+ x = File.expand_path x
377
+ Dir.chdir x rescue raise Failed, $!.to_s
378
+ @wds.push y if y
379
+ y = Dir.getwd
380
+ @wds.delete y
381
+ end
382
+ @redir.redirect_output do
383
+ i = @wds.length
384
+ @wds.each { |d|
385
+ puts "%2d %s" % [ i, d]
386
+ i -= 1
387
+ }
388
+ end
389
+ y
390
+ end
391
+
392
+ metacmd %w($ env), "Set environment variable", <<~EOT
393
+ Set or display an environment variable.
394
+ EOT
395
+ def cmd_env x
396
+ if x then
397
+ cmds_split_assign x do |n,v|
398
+ if v then
399
+ v =~ /\A"((?:[^\\"]|\\.)*)"\z/ and v = ($1.gsub /\\(.)/, "\\1")
400
+ ENV[ n] = v
401
+ else
402
+ ENV[ n]
403
+ end
404
+ end
405
+ else
406
+ @redir.redirect_output do
407
+ ENV.keys.sort.each { |n|
408
+ puts "#{n}=#{ENV[ n]}"
409
+ }
410
+ nil
411
+ end
412
+ end
413
+ end
414
+ def cmds_split_assign x
415
+ n, v = x.split /\s*=\s*|\s+/, 2
416
+ yield n, v.notempty?
417
+ end
418
+
419
+ metacmd %w(! sh shell), "Run shell command", <<~EOT
420
+ Run a shell command or a subshell.
421
+ EOT
422
+ def cmd_shell x
423
+ @redir.redirect_output do
424
+ system x||ENV[ "SHELL"]||"/bin/sh" or
425
+ raise Failed, "Exit code: #{$?.exitstatus}"
426
+ end
427
+ nil
428
+ end
429
+
430
+ metacmd %w(p param), "Set parameter", <<~EOT
431
+ Set or display a parameter
432
+ EOT
433
+ def cmd_param x
434
+ if x then
435
+ cmds_split_assign x do |n,v|
436
+ if v then
437
+ @params[ n.to_sym] = parse_param v
438
+ else
439
+ @params[ n.to_sym]
440
+ end
441
+ end
442
+ else
443
+ @redir.redirect_output do
444
+ @params.keys.sort.each { |n|
445
+ puts "#{n}=#{@params[n].inspect}"
446
+ }
447
+ nil
448
+ end
449
+ end
450
+ end
451
+ def parse_param v
452
+ case v
453
+ when /\At(?:rue)?\z/i, /\Ayes\z/i, /\Aon\z/i then true
454
+ when /\Af(?:alse)?\z/i, /\Ano\z/i, /\Aoff\z/i then false
455
+ when /\A(?:nil|none|-)\z/i then nil
456
+ when /\A(?:[+-])?\d+\z/i then $&.to_i
457
+ when /\A"((?:[^\\"]|\\.)*)"\z/ then $1.gsub /\\(.)/, "\\1"
458
+ else v
459
+ end
460
+ end
461
+
462
+ metacmd %w(< i input), "Load Ruby file", <<~EOT
463
+ Load a Ruby file and eval its contents.
464
+ EOT
465
+ def cmd_input x
466
+ x or raise Failed, "No input file given."
467
+ l = File.read x rescue raise Failed, $!.to_s
468
+ @redir.redirect_output do
469
+ eval l, @binding, x
470
+ end
471
+ end
472
+
473
+ metacmd %w(> o output), "Output to file", <<~EOT
474
+ Append output to a file.
475
+ EOT
476
+ def cmd_output x
477
+ if x then
478
+ File.open x, "w" do end rescue raise Failed, "File error: #$!"
479
+ end
480
+ @params[ :output] = x
481
+ end
482
+
483
+ metacmd %w(e edit), "Edit last command", <<~EOT
484
+ Take last command line from the history and open it in an editor.
485
+ Then execute the edited line.
486
+ EOT
487
+ def cmd_edit x
488
+ fn = tempname
489
+ x = Regexp.new x if x
490
+ p = prompt_scan_history { |l| break l if not x or l =~ x }
491
+ File.open fn, "w" do |f| f.write p end
492
+ begin
493
+ system ENV[ "EDITOR"]||ENV[ "VISUAL"]||"vi", fn or
494
+ raise Failed, "Executing editor failed: #{$?.exitstatus}"
495
+ p = File.read fn
496
+ p.strip!
497
+ @prompt.push p
498
+ @redir.redirect_output do eval p, @binding, @file end
499
+ ensure
500
+ File.unlink fn
501
+ end
502
+ end
503
+ def tempname
504
+ t = Time.now.strftime "%Y%m%d-%H%M%S"
505
+ File.expand_path "intar-#{t}-#$$.rb", ENV[ "TMPDR"]||"/tmp"
506
+ end
507
+
508
+ metacmd %w(^ hist history), "Manage history", <<~EOT
509
+ l load Load history
510
+ s save Save history
511
+ / Search in history
512
+ [N [M]] Show last N items (skip M)
513
+ EOT
514
+ def cmd_history x
515
+ case x
516
+ when "l", "load" then prompt_load_history
517
+ when "s", "save" then prompt_save_history
518
+ when %r(\A(\d+)?/\s*) then search_history $1, $'
519
+ when %r(\A(\d+)(?:\s+(\d+))?\s*\z) then show_history $1.to_i, $2.to_i
520
+ when nil then show_history 5, 0
521
+ else raise Failed, "Unknown history command: #{x}"
522
+ end
523
+ end
524
+ def search_history num, pat
525
+ r = Regexp.new pat
526
+ num ||= 1
527
+ num = num.to_i.nonzero?
528
+ extract_from_history { |l|
529
+ if l =~ r then
530
+ if num then
531
+ break unless num > 0
532
+ num -= 1
533
+ end
534
+ next l
535
+ end
536
+ }
537
+ end
538
+ def show_history n, m
539
+ i, j = 0, 0
540
+ extract_from_history { |l|
541
+ i += 1
542
+ if i > m then
543
+ j += 1
544
+ break if j > n
545
+ next l
546
+ end
547
+ }
548
+ end
549
+ def extract_from_history
550
+ a = []
551
+ prompt_scan_history do |l|
552
+ n = yield l
553
+ a.push n if n
554
+ end
555
+ ensure
556
+ @redir.redirect_output do
557
+ while (p = a.pop) do
558
+ puts p
559
+ end
560
+ end
561
+ end
562
+
563
+ end
564
+
@@ -0,0 +1,128 @@
1
+ #
2
+ # intar/prompt.rb -- Prompt for Intar
3
+ #
4
+
5
+ require "supplement"
6
+ require "readline"
7
+
8
+
9
+ class Intar
10
+
11
+ class Prompt
12
+
13
+ def initialize histfile: nil, limit: nil
14
+ @limit = limit.nonzero?
15
+ Readline::HISTORY.clear
16
+ @new = 0
17
+ end
18
+
19
+ def ask prompt
20
+ l = Readline.readline prompt
21
+ rescue Interrupt
22
+ puts "^C -- #{$!.inspect}"
23
+ retry
24
+ end
25
+
26
+ def last
27
+ Readline::HISTORY[-1] unless Readline::HISTORY.empty?
28
+ end
29
+
30
+ def scan_history
31
+ i = Readline::HISTORY.length
32
+ while i > 0 do
33
+ i -= 1
34
+ l = Readline::HISTORY[i]
35
+ yield l
36
+ end
37
+ end
38
+
39
+ def push item
40
+ item.empty? and return
41
+ last != item or return
42
+ Readline::HISTORY.push item
43
+ @new += 1
44
+ end
45
+
46
+ def load_history filepath
47
+ with_filepath filepath do |p|
48
+ read_file_if p do |f|
49
+ h = []
50
+ @new.times { h.push Readline::HISTORY.pop }
51
+ Readline::HISTORY.clear
52
+ f.each_line { |l|
53
+ l.chomp!
54
+ l.sub! "\r", "\n"
55
+ Readline::HISTORY.push l
56
+ }
57
+ Readline::HISTORY.push h.pop while h.any?
58
+ end
59
+ nil
60
+ end
61
+ end
62
+
63
+ def save_history filepath, maxsize
64
+ with_filepath filepath do |p|
65
+ lock_histfile p do
66
+ old, m = [], maxsize-@new
67
+ read_file_if p do |f|
68
+ f.each_line { |l|
69
+ old.size >= m and old.shift
70
+ old.push l
71
+ }
72
+ end
73
+ File.open p, "w" do |f|
74
+ old.each { |l| f.puts l }
75
+ i = Readline::HISTORY.length - @new
76
+ while i < Readline::HISTORY.length do
77
+ l = Readline::HISTORY[ i].sub "\n", "\r"
78
+ f.puts l
79
+ i += 1
80
+ end
81
+ end
82
+ end
83
+ nil
84
+ end
85
+ end
86
+
87
+ def limit_history max
88
+ n = Readline::HISTORY.length - max
89
+ n.times { Readline::HISTORY.shift }
90
+ @new > max and @new = max
91
+ nil
92
+ end
93
+
94
+ private
95
+
96
+ def with_filepath filepath
97
+ if filepath then
98
+ p = File.expand_path filepath, "~"
99
+ yield p
100
+ end
101
+ end
102
+
103
+ def read_file_if filepath
104
+ if File.exist? filepath then
105
+ File.open filepath do |f|
106
+ yield f
107
+ end
108
+ end
109
+ end
110
+
111
+ def lock_histfile filepath
112
+ l = "#{filepath}.lock"
113
+ loop do
114
+ File.open l, File::CREAT|File::EXCL do end
115
+ break
116
+ rescue Errno::EEXIST
117
+ puts "Lockfile #{l} exists."
118
+ sleep 1
119
+ end
120
+ yield
121
+ ensure
122
+ File.unlink l
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
@@ -0,0 +1,67 @@
1
+ #
2
+ # intar/redirect.rb -- Output redirection for Intar
3
+ #
4
+
5
+ require "supplement"
6
+
7
+
8
+ class Intar
9
+
10
+ class RedirectNone
11
+ def redirect_output
12
+ yield
13
+ end
14
+ end
15
+
16
+ class Redirect
17
+ def redirect_output
18
+ out = outfile
19
+ stdin, stdout = $stdin.dup, $stdout.dup
20
+ $stdin .reopen "/dev/null"
21
+ $stdout.reopen out
22
+ yield
23
+ ensure
24
+ $stdin .reopen stdin
25
+ $stdout.reopen stdout
26
+ out.close
27
+ end
28
+ end
29
+
30
+ class RedirectPipe < Redirect
31
+ class <<self
32
+ def detect line, pager
33
+ if line.slice! /\s+\|\z/ then
34
+ new pager
35
+ end
36
+ end
37
+ end
38
+ def initialize pager
39
+ @pager = pager||ENV[ "PAGER"]||"more"
40
+ end
41
+ def outfile
42
+ IO.popen @pager.to_s, "w" rescue raise Failed, "Pipe error: #$!"
43
+ end
44
+ end
45
+
46
+ class RedirectFile < Redirect
47
+ class <<self
48
+ def detect line, outfile
49
+ if line.slice! /\s+>(>)?(\S+|"((?:[^\\"]|\\.)*)")\z/ then
50
+ p = $3 ? ($3.gsub /\\(.)/, "\\1") : $2
51
+ append = true if $1
52
+ new p, append
53
+ elsif outfile then
54
+ new outfile.to_s, true
55
+ end
56
+ end
57
+ end
58
+ def initialize path, append
59
+ @path, @append = path, append
60
+ end
61
+ def outfile
62
+ File.open @path, (@append ? "a" : "w") rescue raise Failed, "File error: #$!"
63
+ end
64
+ end
65
+
66
+ end
67
+
@@ -0,0 +1,10 @@
1
+ #
2
+ # intar/version.rb -- Version number
3
+ #
4
+
5
+ class Intar
6
+
7
+ VERSION = "2.0".freeze
8
+
9
+ end
10
+
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: intar
3
+ version: !ruby/object:Gem::Version
4
+ version: '2.0'
5
+ platform: ruby
6
+ authors:
7
+ - Bertram Scharpf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: appl
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: supplement
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ description: 'This is a lean replacement for Irb.
42
+
43
+ '
44
+ email: "<software@bertram-scharpf.de>"
45
+ executables:
46
+ - intar
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - bin/intar
51
+ - lib/intar.rb
52
+ - lib/intar/prompt.rb
53
+ - lib/intar/redirect.rb
54
+ - lib/intar/version.rb
55
+ homepage: http://www.bertram-scharpf.de
56
+ licenses:
57
+ - BSD-2-Clause
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements:
74
+ - Ruby and some small Gems; Readline
75
+ rubygems_version: 3.0.8
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Interactive Ruby
79
+ test_files: []