=begin rd LANG=ja
IO module でコード系変換をサポートする
=end

=begin RD LANG=ja
= クラスとモジュール
=end

=begin RD LANG=ja
== CodingSystem
コード系変換
=end
class CodingSystem
=begin RD LANG=ja
=== 定数
=end
  Summary = %$Summary: coding-system conversion $.freeze
  Copyright = %$Copyleft: (c) 1999-2000 Nakada.Nobuyoshi <nobu.nokada@softhome.net> $.freeze
  RCSID = %w$Id: codeconv.rb,v 0.9 2004/05/12 09:11:54 nobu Exp $[1..-1].each {|s| s.freeze}.freeze
  Version = RCSID[1].split('.').collect {|s| s.to_i}.freeze

=begin RD LANG=ja
--- CodingSystem::Default
    デフォルトのコード系
=end
  Default = new

=begin RD LANG=ja
=== クラスメソッド
=end

=begin RD LANG=ja
--- CodingSystem.new(r = nil, w = true, i = true)
    nil =>  無変換
    true => デフォルト
    : Parameters
      * ((|r|))	読み込み用コード系(デフォルト 無変換)
      * ((|w|))	書き出し用コード系(デフォルト reading に同じ)
      * ((|i|))	内部用コード系(デフォルト $KCODE)
=end
  def initialize(r = nil, w = true, i = true)
    @reading = @writing = @internal = nil
    self.reading = r
    self.writing = w
    self.internal = i
  end

=begin RD LANG=ja
=== インスタンスメソッド
=end

=begin private LANG=ja
--- CodingSystem.check_for_internal(code)
    内部用コード名に変換する
    : Parameters
      * ((|code|))	コード名
=end
  def self.check_for_internal(code)
    case code
    when 'BINARY', :BINARY, 'B', 'b', nil, false; nil
    when 'EUC', :EUC, 'E', 'e'; 'EUC'
    when 'SJIS', :SJIS, 'S', 's'; 'SJIS'
    when 'UTF8', :UTF8, 'U', 'u'; 'UTF8'
    when true; true
    else raise "Unknown coding system: \"#{code}\""
    end
  end

=begin private LANG=ja
--- CodingSystem.check_for_external(code)
    外部用コード名に変換する
    : Parameters
      * ((|code|))	コード名
=end
  def self.check_for_external(code)
    case code
    when 'JIS', :JIS, 'J', 'j'; 'JIS'
    else check_for_internal(code)
    end
  end

=begin RD LANG=ja
--- CodingSystem#new(r = nil, w = true, i = true)
    新しいコーディングシステムセットを作る。デフォルトは(({self}))のコピー。
    nil =>  無変換
    true => デフォルト
    : Parameters
      * ((|r|))	読み込み用コード系(デフォルト 無変換)
      * ((|w|))	書き出し用コード系(デフォルト reading に同じ)
      * ((|i|))	内部用コード系(デフォルト $KCODE)
=end
  def new(r = @reading, w = @writing, i = @internal)
    self.class.new(r, w, i)
  end

=begin RD LANG=ja
--- CodingSystem#internal
    指定されている内部コード系を返す。
=end
  def internal
    return @internal if @internal != true
    $KCODE
  end

=begin RD LANG=ja
--- CodingSystem#internal=(code)
    内部コード系を指定する。
    : Parameters
      * ((|code|))	コード系名((({String})), (({Symbol}))または(({nil})))
=end
  def internal=(code)
    @internal = self.class.check_for_internal(code)
    code
  end

=begin RD LANG=ja
--- CodingSystem#reading
    指定されている読み込み用外部コード系を返す。
=end
  def reading
    return @reading if @reading != true
    $KIOCODE or $KCODE
  end

=begin RD LANG=ja
--- CodingSystem#reading=(code)
    読み込み用外部コード系を指定する。
    : Parameters
      * ((|code|))	コード系名((({String})), (({Symbol}))または(({nil})))
=end
  def reading=(code)
    @reading = self.class.check_for_external(code)
    code
  end

=begin RD LANG=ja
--- CodingSystem#writing
    指定されている書き出し用外部コード系を返す。
=end
  def writing
    return @writing if @writing != true
    reading
  end

=begin RD LANG=ja
--- CodingSystem#writing=(code)
    書き出し用外部コード系を指定する。
    : Parameters
      * ((|code|))	コード系名((({String})), (({Symbol}))または(({nil})))
=end
  def writing=(code)
    @writing = self.class.check_for_external(code)
    code
  end

=begin RD LANG=ja
--- CodingSystem#tointern(s)
    外部文字コードから内部文字コードへの変換。
    : Parameters
      * ((|s|))	変換する文字列
=end
  def tointern(s)
    s.to_s.codeconv(internal, reading)
  end

=begin RD LANG=ja
--- CodingSystem#tointerns(*args)
    外部文字コードから内部文字コードへの変換。
    : Parameters
      * ((|*args|))	変換する文字列
