#!/usr/bin/ruby -Ke
#  タブ区切ファイルの閲覧 2002.10.24 by tokuhisa
#                         Time-stamp: "2003-04-15 11:29:27 tokuhisa"


class Sheet
  def Sheet.manual(mode=0)
    $stderr.puts '   コマンド：'
    $stderr.puts '       cuff数,数                指定列の内容を指定行ごとにおりかえして次の列に並べる'
    $stderr.puts '       grep数,パターン          指定列に注目してgrep'
    $stderr.puts '       gsub数,パターン-->文字列 指定列のパターンを文字列に置換'
    $stderr.puts '       del数,パターン           指定列のパターンを消去'
    $stderr.puts '       split数,パターン         指定列をパターンで分割'
    $stderr.puts '       p                        すべて表示'
    $stderr.puts '       pl数                     指定行のみ表示'
    $stderr.puts '       pc数[,数…]              指定列のみ表示(数の代りにA-Zも可)'
    $stderr.puts '       sort数                   指定列に注目しソート'
    $stderr.puts '       sort数,len               指定列に注目し長さでソート'
    $stderr.puts '       sort数,rev               指定列の文字列を左右反転しABC順でソート'
    $stderr.puts '       uniq数                   指定列に注目してユニーク'
    if( mode != 0 )
        $stderr.puts '       m                        このマニュアルを表示'
        $stderr.puts '       wファイル名              ファイルに出力'
        $stderr.puts '       q                        終了'
    end
  end

  def Sheet.analyze_command(line)
    if    /^grep(\d+)\,(.+)$/ =~ line
      com = ["key_grep",$1.to_i,$2]
    elsif /^gsub(\d+)\,(\S+)-->(\S+)$/ =~ line
      com = ["key_gsub",$1.to_i,$2,$3]
    elsif /^del(\d+)\,(\S+)$/ =~ line
      com = ["key_del",$1.to_i,$2]
    elsif /^split(\d+)\,(.+)$/ =~ line
      com = ["key_split",$1.to_i,$2]
    elsif /^p$/ =~ line
      com = ["show"]
    elsif /^pl(\d+)/ =~ line
      com = ["showl",$1.to_i]
    elsif /^pc([\d\,]+)/ =~ line
      com = ["showc",$1.split(",").collect{|elem| elem.to_i}]
    elsif /^pc([A-Z\,]+)/ =~ line
      numlist=$1.gsub(/[A-Z]/){|elem| elem[0] - 'A'[0]}.split(",").collect{|elem| elem.to_i}
      com = ["showc",numlist]
    elsif /^sort(\d+)/ =~ line
      com = ["key_sort",$1.to_i]
      if /\,(\S+)/ =~ $'
          com << $1
      else
	  com << "abc"
      end
    elsif /^sort([A-Z]+)/ =~ line
      num=$1.gsub(/[A-Z]/){|elem| elem[0] - 'A'[0]}.to_i
      com = ["key_sort",num]
      if /\,(\S+)/ =~ $'
          com << $1
      else
	  com << "abc"
      end

    elsif /^uniq([\d\,]+)/ =~ line
      com = ["key_uniq",$1.split(",").collect{|elem| elem.to_i}]
    elsif /^cuff(\d),(\d+)/ =~ line
      com = ["key_cuff",$1.to_i,$2.to_i]

    elsif /^m$/ =~ line
      com = ["manual"]
    elsif /^w(\S+)$/ =~ line
      com = ["write",$1]
    elsif /^q$/ =~ line
      com = ["quit"]
    else
      com = [nil]
    end
    return com
  end

  @@separater = /\t/

  def Sheet.separater(s)
    if s == 'space'
        @@separater = /\s+/
    elsif s == 'semicollon'
	@@separater = /;/
    else
        @@separater = /\t/
    end
  end

  @@ignorechar = ''
  def Sheet.ignorechar(c)
    @@ignorechar = c
  end

  attr_accessor :@sheet

  def initialize(input=$stdin)
    @sheet = []
    if @@ignorechar != '' then
	input.each{|line|
	    @sheet << line.chomp.gsub(@@ignorechar,'').split(@@separater)
	}
    else
	input.each{|line|
	    @sheet << line.chomp.split(@@separater)
	}
    end
  end

  def show(stream=$stdout)
    @sheet.each{ |line|
	stream.puts line.join("\t")
    }
  end

  def line( no )
    return @sheet[no]
  end

  def showline( line )
    puts @sheet[line].join("\t")
  end

  def showcolumn( columns )
    @sheet.each{ |line|
	line2 = []
	columns.each{|column|
	    line2 << line[column]
	}
 	line3 = line2.join("\t")
	if !(line3 =~ /^\s*$/) then
	    puts line3
	end
    }
  end



  def key_sort!( column=0, type="abc" )
    buffer = []
    @sheet.size.times{ |i|
       if /^len/ =~ type
           buffer << [@sheet[i][column].length, @sheet[i]]
       elsif /^rev/ =~ type
           buffer << [@sheet[i][column].reverse, @sheet[i]]
       else
           buffer << [@sheet[i][column], @sheet[i]]
       end
    }
    buffer.sort!
    @sheet = []
    buffer.size.times{ |i|
        @sheet << buffer[i][1]
    }
  end

  def key_uniq!( columns )
    unknown = Hash.new(true)
    size = @sheet.size
    newsheet = []
    for i in 0...size do
	keyword = ''
	columns.each{|column|
	   keyword += @sheet[i][column]
	}
	if unknown[keyword]
	    unknown[keyword] = false
	    newsheet << @sheet[i]
        end
    end
    @sheet = newsheet
  end

  def key_grep( column, pattern )
    newsheet = []
    p = Regexp.new(pattern)
    for i in 0...@sheet.size do
        if p =~ @sheet[i][column]
	    tmp = []
	    @sheet[i].each{|cell|
		tmp << cell
	    }
	    newsheet << tmp
        end
    end
    return newsheet
  end
  def key_grep!( column, pattern )
    @sheet = self.key_grep( column, pattern )
  end

  def key_find( column, pattern )
    p = Regexp.new(pattern)
    for i in 0...@sheet.size do
        if p =~ @sheet[i][column]
	     return i
        end
    end
    return nil
  end

  def key_gsub( column, pattern, str )
    newsheet = []
    p = Regexp.new(pattern)
    for i in 0...@sheet.size do
        if p =~ @sheet[i][column]
	    tmp = @sheet[i][0...column]
	    tmp << @sheet[i][column].gsub(p,str)
	    tmp += @sheet[i][column+1..@sheet[i].size]
            newsheet << tmp
        else
	    newsheet << @sheet[i]
	end
    end
    return newsheet
  end
  def key_gsub!( column, pattern, str )
    @sheet = self.key_gsub( column, pattern, str )
  end

  def key_del( column, pattern )
    return self.key_gsub( column, pattern, "" )
  end
  def key_del!( column, pattern )
    @sheet = self.key_gsub( column, pattern, "" )
  end

  def key_split( column, pattern )
    newsheet = []
    p = Regexp.new(pattern)
    for i in 0...@sheet.size do
        if p =~ @sheet[i][column]
	    tmp = @sheet[i][0...column]
	    tmp += @sheet[i][column].split(p)
	    tmp += @sheet[i][column+1..@sheet[i].size]
            newsheet << tmp
        else
	    newsheet << @sheet[i]
	end
    end
    return newsheet
  end
  def key_split!( column, pattern )
    @sheet = self.key_split( column, pattern )
  end

  def key_cuff( column, linenum )
      newsheet = []
      for i in 0...@sheet.size do
          if newsheet[i % linenum] == nil then
	      newsheet[i % linenum] = [@sheet[i][column]]
	  else
              newsheet[i % linenum] += [@sheet[i][column]]
          end
      end
      return newsheet
  end
  def key_cuff!( column, linenum )
    @sheet = self.key_cuff( column, linenum )
  end
