#!/usr/bin/ruby -w
#
# rdict - a DICT protocol client (see RFC 2229)
#
# $Id: rdict,v 1.30 2007/05/20 00:01:36 ianmacd Exp $
#
# Version : 0.9.4
# Author : Ian Macdonald <ian@caliban.org>
#
# Copyright (C) 2002-2007 Ian Macdonald
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
=begin
= NAME
rdict - a DICT protocol client
= SYNOPSIS
rdict [-h|--host server] [-p|--port service] [-d|--database dbname]
[-m|--match] [-s|--strategy strategy] [-C|--nocorrect]
[-D|--dbs] [-S|--strats] [-H|--serverhelp] [-i|--info dbname]
[-I|--serverinfo] [-T|--status] [-b|--debug] [-u|--user user]
[-k|--key key] [-v|--verbose] word1 [word2 ... wordn]
rdict [--help|-v|--version]
= DESCRIPTION
rdict is an ((<RFC 2229|URL:ftp://ftp.isi.edu/in-notes/rfc2229.txt>)) compliant
Dictionary Server Protocol (DICT) client that provides access to dictionary
definitions from a set of natural language dictionary databases.
= OPTIONS
: -h ((*server*)) or --host ((*server*))
Specifies the hostname for the DICT server. If no servers are specified,
the default behavior is to try dict.org, followed by alt0.dict.org.
: -p ((*port*)) or --port ((*port*))
Specifies the port (e.g. 2628) or service (e.g. dict) for connections.
The default is 2628, as specified in the DICT Protocol RFC.
: -d ((*dbname*)) or --database ((*dbname*))
Specifies a specific database to search. The default is to search all
databases (a '*' from the DICT protocol). Note that a '!' in the DICT
protocol means to search all of the databases until a match is found, and
then stop searching.
: -m or --match
Instead of printing a definition, perform a match using the specified
strategy.
: -s ((*strategy*)) or --strategy ((*strategy*))
Specify a matching strategy. By default, the server default match strategy
is used. This is usually 'exact' for definitions, and a server-defined
optimal spelling correction strategy for matches ('.' from the DICT
protocol). The available strategies are dependent on the server
implementation. For a list of available strategies, see the -S or --strats
option.
: -C or --nocorrect
Usually, if a definition is requested and the word cannot be found, spelling
correction is requested from the server, and a list of possible words are
provided. This option disables the generation of this list.
: -D or --dbs
Query the server and display a list of available databases.
: -S or --strats
Query the server and display a list of available search strategies.
: -H or --serverhelp
Query the server and display the help information that it provides.
: -i ((*dbname*)) or --info ((*dbname*))
Request information on the specified database (usually the server will
provide origination, descriptive or other information about the database or
its contents).
: -I or --serverinfo
Query the server and display information about the server.
: -T or --status
Query the server for status information.
: -u ((*user*)) or --user ((*user*))
Specifies the username for authentication.
: -k ((*key*)) or --key ((*key*))
Specifies the shared secret for authentication.
: -V or --version
Display version information.
: --help
Display help information.
: -v or --verbose
Be verbose.
: -b or --debug
Display debugging information. This is long-winded, as the entire protocol
exchange will be dumped.
= EXAMPLES
: $ rdict -D
This will provide you with a list of databases you can query.
: $ rdict -S
This will provide you with a list of strategies you can employ to match
words.
: $ rdict -m -s prefix foo
This shows you a list of all words that begin with 'foo' in all of the
databases.
: $ rdict -s re '^(cu|ke)rb$'
This shows you all the definitions relating to both 'curb' and 'kerb' from
all the databases. The 're' strategy allows regular expression matching.
: $ rdict -m -s suffix fix
This shows a list of all words that end in 'fix' in all of the databases.
: $ rdict -d jargon -m -s prefix ''
This displays a list of all the entries in the 'jargon' database.
= AUTHOR
Written by Ian Macdonald <ian@caliban.org>
= COPYRIGHT
Copyright (C) 2002-2007 Ian Macdonald
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
= SEE ALSO
* ((<"Ruby/DICT home page - http://www.caliban.org/ruby/"|URL:http://www.caliban.org/ruby/>))
* ((<dict(3)>))
* ((<"The DICT development group - http://www.dict.org/"|URL:http://www.dict.org/>))
* ((<"RFC 2229 - ftp://ftp.isi.edu/in-notes/rfc2229.txt"|URL:ftp://ftp.isi.edu/in-notes/rfc2229.txt>))
= BUGS
* MIME is not implemented
* command pipelining is not implemented
* URL parsing is not implemented
=end
require 'dict'
require 'getoptlong'
DEFAULT_SERVERS = %w(dict.org alt0.dict.org)
DEFAULT_STRATEGY = 'exact'
PROGRAM_NAME = File::basename($0)
PROGRAM_VERSION = '0.9.4'
class Optlist
attr_reader :correct, :database, :dbs, :debug, :host, :key, :info, :match,
:port, :serverhelp, :serverinfo, :status, :strategy, :strats,
:user, :match_strategy, :verbose
def initialize
begin
@correct = true
@database = DICT::ALL_DATABASES
@dbs = false
@debug = false
@host = DEFAULT_SERVERS
@key = nil
@info = nil
@match = false
@match_strategy = DICT::DEFAULT_MATCH_STRATEGY
@port = DICT::DEFAULT_PORT
@serverhelp = false
@serverinfo = false
@status = false
@strategy = DEFAULT_STRATEGY
@strats = false
@user = nil
@verbose = false
opt = GetoptLong.new(
[ '--debug', '-b', GetoptLong::NO_ARGUMENT ],
[ '--database', '-d', GetoptLong::REQUIRED_ARGUMENT ],
[ '--dbs', '-D', GetoptLong::NO_ARGUMENT ],
[ '--help', GetoptLong::NO_ARGUMENT ],
[ '--host', '-h', GetoptLong::REQUIRED_ARGUMENT ],
[ '--info', '-i', GetoptLong::REQUIRED_ARGUMENT ],
[ '--key', '-k', GetoptLong::REQUIRED_ARGUMENT ],
[ '--match', '-m', GetoptLong::NO_ARGUMENT ],
[ '--nocorrect', '-C', GetoptLong::NO_ARGUMENT ],
[ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
[ '--serverhelp', '-H', GetoptLong::NO_ARGUMENT ],
[ '--serverinfo', '-I', GetoptLong::NO_ARGUMENT ],
[ '--status', '-T', GetoptLong::NO_ARGUMENT ],
[ '--strategy', '-s', GetoptLong::REQUIRED_ARGUMENT ],
[ '--strats', '-S', GetoptLong::NO_ARGUMENT ],
[ '--user', '-u', GetoptLong::REQUIRED_ARGUMENT ],
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
[ '--version', '-V', GetoptLong::NO_ARGUMENT ]
)
opt.each_option do |name, arg|
case name
when '--debug' then @debug = true
when '--database' then @database = arg
when '--dbs' then @dbs = true
when '--help' then usage
when '--host' then @host = arg
when '--key' then @key = arg
when '--info' then @info = arg
when '--match' then @match = true
when '--nocorrect' then @correct = false
when '--port' then @port = arg
when '--serverhelp' then @serverhelp = true
when '--serverinfo' then @serverinfo = true
when '--status' then @status = true
when '--strategy' then @strategy = arg; @match_strategy = arg
when '--strats' then @strats = true
when '--user' then @user = arg
when '--verbose' then @verbose = true
when '--version' then version
end
end
rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument
usage(1)
end
end
end
# display usage message and exit
#
def usage(code = 0)
$stderr.puts <<EOF
Usage: #{PROGRAM_NAME} [-h|--host server] [-p|--port service] [-d|--database dbname]
[-m|--match] [-s|--strategy strategy] [-C|--nocorrect]
[-D|--dbs] [-S|--strats] [-H|--serverhelp] [-i|--info dbname]
[-I|--serverinfo] [-T|--status] [-b|--debug] [-u|--user user]
[-k|--key key] [-v|--verbose] word1 [word2 ... wordn]
#{PROGRAM_NAME} [--help|-v|--version]
EOF
exit code
end
# display version and copyright message, then exit
#
def version
$stderr.puts <<EOF
#{PROGRAM_NAME} #{PROGRAM_VERSION}
Copyright (C) 2002-2007 Ian Macdonald <ian@caliban.org>
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE, to the extent permitted by law.
EOF
exit
end
# tabulate databases and strategies
#
def tabulate(data_type, data)
printf("%d %s available:\n", data.size, data_type)
data.each { |k, v| printf(" %-11s%s\n", k, v) }
end
# show matches
#
def show_matches(match, header = nil)
puts header if header
yield if block_given?
screen_width = 80
match_separator = ' '
match.each do |db, definitions|
# quote multi-word matches
definitions.each { |definition| definition.sub!(/^(.* .*)$/, "\"\\1\"") }
line = "%s: %s\n" % [ db, definitions.join(match_separator) ]
# wrap display of definitions at screen_width characters
while line.length > screen_width - match_separator.length
cutoff = line[0 .. screen_width - 1].rindex(match_separator)
puts line[0 .. cutoff - 1]
print match_separator
line = line[cutoff + 2, line.length - cutoff - 1]
end
puts line
end
end
# show definitions
#
def show_definitions(definitions, header = nil)
puts header if header
definitions.each do |d|
printf("\nFrom %s [%s]:\n\n", d.description, d.database)
d.definition.each { |line| print ' ', line }
end
end
# take care of pluralising nouns
#
def pluralise(ending, count)
count == 1 ? '' : ending
end
# check for status code errors
#
def check_code(code, message)
case code
when DICT::INVALID_DATABASE
die(code, 'Invalid database. Use -D|--dbs for a list.')
when DICT::ILLEGAL_PARAMETERS
die(code, message)
when DICT::INVALID_STRATEGY
die(code, "Invalid search strategy. Use -S|--strats for a list.")
when DICT::AUTH_DENIED
$stderr.puts "Authentication denied"
end
end
# raise an exception and exit
#
def die(code, message)
raise ProtocolError, "(%s) %s" % [ code, message ]
end
# start of main program
option = Optlist.new
# connect
dict = DICT.new(option.host, option.port, option.debug, option.verbose)
# send client info
dict.client("%s v%s" % [ PROGRAM_NAME, PROGRAM_VERSION ])
# authorise if possible, but don't die on failure
dict.auth(option.user, option.key) if option.user && option.key
check_code(dict.code, dict.message)
# informational options
if option.info
resp = dict.show_info(option.info)
puts resp unless resp.nil?
check_code(dict.code, dict.message)
end
if option.serverinfo
resp = dict.show_server
puts resp unless resp.nil?
check_code(dict.code, dict.message)
end
if option.dbs
unless (resp = dict.show_db).nil?
check_code(dict.code, dict.message)
tabulate("databases", resp)
end
end
if option.strats
unless (resp = dict.show_strat).nil?
check_code(dict.code, dict.message)
tabulate("strategies", resp)
end
end
# status option
puts dict.status if option.status
# server-side help option
puts "Server help:", dict.help if option.serverhelp
exit if ARGV.empty? && option.dbs || option.strats || option.info ||
option.serverinfo || option.status || option.serverhelp
usage if ARGV.empty?
ARGV.each do |word|
if option.match # perform match
match = dict.match(option.database, option.match_strategy, word)
check_code(dict.code, dict.message)
printf(%Q(No matches found for "%s"\n), word) unless match
if match
show_matches(match) do
count = 0
match.each_value { |definitions| count += definitions.size }
printf(%Q(\n%d definition%s for "%s" found in %d database%s\n\n),
count, pluralise('s', count), word, match.size,
pluralise('s', match.size))
end
end
next
end
# check for non-default matching strategy
if option.strategy != DEFAULT_STRATEGY # match to get list of words
match = dict.match(option.database, option.match_strategy, word)
check_code(dict.code, dict.message)
if match
match.each do |db, words| # iterate over databases
words.each do |w| # iterate over words
definitions = dict.define(db, w)
show_definitions(definitions) if definitions
end
end
end
next
end
# look up definitions
definitions = dict.define(option.database, word)
check_code(dict.code, dict.message)
if definitions
show_definitions(definitions, "%d definition%s found" %
[ definitions.size, pluralise('s', definitions.size) ])
else
printf('No definitions found for "%s"', word)
if option.correct # perform correction
match = dict.match(option.database, DICT::DEFAULT_MATCH_STRATEGY, word)
show_matches(match, ', perhaps you mean:') if match
puts unless match
else
puts
end
end
end
dict.disconnect
syntax highlighted by Code2HTML, v. 0.9.1