=end
  def tointerns(*args)
    r, i = reading, internal
    args.collect {|s| s.to_s.codeconv(i, r)}
  end

=begin RD LANG=ja
--- CodingSystem#tointern(s)
    内部文字コードから外部文字コードへの変換。
    : Parameters
      * ((|s|))	変換する文字列
=end
  def toextern(s)
    s.to_s.codeconv(writing, internal)
  end

=begin RD LANG=ja
--- CodingSystem#tointerns(*args)
    内部文字コードから外部文字コードへの変換。
    : Parameters
      * ((|*args|))	変換する文字列
=end
  def toexterns(*args)
    w, i = writing, internal
    args.filter {|s| s.to_s.codeconv(w, i)}
  end

=begin RD LANG=ja
== CodingSystem::Converter
コード系変換モジュール
=end
  module Converter
=begin RD LANG=ja
--- CodingSystem::Converter#coding_system=(codesys)
    コード系を設定する。
    : Parameters
      * ((|codesys|))	コード系
=end
    def coding_system=(codesys)
      @coding_system =
	case codesys
	when CodingSystem; codesys
	when Array; (@coding_system || CodingSystem).new(*codesys)
	else (@coding_system || CodingSystem).new(codesys)
	end
    end

=begin RD LANG=ja
--- CodingSystem::Converter#coding_system
    設定されているコード系を返す。設定されていなければ新たに作る。
=end
    def coding_system
      @coding_system ||= CodingSystem.new
    end

    append_features(IO)
  end
end

=begin RD LANG=ja
== 変更・追加されるクラス・メソッド
=end

class String
=begin RD LANG=ja
--- String#codeconv(to[, from = nil])
    コード系変換をした結果を返す。
    : Parameters
      * ((|to|))	変換先のコード系名
      * ((|from|))	変換元のコード系名
=end
  begin
    require 'nkf'
  rescue LoadError
    require 'kconv'
    # when Kconv available
    def codeconv(to, from = nil)
      return self if !to or !from or to == from
      Kconv.kconv(self, to, from)
    end
  else
    # when NKF available
    def codeconv(to, from = nil)
      return self if !to or to == from
      flag = "-#{to[0, 1].downcase}xm0"
      flag << from[0, 1].upcase if from
      NKF.nkf(flag, self)
    end
  end
end

class IO
  include CodingSystem::Converter

  alias _write write
  def write(arg)
    _write(coding_system.toextern(arg))
  end

  alias _read read
  def read(*args)
    s = _read(*args) and coding_system.tointern(s)
  end

  alias _gets gets
  def gets(*args)
    s = _gets(*args) and coding_system.tointern(s)
  end

  alias _readline readline
  def readline(*args)
    s = _readline(*args) and coding_system.tointern(s)
  end

  alias _readlines readlines
  def readlines(*args)
    coding_system.tointerns(_readlines(*args))
  end

  alias _each each
  def each(*args)
    i, r = coding_system.internal, coding_system.reading
    _each(*args) {|s| yield s.codeconv(i, r)}
  end

  alias each_line each
end

class << IO
  alias _foreach foreach
  def foreach(*args)
    i, r = CodingSystem::Default.internal, CodingSystem::Default.reading
    _foreach(*args) {|s| yield s.codeconv(i, r)}
  end

  alias _readlines readlines
  def readlines(*args)
    i, r = CodingSystem::Default.internal, CodingSystem::Default.reading
    _readlines(*args) {|s| yield s.codeconv(i, r)}
  end
end

class << $<
  include CodingSystem::Converter

  alias _read read
  def read(*args)
    s = _read(*args) and coding_system.tointern(s)
  end

  alias _gets gets
  def gets(*args)
    s = _gets(*args) and coding_system.tointern(s)
  end

  alias _readline readline
  def readline(*args)
    s = _readline(*args) and coding_system.tointern(s)
  end

  alias _readlines readlines
  def readlines(*args)
    coding_system.tointerns(_readlines(*args))
  end

  alias _each each
  def each(*args)
    i, r = coding_system.internal, coding_system.reading
    _each(*args) {|s| yield s.codeconv(i, r)}
  end

  alias each_line each
end

module Kernel
  undef print, printf, gets, readline, readlines

  def print(*args)
    $>.print(*args)
  end

  def printf(*args)
    $>.printf(*args)
  end

  def gets(*args)
    $<.gets(*args)
  end

  def readline(*args)
    $<.readline(*args)
  end

  def readlines(*args)
    $<.readlines(*args)
  end
end

STDIN.coding_system = STDOUT.coding_system = STDERR.coding_system =
  $>.coding_system = $<.coding_system = CodingSystem::Default

if $0 == __FILE__
  out = open("/dev/stdout", "w")
  out.coding_system.writing = :JIS

  while s = ARGV.shift
    out.puts s
  end
end
