Format of RUBYOPT

Hello all,

I'm working on fixing the remaining installer problems in the
One-Click Installer so we can finally release it, and one of those
involves the manipulation of the RUBYOPT environment variable. On that
note, I was wondering the format of that. If there are multiple
entrys, how are they formatted? Just spaces in between?

In other words, if I am to add rubygems as a new option while
preserving the older options, what would I do? Just add it with a
space prefix at the end of RUBYOPT?

In my testing there seemed to be cases when RUBYOPT was not read and I
also got errors sometimes, so I want to ensure I don't break someone's
system by setting an invalid RUBYOPT.

Thanks,
Ryan

if you look in ruby.c you'll see that it's literally parsed as part of the
commandline - except only the switchs 'IdvwrK' and 'T' are accepted. it looks
like it is indeed space separated.

   harp:~ > cat rubylib/a.rb
   A=42

   harp:~ > RUBYOPT="-ryaml -Irubylib/ -ra" ruby -e "p [YAML, A]"
   [YAML, 42]

seems to work.

cheers.

-a

···

On Tue, 25 Apr 2006, Ryan Leavengood wrote:

Hello all,

I'm working on fixing the remaining installer problems in the
One-Click Installer so we can finally release it, and one of those
involves the manipulation of the RUBYOPT environment variable. On that
note, I was wondering the format of that. If there are multiple
entrys, how are they formatted? Just spaces in between?

In other words, if I am to add rubygems as a new option while
preserving the older options, what would I do? Just add it with a
space prefix at the end of RUBYOPT?

In my testing there seemed to be cases when RUBYOPT was not read and I
also got errors sometimes, so I want to ensure I don't break someone's
system by setting an invalid RUBYOPT.

Thanks,
Ryan

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

Responding to my own thread: Given that the above is true, I wrote
Ruby script to work out the logic of this and here is the result:

$already_defined = {}

def add_to_rubyopt(var, rubyopt)
  a = rubyopt.split(' ')
  if not a.include?(var)
    a << var
    $already_defined[var] = nil
  else
    $already_defined[var] = true
  end
  a.join(' ')
end

def del_from_rubyopt(var, rubyopt)
  if not $already_defined[var]
    a = rubyopt.split(' ')
    a.delete(var)
    a.join(' ')
  else
    rubyopt
  end
end

require 'test/unit'

class RubyOptTest < Test::Unit::TestCase
  def test_add_to_rubyopt
    var = 'rubygems'
    # Before and after tests
    [
      ['', var],
      ['w', "w #{var}"],
      ['w v somethinglong', "w v somethinglong #{var}"]
    ].each do |before, after|
      assert_equal(after, add_to_rubyopt(var, before))
      assert_equal(before, del_from_rubyopt(var, after))
    end
    # Here nothing should be changed
    [var, "w #{var}", "w v #{var} somethinglong"].each do |s|
      assert_equal(s, add_to_rubyopt(var, s))
      assert_equal(s, del_from_rubyopt(var, s))
    end
  end
end
__END__

If the logic of the above seems wrong in any way, let me know. Now the
trick is writing the above in the NSIS scripting language (it's
practically like assembler compared to Ruby, grrr.)

Ryan

···

On 4/24/06, Ryan Leavengood <leavengood@gmail.com> wrote:

In other words, if I am to add rubygems as a new option while
preserving the older options, what would I do? Just add it with a
space prefix at the end of RUBYOPT?

Hmmm, OK. Then why does setting RUBYOPT to just rubygems work (it has
no dash?) That doesn't work on the command-line (at least on Windows):

C:\_Ryan\ruby_installer\svn\NSIS>ruby rubygems -e 'puts "hello"'
ruby: No such file or directory -- rubygems (LoadError)

C:\_Ryan\ruby_installer\svn\NSIS>ruby -rubygems -e 'puts "hello"'
hello

Maybe to be safe I should always just add -rubygems (i.e with a dash prefix.)

Ryan

···

On 4/24/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

if you look in ruby.c you'll see that it's literally parsed as part of the
commandline - except only the switchs 'IdvwrK' and 'T' are accepted. it looks
like it is indeed space separated.

   harp:~ > cat rubylib/a.rb
   A=42

   harp:~ > RUBYOPT="-ryaml -Irubylib/ -ra" ruby -e "p [YAML, A]"
   [YAML, 42]

seems to work.

I only recently discovered this myself... with a single option you can omit
the "-", but with multiple, the "-" is required.

Curt

···

On 4/24/06, Ryan Leavengood <leavengood@gmail.com> wrote:

On 4/24/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>
> if you look in ruby.c you'll see that it's literally parsed as part of
the
> commandline - except only the switchs 'IdvwrK' and 'T' are accepted. it
looks
> like it is indeed space separated.
>
> harp:~ > cat rubylib/a.rb
> A=42
>
> harp:~ > RUBYOPT="-ryaml -Irubylib/ -ra" ruby -e "p [YAML, A]"
> [YAML, 42]
>
> seems to work.

