summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2026-02-10 08:17:06 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2026-02-10 20:07:47 +0900
commitf486ee340289b779654fe2d6c1de5a348fd76d86 (patch)
treeb7b0a6239c1a247abe732b82f76a2740191a8ea6
parent1ea75b711f9c64e32b9bc21047a5c1ce6ac0b9ae (diff)
[Feature #21872] Search script from $PATH only if no separator
-rw-r--r--ruby.c12
-rw-r--r--spec/ruby/command_line/dash_upper_s_spec.rb40
-rw-r--r--test/ruby/test_require.rb2
-rw-r--r--test/ruby/test_rubyoptions.rb33
4 files changed, 63 insertions, 24 deletions
diff --git a/ruby.c b/ruby.c
index cd5c8d1d15..3d53610c24 100644
--- a/ruby.c
+++ b/ruby.c
@@ -2301,6 +2301,16 @@ process_options_global_setup(const ruby_cmdline_options_t *opt, const rb_iseq_t
rb_exec_event_hook_script_compiled(ec, iseq, script);
}
+static bool
+has_dir_sep(const char *path)
+{
+ if (strchr(path, '/')) return true;
+#ifdef _WIN32
+ if (strchr(path, '\\')) return true;
+#endif
+ return false;
+}
+
static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
@@ -2406,7 +2416,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (!opt->script || opt->script[0] == '\0') {
opt->script = "-";
}
- else if (opt->do_search) {
+ else if (opt->do_search && !has_dir_sep(opt->script)) {
const char *path = getenv("RUBYPATH");
opt->script = 0;
diff --git a/spec/ruby/command_line/dash_upper_s_spec.rb b/spec/ruby/command_line/dash_upper_s_spec.rb
index 17991503f1..71c6016659 100644
--- a/spec/ruby/command_line/dash_upper_s_spec.rb
+++ b/spec/ruby/command_line/dash_upper_s_spec.rb
@@ -2,7 +2,8 @@ require_relative '../spec_helper'
describe 'The -S command line option' do
before :each do
- @path = [ENV['PATH'], fixture(__FILE__, "bin")].join(':')
+ @bin = fixture(__FILE__, "bin")
+ @path = [ENV['PATH'], @bin].join(File::PATH_SEPARATOR)
end
platform_is_not :windows do
@@ -10,20 +11,57 @@ describe 'The -S command line option' do
# and MRI shows warning when including world writable path in ENV['PATH'].
# This is why we are using /success$/ matching in the following cases.
+ it "runs launcher found in RUBYPATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do
+ result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'RUBYPATH' => @bin }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
it "runs launcher found in PATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do
result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'PATH' => @path }, args: '2>&1')
result.should =~ /success$/
end
+ it "runs launcher found in RUBYPATH" do
+ result = ruby_exe(nil, options: '-S launcher.rb', env: { 'RUBYPATH' => @bin }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
it "runs launcher found in PATH" do
result = ruby_exe(nil, options: '-S launcher.rb', env: { 'PATH' => @path }, args: '2>&1')
result.should =~ /success$/
end
+ it "runs launcher found in RUBYPATH and sets the exit status to 1 if it fails" do
+ result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'RUBYPATH' => @bin }, args: '2>&1', exit_status: 1)
+ result.should =~ /\bdie\b/
+ $?.exitstatus.should == 1
+ end
+
it "runs launcher found in PATH and sets the exit status to 1 if it fails" do
result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'PATH' => @path }, args: '2>&1', exit_status: 1)
result.should =~ /\bdie\b/
$?.exitstatus.should == 1
end
+
+ ruby_version_is "4.1" do
+ describe "if the script name contains separator" do
+ before(:each) do
+ @bin = File.dirname(@bin)
+ @path = [ENV['PATH'], @bin].join(File::PATH_SEPARATOR)
+ end
+
+ it "does not search launcher in RUBYPATH" do
+ result = ruby_exe(nil, options: '-S bin/launcher.rb', env: { 'RUBYPATH' => @bin }, args: '2>&1', exit_status: 1)
+ result.should =~ /LoadError/
+ $?.should_not.success?
+ end
+
+ it "does not search launcher in PATH" do
+ result = ruby_exe(nil, options: '-S bin/launcher.rb', env: { 'PATH' => @path }, args: '2>&1', exit_status: 1)
+ result.should =~ /LoadError/
+ $?.should_not.success?
+ end
+ end
+ end
end
end
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 0067a49700..eed8e97da8 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -54,7 +54,7 @@ class TestRequire < Test::Unit::TestCase
end;
begin
- assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e|
+ assert_in_out_err(["-S", "-w", (["foo"] * 1025).join("_")], "") do |r, e|
assert_equal([], r)
assert_operator(2, :<=, e.size)
assert_match(/warning: openpath: pathname too long \(ignored\)/, e.first)
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index cd2dd5d3ff..4a31f91b4a 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -446,37 +446,28 @@ class TestRubyOptions < Test::Unit::TestCase
def test_search
rubypath_orig = ENV['RUBYPATH']
path_orig = ENV['PATH']
+ libpath = (path_orig if path_orig && RbConfig::CONFIG['LIBPATHENV'] == 'PATH')
- Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
- t.puts "p 1"
- t.close
-
- @verbose = $VERBOSE
- $VERBOSE = nil
+ Dir.mktmpdir("test_ruby_test_rubyoption") do |path|
+ name = "test_rubyoption.rb"
+ parent, dir = File.split(path)
+ File.write("#{path}/#{name}", "p 1")
+ load_error = %r[#{Regexp.quote dir}/#{Regexp.quote name} \(LoadError\)]
- path, name = File.split(t.path)
-
- ENV['PATH'] = (path_orig && RbConfig::CONFIG['LIBPATHENV'] == 'PATH') ?
- [path, path_orig].join(File::PATH_SEPARATOR) : path
+ ENV['PATH'] = [path, *libpath].join(File::PATH_SEPARATOR)
assert_in_out_err(%w(-S) + [name], "", %w(1), [])
+ ENV['PATH'] = [parent, *libpath].join(File::PATH_SEPARATOR)
+ assert_in_out_err(%W(-S) + ["#{dir}/#{name}"], "", [], load_error)
ENV['PATH'] = path_orig
ENV['RUBYPATH'] = path
assert_in_out_err(%w(-S) + [name], "", %w(1), [])
- }
-
- ensure
- if rubypath_orig
+ ENV['RUBYPATH'] = parent
+ assert_in_out_err(%w(-S) + ["#{dir}/#{name}"], "", [], load_error)
+ ensure
ENV['RUBYPATH'] = rubypath_orig
- else
- ENV.delete('RUBYPATH')
- end
- if path_orig
ENV['PATH'] = path_orig
- else
- ENV.delete('PATH')
end
- $VERBOSE = @verbose
end
def test_shebang