Plan 9 from Bell Labs’s /usr/web/sources/contrib/mjl/pdict

Copyright © 2009 Alcatel-Lucent.
Distributed under the Lucent Public License version 1.02.
Download the Plan 9 distribution.


#!/bin/python

# dict (rfc2229) client, by mechiel lukkien <mjl@cat-v.org>, public domain

import sys
import os
import getopt
import string
import socket
#from sets import Set


# todo
# - handle escaping that dict protocol can do (i think)
# - remove some quotes (") around matches of non-exact match strategy


def usage(s):
	print >>sys.stderr, 'dict:', s
	print >>sys.stderr, 'usage: dict [-DIM] [-d db] [-m strategy] [-i db] [word]'
	sys.exit(1)


def dial(dialstr):
	proto, host, port = string.split(dialstr, '!', 2)

	if proto != 'tcp':
		raise Exception('Protocols other than tcp not implemented.')

	try:
		port = int(port)
	except:
		pass

	sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock.connect((host, port))
	
	return sock


# no module sets in python2.2+
def unique(l):
	r = []
	for e in l:
		if not e in r:
			r.append(e)
	return r



class Dict:
	def __init__(self, addr, verbose):
		self.sock = dial(addr)
		self.f = self.sock.makefile("r")
		self.verbose = verbose

		welcome = self.f.readline()
		if welcome[0:4] != '220 ':
			raise Exception("server doesn't want you (%s)" % welcome[0:4])
		r, _ = self._cmd('CLIENT python client, versionless')
		if r != '250':
			raise Exception('sending client string failed')


	def debug(self, s):
		if self.verbose:
			print >>sys.stderr, s


	def getdbs(self):
		r, line = self._cmd('SHOW DB')
		if r != '110':
			raise Exception('show db failed (%s)' % line)

		return [tuple(string.split(line, ' ', 1)) for line in self._readlist()]


	def getstrats(self):
		r, line = self._cmd('SHOW STRAT')
		if r != '111':
			raise Exception('show strat failed (%s)' % line)

		return [tuple(string.split(line, ' ', 1)) for line in self._readlist()]


	def getserverinfo(self):
		r, line = self._cmd('SHOW SERVER')
		if r != '114':
			raise Exception('show server failed (%s)' % line)
		return '\n'.join(self._readlist())


	def getdbinfo(self, db):
		r, line = self._cmd('SHOW INFO %s' % self.quote(db))
		if r != '112':
			raise Exception('show info failed (%s)' % line)
		return '\n'.join(self._readlist())


	def match(self, word, db='!', strat='.'):
		r, line = self._cmd('MATCH %s %s %s' % (self.quote(db), self.quote(strat), self.quote(word)))
		if r == '552':
			return []
		if r[0] in ['4', '5']:
			raise Exception('response to match: %s' % line)
		lines = [tuple(self.split(l, ' ', 1)) for l in self._readlist()]
		line = self._read()
		if line[0:4] != '250 ':
			raise Exception('expected code 250 after match (%s)' % line)
		return lines

	def definition(self, word, db='!'):
		r, line = self._cmd('DEFINE %s %s' % (self.quote(db), self.quote(word)))
		if r == '552':
			return []
		if r[0] in ['4', '5']:
			raise Exception('response to define: %s' % line)
		defs = []
		while 1:
			line = self._read()
			if line[0:4] == '151 ':
				_, _, db, dbdescr = self.split(line, ' ', 3)
				defs.append((db, dbdescr, '\n'.join(self._readlist())))
			else:
				break
		return defs


	def quote(self, word):
		if ' ' in word or "'" in word or '"' in word:
			return "'%s'" % string.replace(word, "'", "''")
		return word

	def split(self, line, delim, num):
		def unquote(l):
			if l[0] in ['"', "'"]:
				q = l[0]
				offset = 1
				while 1:
					offset = string.find(l[offset:], q)
					if offset == -1:
						raise Exception('Invalidly quoted line from server')

					if l[offset-1:offset+1] == (r'\%s' % q):
						offset += 1
					else:
						word = string.replace(l[1:offset+1], r'\%s' % q, q)
						l = string.lstrip(l[offset+2:])
						break
			else:
				word, l = string.split(l, delim, 1)
			return word, l

		r = []
		l = line
		while num != 0:
			word, l = unquote(l)
			r.append(word)
			num -= 1
		word, rest = unquote(l)
		r.append(word)

		return r


	def _readlist(self):
		lines = []
		while 1:
			line = self._read()
			if line == '.':
				break
			if line[0:2] == '..':
				line = line[1:]
			lines.append(line)
		return lines


	def _read(self):
		line = self.f.readline()
		if line[-1] == '\n':
			line = line[0:-1]
		if line[-1] == '\r':
			line = line[0:-1]
		self.debug('< %s' % line)
		return line


	def _cmd(self, cmd):
		self.sock.sendall(cmd + '\r\n')
		self.f.flush()
		self.debug('> %s' % cmd)

		line = self._read()

		code = line[0:3]
		return code, line


if __name__ == '__main__':
	listdb = liststrats = serverinfo = 0
	defaultdatabase = database = '!'
	defaultstrat = strat = 'exact'
	infodb = None
	verbose = 0

	try:
		optlist, args = getopt.getopt(sys.argv[1:], 'd:i:m:vDIM')
	except getopt.GetoptError, s:
		usage(s)

	for opt, arg in optlist:
		if opt == '-d':
			database = arg
		elif opt == '-i':
			infodb = arg
		elif opt == '-m':
			strat = arg
			if database == defaultdatabase:
				database = '*'
		elif opt == '-D':
			listdb = 1
		elif opt == '-I':
			serverinfo = 1
		elif opt == '-M':
			liststrats = 1
		elif opt == '-v':
			verbose = 1

	d = Dict('tcp!dict.org!2628', verbose)

	if len(args) > 1:
		usage('too many arguments')

	if liststrats:
		strats = d.getstrats()
		for name, descr in strats:
			print '%-15s %s' % (name, descr)
	if listdb:
		dbs = d.getdbs()
		for name, descr in dbs:
			print '%-15s %s' % (name, descr)
	if serverinfo:
		s = d.getserverinfo()
		print s
	if infodb:
		s = d.getdbinfo(infodb)
		print s
	if len(args) == 0:
		if not (infodb or listdb or serverinfo or liststrats):
			usage('no arguments passed')
		sys.exit(0)


	def quote(s):
		if ' ' in s or "'" in s:
			return "'%s'" % string.replace(s, "'", "''")
		return s


	if strat != defaultstrat:
		l = d.match(args[0], db=database, strat=strat)
		keys = unique([db for db, word in l])
		matches = dict([(key, [value for k, value in l if k == key]) for key in keys])
		for key in matches.keys():
			for m in matches[key]:
				print 'dict -d  %-8s %-15s   # dict:%s:%s' % (key, quote(m), key, quote(m))
		else:
			print >>sys.stderr, 'no match'
			sys.exit(2)
	else:
		if database == defaultdatabase:
			l = d.match(args[0], db=database, strat=strat)
		else:
			l = [(database, args[0])]

		for db, word in l:
			defs = d.definition(word, db=db)
			if defs == []:
				print >>sys.stderr, 'no match'
				sys.exit(2)
			db, dbdescr, defstr  = defs[0]
			if db != database:
				print '### From %s' % quote(dbdescr)
				if len(defs) > 1:
					print ''
			print '\n\n\n'.join([defstr for _, _, defstr in defs])

	sys.exit(0)

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2009 Alcatel-Lucent. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.