Hmmm, OK. Then why does setting RUBYOPT to just rubygems work (it has
no dash?) That doesn't work on the command-line (at least on Windows):

C:\_Ryan\ruby_installer\svn\NSIS>ruby rubygems -e 'puts "hello"'
ruby: No such file or directory -- rubygems (LoadError)

C:\_Ryan\ruby_installer\svn\NSIS>ruby -rubygems -e 'puts "hello"'
hello

Maybe to be safe I should always just add -rubygems (i.e with a dash
prefix.)

Ryan

ruby.c, in proc_options:

     731 if (rb_safe_level() == 0 && (s = getenv("RUBYOPT"))) {
     732 while (ISSPACE(*s)) s++;
     733 if (*s == 'T' || (*s == '-' && *(s+1) == 'T')) {
     734 int numlen;
     735 int v = 1;
     736
     737 if (*s != 'T') ++s;
     738 if (*++s) {
     739 v = scan_oct(s, 2, &numlen);
     740 if (numlen == 0) v = 1;
     741 }
     742 rb_set_safe_level(v);
     743 }

in the else block we eat the dash and any space, iff present. then we look
for the opt char

     744 else {
     745 while (s && *s) {
     746 if (*s == '-') {
     747 s++;
     748 if (ISSPACE(*s)) {
     749 do {s++;} while (ISSPACE(*s));
     750 continue;
     751 }
     752 }
     753 if (!*s) break;
     754 if (!strchr("IdvwrK", *s))
     755 rb_raise(rb_eRuntimeError, "illegal switch in RUBYOPT: -%c", *s);

now, given RUBYOPT=-rubygems, and *s==r, we call moreswitches

     756 s = moreswitches(s);
     757 }
     758 }
     759 }

so in this function

     433 static char*
     434 moreswitches(s)
     435 char *s;
     436 {
     437 int argc; char *argv[3];
     438 char *p = s;
     439
     440 argc = 2; argv[0] = argv[2] = 0;
     441 while (*s && !ISSPACE(*s))
     442 s++;
     443 argv[1] = ALLOCA_N(char, s-p+2);
     444 argv[1][0] = '-';
     445 strncpy(argv[1]+1, p, s-p);
     446 argv[1][s-p+1] = '\0';
     447 proc_options(argc, argv);
     448 while (*s && ISSPACE(*s))
     449 s++;
     450 return s;
     451 }

we eat whitespace, setup argv=[0, "-rubygems", 0] and argc == 2. then recurse
into proc_options (the first method shown).

