ruby-shell 3.6.14 → 3.6.16
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.
- checksums.yaml +4 -4
- data/bin/rsh +41 -21
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e133cdb364d88a5e82942a136ffd6a284105553a5e367bc5415e4623b1715ef
|
|
4
|
+
data.tar.gz: e712cf81e8ffccebadfb579272892d54617da2e59e53a95fab02b5e6766a5a9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3d81facd0b334b13a4112a17c02aa43e0364844d1405d45e3d989ef2122d24ea0dad9ebf80a86bfa1840fd0d1f1e56c2cddf1fe0c01590aa9feb8df764bc88f
|
|
7
|
+
data.tar.gz: 7cf717175c6319230ab76586a02d065196e41a51b0e429a26d16bd5dd37ef3c037262a89f59697b4c31b7ad149664e32d8c19cbc9ff6d5581fa6d378463ed421
|
data/bin/rsh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Web_site: http://isene.com/
|
|
9
9
|
# Github: https://github.com/isene/rsh
|
|
10
10
|
# License: Public domain
|
|
11
|
-
@version = "3.6.
|
|
11
|
+
@version = "3.6.15" # Fix config persistence bugs in .rshrc
|
|
12
12
|
|
|
13
13
|
# MODULES, CLASSES AND EXTENSIONS
|
|
14
14
|
class String # Add coloring to strings (with escaping for Readline)
|
|
@@ -162,6 +162,7 @@ begin # Initialization
|
|
|
162
162
|
@history_dedup = 'smart' # History dedup mode: 'off', 'full', 'smart'
|
|
163
163
|
@auto_correct = false # Auto-correct typos (default: off)
|
|
164
164
|
@slow_command_threshold = 0 # Threshold for slow command alerts (0 = disabled)
|
|
165
|
+
@config_dirty = [] # Track which config vars were changed via :config
|
|
165
166
|
@plugin_dir = Dir.home + '/.rsh/plugins' # Plugins directory
|
|
166
167
|
@plugins = [] # Loaded plugin instances
|
|
167
168
|
@plugin_enabled = [] # List of enabled plugin names (whitelist)
|
|
@@ -429,7 +430,7 @@ def getstr # A custom Readline-like function
|
|
|
429
430
|
when 'C-G' # Ctrl-G opens command in $EDITOR
|
|
430
431
|
temp_file = "/tmp/rsh_edit_#{Process.pid}.tmp"
|
|
431
432
|
File.write(temp_file, @history[0] || "")
|
|
432
|
-
system(
|
|
433
|
+
system(ENV['EDITOR'] || 'vi', temp_file)
|
|
433
434
|
if File.exist?(temp_file)
|
|
434
435
|
edited = File.read(temp_file).strip
|
|
435
436
|
# Convert multi-line to single line with proper separators
|
|
@@ -561,7 +562,7 @@ def getstr # A custom Readline-like function
|
|
|
561
562
|
end
|
|
562
563
|
lift = true
|
|
563
564
|
when 'C-Y' # Copy command line to primary selection
|
|
564
|
-
|
|
565
|
+
IO.popen('xclip', 'w') { |io| io.write(@history[0]) }
|
|
565
566
|
puts "\n#{Time.now.strftime("%H:%M:%S")}: Copied to primary selection (paste with middle buttoni)".c(@c_stamp)
|
|
566
567
|
when 'C-Z' # Suspend current process (background job)
|
|
567
568
|
if @current_pid
|
|
@@ -968,7 +969,7 @@ def tab(type)
|
|
|
968
969
|
begin
|
|
969
970
|
count = Dir.entries(clean_item).length - 2
|
|
970
971
|
print " [#{count}]".c(244)
|
|
971
|
-
rescue
|
|
972
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
972
973
|
end
|
|
973
974
|
else
|
|
974
975
|
size = File.size(clean_item)
|
|
@@ -1004,7 +1005,7 @@ def tab(type)
|
|
|
1004
1005
|
begin
|
|
1005
1006
|
count = Dir.entries(clean_item).length - 2
|
|
1006
1007
|
print " [#{count}]".c(244)
|
|
1007
|
-
rescue
|
|
1008
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
1008
1009
|
end
|
|
1009
1010
|
else
|
|
1010
1011
|
size = File.size(clean_item)
|
|
@@ -1088,6 +1089,7 @@ def tabend
|
|
|
1088
1089
|
@c.col(@c_col)
|
|
1089
1090
|
end
|
|
1090
1091
|
def get_command_switches(command) # Helper function to extract switches from --help
|
|
1092
|
+
require 'shellwords' unless defined?(Shellwords)
|
|
1091
1093
|
# Check cache first (cache expires after 1 hour)
|
|
1092
1094
|
cache_key = command.to_s.strip
|
|
1093
1095
|
return [] if cache_key.empty?
|
|
@@ -1098,9 +1100,10 @@ def get_command_switches(command) # Helper function to extract switches from --h
|
|
|
1098
1100
|
end
|
|
1099
1101
|
|
|
1100
1102
|
# Parse --help output
|
|
1101
|
-
|
|
1103
|
+
escaped = Shellwords.escape(command)
|
|
1104
|
+
hlp = `#{escaped} --help 2>/dev/null`
|
|
1102
1105
|
# Try -h if --help didn't work
|
|
1103
|
-
hlp = `#{
|
|
1106
|
+
hlp = `#{escaped} -h 2>/dev/null` if hlp.empty?
|
|
1104
1107
|
return [] if hlp.empty?
|
|
1105
1108
|
|
|
1106
1109
|
switches = []
|
|
@@ -1242,6 +1245,7 @@ def config(*args) # Configure rsh settings
|
|
|
1242
1245
|
when 'history_dedup'
|
|
1243
1246
|
if %w[off full smart].include?(value)
|
|
1244
1247
|
@history_dedup = value
|
|
1248
|
+
@config_dirty << '@history_dedup'
|
|
1245
1249
|
puts "History deduplication set to '#{value}'"
|
|
1246
1250
|
rshrc
|
|
1247
1251
|
else
|
|
@@ -1249,30 +1253,37 @@ def config(*args) # Configure rsh settings
|
|
|
1249
1253
|
end
|
|
1250
1254
|
when 'session_autosave'
|
|
1251
1255
|
@session_autosave = value.to_i
|
|
1256
|
+
@config_dirty << '@session_autosave'
|
|
1252
1257
|
puts "Session auto-save set to #{value}s #{value.to_i > 0 ? '(enabled)' : '(disabled)'}"
|
|
1253
1258
|
rshrc
|
|
1254
1259
|
when 'auto_correct'
|
|
1255
1260
|
@auto_correct = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1261
|
+
@config_dirty << '@auto_correct'
|
|
1256
1262
|
puts "Auto-correct #{@auto_correct ? 'enabled' : 'disabled'}"
|
|
1257
1263
|
rshrc
|
|
1258
1264
|
when 'slow_command_threshold'
|
|
1259
1265
|
@slow_command_threshold = value.to_i
|
|
1266
|
+
@config_dirty << '@slow_command_threshold'
|
|
1260
1267
|
puts "Slow command threshold set to #{value}s #{value.to_i > 0 ? '(enabled)' : '(disabled)'}"
|
|
1261
1268
|
rshrc
|
|
1262
1269
|
when 'completion_learning'
|
|
1263
1270
|
@completion_learning = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1271
|
+
@config_dirty << '@completion_learning'
|
|
1264
1272
|
puts "Completion learning #{@completion_learning ? 'enabled' : 'disabled'}"
|
|
1265
1273
|
rshrc
|
|
1266
1274
|
when 'completion_limit'
|
|
1267
1275
|
@completion_limit = value.to_i
|
|
1276
|
+
@config_dirty << '@completion_limit'
|
|
1268
1277
|
puts "Completion limit set to #{value}"
|
|
1269
1278
|
rshrc
|
|
1270
1279
|
when 'completion_show_metadata'
|
|
1271
1280
|
@completion_show_metadata = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1281
|
+
@config_dirty << '@completion_show_metadata'
|
|
1272
1282
|
puts "Completion metadata display #{@completion_show_metadata ? 'enabled' : 'disabled'}"
|
|
1273
1283
|
rshrc
|
|
1274
1284
|
when 'show_tips'
|
|
1275
1285
|
@show_tips = %w[on true yes 1].include?(value.to_s.downcase)
|
|
1286
|
+
@config_dirty << '@show_tips'
|
|
1276
1287
|
puts "Startup tips #{@show_tips ? 'enabled' : 'disabled'}"
|
|
1277
1288
|
rshrc
|
|
1278
1289
|
else
|
|
@@ -1432,7 +1443,7 @@ def format_tab_item(item, show_metadata: false) # Format tab item with color and
|
|
|
1432
1443
|
begin
|
|
1433
1444
|
count = Dir.entries(clean_name).length - 2 # Exclude . and ..
|
|
1434
1445
|
meta = "[dir, #{count} items]"
|
|
1435
|
-
rescue
|
|
1446
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
1436
1447
|
meta = "[dir]"
|
|
1437
1448
|
end
|
|
1438
1449
|
else
|
|
@@ -1511,7 +1522,7 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1511
1522
|
begin
|
|
1512
1523
|
file_nicks = eval($1)
|
|
1513
1524
|
@nick = file_nicks.merge(@nick) if file_nicks.is_a?(Hash)
|
|
1514
|
-
rescue
|
|
1525
|
+
rescue SyntaxError, StandardError
|
|
1515
1526
|
# If eval fails, keep current @nick
|
|
1516
1527
|
end
|
|
1517
1528
|
end
|
|
@@ -1521,7 +1532,7 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1521
1532
|
begin
|
|
1522
1533
|
file_gnicks = eval($1)
|
|
1523
1534
|
@gnick = file_gnicks.merge(@gnick) if file_gnicks.is_a?(Hash)
|
|
1524
|
-
rescue
|
|
1535
|
+
rescue SyntaxError, StandardError
|
|
1525
1536
|
# If eval fails, keep current @gnick
|
|
1526
1537
|
end
|
|
1527
1538
|
end
|
|
@@ -1529,21 +1540,29 @@ def rshrc # Write user configuration to .rshrc (portable between machines)
|
|
|
1529
1540
|
conf = ""
|
|
1530
1541
|
end
|
|
1531
1542
|
|
|
1532
|
-
#
|
|
1543
|
+
# Strip runtime data that belongs in .rshstate (may linger from pre-split config)
|
|
1544
|
+
%w[@cmd_frequency @cmd_stats @exe_cache @exe_cache_path @exe_cache_time
|
|
1545
|
+
@completion_weights @history @recordings @plugin_enabled].each do |var|
|
|
1546
|
+
conf.sub!(/^#{Regexp.escape(var)} =.*\n?/, "")
|
|
1547
|
+
end
|
|
1548
|
+
|
|
1549
|
+
# Persist runtime data (nicks, bookmarks, etc. change during normal use)
|
|
1533
1550
|
conf = persist_var(conf, '@nick', @nick)
|
|
1534
1551
|
conf = persist_var(conf, '@gnick', @gnick)
|
|
1535
1552
|
conf = persist_var(conf, '@bookmarks', @bookmarks, !@bookmarks.empty?)
|
|
1536
1553
|
conf = persist_var(conf, '@defuns', @defuns, !@defuns.empty?)
|
|
1537
|
-
conf = persist_var(conf, '@history_dedup', @history_dedup, @history_dedup && @history_dedup != 'smart')
|
|
1538
|
-
conf = persist_var(conf, '@session_autosave', @session_autosave, @session_autosave && @session_autosave > 0)
|
|
1539
|
-
conf = persist_var(conf, '@auto_correct', @auto_correct, @auto_correct)
|
|
1540
|
-
conf = persist_var(conf, '@slow_command_threshold', @slow_command_threshold, @slow_command_threshold && @slow_command_threshold > 0)
|
|
1541
|
-
conf = persist_var(conf, '@completion_learning', @completion_learning, !@completion_learning)
|
|
1542
|
-
conf = persist_var(conf, '@show_tips', @show_tips, !@show_tips)
|
|
1543
|
-
conf = persist_var(conf, '@completion_show_metadata', @completion_show_metadata, @completion_show_metadata)
|
|
1544
1554
|
conf = persist_var(conf, '@plugin_disabled', @plugin_disabled, !@plugin_disabled.empty?)
|
|
1545
1555
|
conf = persist_var(conf, '@validation_rules', @validation_rules, !@validation_rules.empty?)
|
|
1546
1556
|
|
|
1557
|
+
# Only persist config vars that were explicitly changed via :config in this session
|
|
1558
|
+
conf = persist_var(conf, '@history_dedup', @history_dedup, @config_dirty.include?('@history_dedup'))
|
|
1559
|
+
conf = persist_var(conf, '@session_autosave', @session_autosave, @config_dirty.include?('@session_autosave'))
|
|
1560
|
+
conf = persist_var(conf, '@auto_correct', @auto_correct, @config_dirty.include?('@auto_correct'))
|
|
1561
|
+
conf = persist_var(conf, '@slow_command_threshold', @slow_command_threshold, @config_dirty.include?('@slow_command_threshold'))
|
|
1562
|
+
conf = persist_var(conf, '@completion_learning', @completion_learning, @config_dirty.include?('@completion_learning'))
|
|
1563
|
+
conf = persist_var(conf, '@show_tips', @show_tips, @config_dirty.include?('@show_tips'))
|
|
1564
|
+
conf = persist_var(conf, '@completion_show_metadata', @completion_show_metadata, @config_dirty.include?('@completion_show_metadata'))
|
|
1565
|
+
|
|
1547
1566
|
File.write(Dir.home+'/.rshrc', conf)
|
|
1548
1567
|
rshstate # Also save runtime state
|
|
1549
1568
|
end
|
|
@@ -3844,12 +3863,13 @@ loop do
|
|
|
3844
3863
|
puts "#{Time.now.strftime("%H:%M:%S")}: #{@cmd}".c(@c_stamp)
|
|
3845
3864
|
# Only auto-open files if it's a single filename (no spaces = no command with args)
|
|
3846
3865
|
if File.read(@cmd).force_encoding("UTF-8").valid_encoding?
|
|
3847
|
-
system(
|
|
3866
|
+
system(ENV['EDITOR'] || 'vi', @cmd) # Try open with user's editor
|
|
3848
3867
|
else
|
|
3868
|
+
require 'shellwords' unless defined?(Shellwords)
|
|
3849
3869
|
if @runmailcap
|
|
3850
|
-
Thread.new { system("run-mailcap #{@cmd} 2>/dev/null") }
|
|
3870
|
+
Thread.new { system("run-mailcap #{Shellwords.escape(@cmd)} 2>/dev/null") }
|
|
3851
3871
|
else
|
|
3852
|
-
Thread.new { system("xdg-open #{@cmd} 2>/dev/null") }
|
|
3872
|
+
Thread.new { system("xdg-open #{Shellwords.escape(@cmd)} 2>/dev/null") }
|
|
3853
3873
|
end
|
|
3854
3874
|
end
|
|
3855
3875
|
else
|
metadata
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-shell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.6.
|
|
4
|
+
version: 3.6.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
|
|
14
14
|
history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
|
|
15
|
-
v3.6.
|
|
15
|
+
v3.6.16: Security hardening, fix shell injection in xclip/editor/mailcap calls,
|
|
16
|
+
narrow bare rescues.'
|
|
16
17
|
email: g@isene.com
|
|
17
18
|
executables:
|
|
18
19
|
- rsh
|