intar 2.0

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.
@@ -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: []