end

if __FILE__==$0 then

  def print_help
	  $stderr.puts 'csv ：タブ区切ファイルの閲覧'
	  $stderr.puts 'csv [-オプション] [コマンド]*'
	  $stderr.puts '   オプション：-f ファイル名    入力ファイル指定'
	  $stderr.puts '               -i               対話入力モード'
	  $stderr.puts '               -s               スペースも区切り文字とみなす'
	  $stderr.puts '               -sc              セミコロンを区切り文字とみなす'
	  $stderr.puts '               -ih              ヘッダ行の無視'
	  $stderr.puts '               -idc             ダブルコーテーションの無視'
	  Sheet::manual
  end

  input = $stdin
  com = [nil]
  commandlist = []
  interactive = false
  nextisloadfile = false
  ignoreheaderline = false
  ARGV.size.times{|i|
      if    /^-f(\S+)$/ =~ ARGV[i]
	input = File::open($1,'r')
      elsif /^-f$/ =~ ARGV[i]
	nextisloadfile = true
      elsif nextisloadfile
	input = File::open(ARGV[i],'r')
	nextisloadfile = false
      elsif /^-h$/ =~ ARGV[i]
        print_help
	exit
      elsif /^-i$/ =~ ARGV[i]
        interactive = true
      elsif /^-s$/ =~ ARGV[i]
        Sheet.separater('space')
      elsif /^-sc$/ =~ ARGV[i]
	Sheet.separater('semicollon')
      elsif /^-ih$/ =~ ARGV[i]
	ignoreheaderline = true
      elsif /^-idc$/ =~ ARGV[i]
	Sheet.ignorechar('"')
      else
        com = Sheet::analyze_command(ARGV[i])
	commandlist << com
      end
  }

  if input == nil
    $stderr.puts 'ファイルがありません。'
    exit
  end

  if ignoreheaderline then
    input.gets
  end

  sheet = Sheet.new(input)

  begin
    if !commandlist.empty?
        com = commandlist.shift
    elsif interactive
        print "> "
	if $stdin.eof?
	    exit
	end
	keyin = $stdin.gets.chomp.strip
        com = Sheet::analyze_command(keyin)
    end
    case com[0]
    when "show"
      sheet.show
    when "showl"
      sheet.showline(com[1])
    when "showc"
      sheet.showcolumn(com[1])
    when "key_sort"
      sheet.key_sort!(com[1],com[2])
    when "key_uniq"
      sheet.key_uniq!(com[1])
    when "key_grep"
      sheet.key_grep!(com[1],com[2])
    when "key_gsub"
      sheet.key_gsub!(com[1],com[2],com[3])
    when "key_del"
      sheet.key_del!(com[1],com[2])
    when "key_split"
      sheet.key_split!(com[1],com[2])
    when "key_cuff"
      sheet.key_cuff!(com[1],com[2])
    when "manual"
      Sheet::manual
    when "write"
      fp = File::open(com[1],'w')
      sheet.show(fp)
      fp.close
    when "quit"
      interactive = false
    end
  end while interactive || !commandlist.empty?
end
