Ruby版cygterm

cygtermをRubyに移植してみました。TCP/IPのサーバの実装、仮想端末のサンプルとしてお手頃かと思います。
使い方。以下のプログラムをcygterm.rbwというファイル名で保存して、拡張子.rbwをcygwinのrubywコマンドに関連付けてください。あとはcygterm.rbwをダブルクリックすればOK。設定はプログラムを適当に書き換えてください。とりあえずデフォルトの場所にインストールしたTeraTermが起動するようにしています。
ファイル: cygterm.rbw

#!/usr/bin/env ruby

class Telnet
  require 'socket'

  def initialize(port_range, command, args = '')
    ssock, port = setup_ssock(port_range)
    fork do
      exec(command, *(sprintf(args, ssock.addr[2], port)).split)
    end

    raise 'timeout.' unless select([ssock], [], [], 5)
    @sock = ssock.accept
    raise 'invalid peer.' unless @sock.peeraddr[3] == '127.0.0.1'
  end
  attr_reader :sock

  def setup_ssock(port_range)
    port_range.each do |port|
      begin
        return TCPServer.open('localhost', port), port
      rescue Errno::EADDRINUSE
        # ignore
      end
    end
    raise 'available port not found.'
  end
  private :setup_ssock
end

class Shell
  require 'pty'

  def initialize(env, command, args = '')
    ENV.update(env)
    @rpty, @wpty, pid = PTY.spawn(command + args)
  end
  attr_reader :rpty, :wpty
end

if $0 == __FILE__
  telnet = Telnet.new(20000 ... 20040,
                      'C:/program files/ttermpro/ttermpro.exe', 
                      '%s %d /T=0 /KR=SJIS /KT=SJIS')
  shell = Shell.new({'TERM' => 'vt100'},
                    '/bin/zsh')
  begin
    loop do
      ios = select([telnet.sock, shell.rpty])
      next unless ios
      ios[0].each do |io|
        out = (io == shell.rpty) ? telnet.sock : shell.wpty
        out.syswrite(io.sysread(1024))
      end
    end
  rescue EOFError, PTY::ChildExited
    # ignore
  end
end

BUGS:

  • Telnetプロトコルまわりはまったく実装していません。
  • 設定ファイルまわりも実装していません。
  • シェルとターミナルエミュレータの間のデータをそのまま流しているだけなので何か問題あるかも?(改行コードとか)