so, as far as i can tell (and i've had three beers), proc_options finds the
'r', jumps into moreswitches which restores the '-', and the were back into
proc_options with '-rubygems', which works because of the 'ubygems' hack. the
parsing of RUBYOPT looks as if it ignores '-' or lack thereof be design, but i
certainly can't be knowing.

good luck! :wink:

-a

···

On Tue, 25 Apr 2006, Ryan Leavengood wrote:

On 4/24/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

if you look in ruby.c you'll see that it's literally parsed as part of the
commandline - except only the switchs 'IdvwrK' and 'T' are accepted. it looks
like it is indeed space separated.

   harp:~ > cat rubylib/a.rb
   A=42

   harp:~ > RUBYOPT="-ryaml -Irubylib/ -ra" ruby -e "p [YAML, A]"
   [YAML, 42]

seems to work.

Hmmm, OK. Then why does setting RUBYOPT to just rubygems work (it has
no dash?) That doesn't work on the command-line (at least on Windows):

C:\_Ryan\ruby_installer\svn\NSIS>ruby rubygems -e 'puts "hello"'
ruby: No such file or directory -- rubygems (LoadError)

C:\_Ryan\ruby_installer\svn\NSIS>ruby -rubygems -e 'puts "hello"'
hello

Maybe to be safe I should always just add -rubygems (i.e with a dash prefix.)

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

OK, so not only do I need to check for an existing RUBYOPT, but if is
defined and it only has one option, I need to add a dash to the
existing if it lacks it, then add -rubygems. Then on uninstall this
needs to be restored. Nice. I'm glad I'm prototyping this in Ruby.

Ryan

···

On 4/24/06, Curt Hibbs <ml.chibbs@gmail.com> wrote:

I only recently discovered this myself... with a single option you can omit
the "-", but with multiple, the "-" is required.

To summarize, if you're going to alter RUBYOPT by appending something,
you should append a space and then '-rrubygems', to be sure?

···

On 4/24/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Tue, 25 Apr 2006, Ryan Leavengood wrote:

> On 4/24/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>>
>> if you look in ruby.c you'll see that it's literally parsed as part of the
>> commandline - except only the switchs 'IdvwrK' and 'T' are accepted. it looks
>> like it is indeed space separated.
>>
>> harp:~ > cat rubylib/a.rb
>> A=42
>>
>> harp:~ > RUBYOPT="-ryaml -Irubylib/ -ra" ruby -e "p [YAML, A]"
>> [YAML, 42]
>>
>> seems to work.
>
> Hmmm, OK. Then why does setting RUBYOPT to just rubygems work (it has
> no dash?) That doesn't work on the command-line (at least on Windows):
>
> C:\_Ryan\ruby_installer\svn\NSIS>ruby rubygems -e 'puts "hello"'
> ruby: No such file or directory -- rubygems (LoadError)
>
> C:\_Ryan\ruby_installer\svn\NSIS>ruby -rubygems -e 'puts "hello"'
> hello
>
> Maybe to be safe I should always just add -rubygems (i.e with a dash prefix.)

ruby.c, in proc_options:

     731 if (rb_safe_level() == 0 && (s = getenv("RUBYOPT"))) {
     732 while (ISSPACE(*s)) s++;
     733 if (*s == 'T' || (*s == '-' && *(s+1) == 'T')) {
     734 int numlen;
     735 int v = 1;
     736
     737 if (*s != 'T') ++s;
     738 if (*++s) {
     739 v = scan_oct(s, 2, &numlen);
     740 if (numlen == 0) v = 1;
     741 }
     742 rb_set_safe_level(v);
     743 }

in the else block we eat the dash and any space, iff present. then we look
for the opt char

     744 else {
     745 while (s && *s) {
     746 if (*s == '-') {
     747 s++;
     748 if (ISSPACE(*s)) {
     749 do {s++;} while (ISSPACE(*s));
     750 continue;
     751 }
     752 }
     753 if (!*s) break;
     754 if (!strchr("IdvwrK", *s))
     755 rb_raise(rb_eRuntimeError, "illegal switch in RUBYOPT: -%c", *s);

now, given RUBYOPT=-rubygems, and *s==r, we call moreswitches

     756 s = moreswitches(s);
     757 }
     758 }
     759 }

so in this function

     433 static char*
     434 moreswitches(s)
     435 char *s;
     436 {
     437 int argc; char *argv[3];
     438 char *p = s;
     439
     440 argc = 2; argv[0] = argv[2] = 0;
     441 while (*s && !ISSPACE(*s))
     442 s++;
     443 argv[1] = ALLOCA_N(char, s-p+2);
     444 argv[1][0] = '-';
     445 strncpy(argv[1]+1, p, s-p);
     446 argv[1][s-p+1] = '\0';
     447 proc_options(argc, argv);
     448 while (*s && ISSPACE(*s))
     449 s++;
     450 return s;
     451 }

we eat whitespace, setup argv=[0, "-rubygems", 0] and argc == 2. then recurse
into proc_options (the first method shown).

so, as far as i can tell (and i've had three beers), proc_options finds the
'r', jumps into moreswitches which restores the '-', and the were back into
proc_options with '-rubygems', which works because of the 'ubygems' hack. the
parsing of RUBYOPT looks as if it ignores '-' or lack thereof be design, but i
certainly can't be knowing.

good luck! :wink:

-a
--

And here is the modified test:

$old_value = {}

def add_to_env(add_this, env_var_name, env_var_value, split_char=' ')
  result = env_var_value
  a = env_var_value.split(split_char)
  if not (a.include?(add_this) or a.include?("-#{add_this}"))
    $old_value[env_var_name] = env_var_value
    if a.length == 1 and a[0][0].chr != '-'
      a[0] = '-' + a[0]
    end
    a << "-#{add_this}"
    result = a.join(split_char)
  end
  result
end

def restore_env(env_var_name, env_var_value)
  if $old_value[env_var_name]
    result = $old_value[env_var_name]
    $old_value[env_var_name] = nil
    result
  else
    env_var_value
  end
end

require 'test/unit'

class RubyOptTest < Test::Unit::TestCase
  def test_add_to_rubyopt
    to_add = 'rubygems'
    env_var = 'RUBYOPT'
    # Before and after tests
    [
      ['', "-#{to_add}"],
      ['w', "-w -#{to_add}"],
      ['-w -v -somethinglong', "-w -v -somethinglong -#{to_add}"]
    ].each do |before, after|
      assert_equal(after, add_to_env(to_add, env_var, before))
      assert_equal(before, restore_env(env_var, after))
    end
    # Here nothing should be changed
    [to_add, "-w -#{to_add}", "-w -v -#{to_add} -somethinglong"].each do |s|
      assert_equal(s, add_to_env(to_add, env_var, s))
      assert_equal(s, restore_env(env_var, s))
    end
  end
end
__END__

Again if anything looks wrong let me know :slight_smile:

Ryan

···

On 4/24/06, Ryan Leavengood <leavengood@gmail.com> wrote:

OK, so not only do I need to check for an existing RUBYOPT, but if is
defined and it only has one option, I need to add a dash to the
existing if it lacks it, then add -rubygems. Then on uninstall this
needs to be restored. Nice. I'm glad I'm prototyping this in Ruby.

it looks that way to me. but caveat emptor.

-a

···

On Tue, 25 Apr 2006, Wilson Bilkovich wrote:

To summarize, if you're going to alter RUBYOPT by appending something, you
should append a space and then '-rrubygems', to be sure?

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama