服部です。

私も参加させてください。

*説明*

超低機能 NDTP(Network Dictionary Transfer Protocol) クライアント
です。

`-h' オプションで使い方を表示します。
検索文字列が「かな」か ASCII かを Kconv.guess で判別していますが、
短いかな文字列は判別できないことがあるようなので、その場合は明示
的に `-k' オプションを指定してください。

サーバとして dserver 2.2beta-pl2 および ndtpd 1.0.4 を用いて動作
確認しましたが、辞書は研究社「新英和・和英中辞典(第6/4版)」
(電子ブック版)しか使っていないので、他の辞書では問題がある可能
性大です。

---
服部  純
jhat / kw.netlaputa.or.jp

===

#!/usr/local/bin/ruby
#
#   sinc -- simple NDTP client
#
class NDTP
    require 'socket'
    require 'kconv'
    include Kconv

    def initialize(host, port = 'ndtp')
	@sock = TCPsocket.new(host, port)
	@sock.write "ANDTP\n"

	while @sock.gets != "$A\n"
	    next if /dictnum=/		# not documented, but sent by dserver
	    raise 'Authentication failed'
	end
    end

    def close
	@sock.write "Q\n"
	@sock.flush
	@sock.close
    end

    def list_dict
	@sock.write "t\n"
	dlist = []

	while @sock.gets != "$*\n"
	    next if $_ == "$I\n"
	    if /^\s*(\d+)\t([^\t]+)/
		dlist.push([$1, $2])
		next
	    end
	    raise 'Cannot get a dictionary list'
	end
	dlist
    end

    def select_dict(id)
	@sock.write "L#{id}\n"
	@sock.gets == "$*\n" or raise 'Cannot select the dictionary'
    end

    def search(word, kana = FALSE)
	cmd = if kana || [JIS, EUC, SJIS].member?(guess(word))
		  if (word = toeuc(word)) =~ /^\*/
		      'K' + word.split(//).reverse.join
		  else
		      'k' + word
		  end
	      else
		  if word =~ /^\*/
		      'A' + word.reverse
		  else
		      'a' + word
		  end
	      end
	@sock.write "P#{cmd}\n"
	@sock.gets == "$0\n" or raise 'Cannot search the word'
	hlist = []

	while (s1 = @sock.gets) != "$$\n"
	    s2 = @sock.gets
	    hlist.push([s1.chomp, s2.chomp])
	end
	hlist
    end

    def get_text(pos)
	@sock.write "S#{pos}\n"
	@sock.gets == "$1\n" or raise 'Cannot get text'
	text = ''
	text += $_ while @sock.gets != "$$\n"
	text
    end
end

def usage
    myname = File.basename($0)
    STDERR.print "\
usage: #{myname} [-s SERVER][-p PORT][-d DICT][-e ENTRY][-x NUM][-k] WORD
       #{myname} [-s SERVER][-p PORT][-d DICT][-x NUM] -l LINK
       #{myname} [-s SERVER][-p PORT] -d 0
       #{myname} [-h]
  WORD       word to search (pre/suffixed `*' matches any sequence of chars)
  -s SERVER  server name (default: $NDTPSERVER or `localhost')
  -p PORT    port No. or service name (default: `ndtp')
  -d DICT    dictionary No. (0: dictionary listing only; default: 1)
  -e ENTRY   entry No. (0: entry listing only; default: 1)
  -x NUM     expand links NUM times recursively (default: 0)
  -l LINK    specify link position(frame:offset)
  -k         force kana search
  -h         show this help
"
    exit
end

def die(*msg)
    STDERR.print msg
    exit 1
end

require 'getopts'
getopts('hk', 's:', 'p:', 'd:', 'e:', 'x:', 'l:') or die "bad option.\n"
usage  if $OPT_h
server =  $OPT_s || ENV['NDTPSERVER'] || 'localhost'
port   =  $OPT_p || 'ndtp'
dict   = ($OPT_d || '1').to_i
entry  = ($OPT_e || '1').to_i
expand = ($OPT_x || '0').to_i
link   =  $OPT_l
kana   =  $OPT_k

(ARGV[0] || link || dict == 0) or usage
ndtp = NDTP.new(server, port)
dictlist = ndtp.list_dict

if dict == 0
    dictlist.each { |d| print " #{d[0]} : #{d[1]}\n" }
    exit
end

dict.between?(1, dictlist.length) or die "bad dictionary No.\n"
ndtp.select_dict(dict)

if link
    link =~ /^[0-9A-Fa-f]+:[0-9A-Fa-f]+$/ or die "bad link position format.\n"
else
    hitlist = ndtp.search(ARGV[0], kana)
    hitlist.empty? and die "no match.\n"

    if hitlist.length > 1 || entry == 0
	i = 0
	hitlist.each { |e| printf("%2d : %s\n", i += 1, e[0]) }
	exit if entry == 0
	print "---\n"
    end

    entry.between?(1, hitlist.length) or die "bad entry No.\n"
    link = hitlist[entry-1][1]
end

text = ndtp.get_text(link)
IGNORE_LINK = /♪|\(音\)/

while (expand -= 1) >= 0
    text.gsub!(/→(.*)<([0-9A-Fa-f]+:[0-9A-Fa-f]+)>/) do |pat|
	label, link = $1, $2
	if label =~ IGNORE_LINK
	    pat
	else
	    "→#{label} " + ndtp.get_text(link).chomp.chomp
	end
    end
end

SO, SE = "\e[7m", "\e[m"
print text.gsub(/<[0-9A-Fa-f]+:[0-9A-Fa-f]+>/, SO + '\0' + SE)
ndtp.close