Hi,

I'm a Perl-er learning ruby. I'm creating a ruby port of Perl's Net::Netrc
module for accessing ftp(1)'s .netrc file. I couldn't find an existing ruby
version anywhere.

My current code is below. I've essentially ported the Perl module's usage
semantics, although my internal implementation is quite different. I'd sure
appreciate any feedback, especially related to doing things the "ruby way",
with a view toward submitting this code to the appropriate archive site(s)
at some point.

Example usage:

  require 'net/netrc'

  n = Net::Netrc.locate('example.com')
  if (n.nil?)
    puts "No entry found"
  else
    puts "login = #{n.login}, password = #{n.password}"
  end

Questions:

1. Should I use locate() instead of new()? locate() is how the Perl
module works.

2. Should I return nil if no entry is found, or should I return an
object with the accessors all nil?

3. Should I be returning a Net::Netrc object at all, or just a simple 
Hash? Or should Net::Netrc sublcass Hash?

4. Is rasing a SecurityError appropriate? Should I create a specific
exception class instead?

------- BEGIN CODE --------

module Net

  class Netrc

    attr_accessor :machine, :login, :password, :account

    # returns name of .netrc file
    def Netrc.rcname
      # TODO: cross platform? getpwuid() for home dir?
      home = ENV['HOME']
      home ||= ENV['HOMEDRIVE'] + (ENV['HOMEPATH'] || '') if
ENV['HOMEDRIVE']
      File.join(home, '.netrc')
    end

    # opens .netrc file, returning File object if successful.
    # returns nil if .netrc not found.
    # raises SecurityError if .netrc is not owned by the current.
    # user or if it is readable or writable by other than the
    # current user.
    def Netrc.open
      name = rcname
      return nil unless File.exist?(name)
      # TODO: this stat code not applicable to Win32 (and others?)
      s = File.stat(name)
      raise SecurityError, "Not owner: #{name}" unless s.owned?
      raise SecurityError, "Bad permissions: #{name}" if s.mode & 077 != 0
      File.open(name, 'r')
    end

    # given a machine name, returns a Net::Netrc object containing
    # the matching entry for that name, or the default entry. If
    # no match is found an no default entry exists, nil is returned.
    def Netrc.locate(mach)
      f = open or return nil
      entry = nil
      key = nil
      inmacdef = false
      while line = f.gets
        if inmacdef
          inmacdef = false if line.strip.empty?
          next
        end
        toks = line.scan(/"((?:\\.|[^"])*)"|((?:\\.|\S)+)/).flatten.compact
        toks.each { |t| t.gsub!(/\\(.)/, '\1') }
        while toks.length > 0
          tok = toks.shift
          if key
            entry = new if key == 'machine' && tok == mach
            entry.send "#{key}=", tok if entry
            key = nil
          end
          case tok
          when 'default'
            return entry if entry
            entry = new
          when 'machine'
            return entry if entry
            key = 'machine'
          when 'login', 'password', 'account'
            key = tok
          end
        end
      end
      entry
    end

  end

end

------- END CODE --------

TIA for any feedback,

Bob