Projects/worblehat-old
Projects
/
worblehat-old
Archived
12
0
Fork 0
This repository has been archived on 2024-07-04. You can view files and clone it, but cannot push or open issues or pull requests.
worblehat-old/python/PyZ3950/z3950.py

755 lines
29 KiB
Python

#!/usr/bin/env python
# This file should be available from
# http://www.pobox.com/~asl2/software/PyZ3950/
# and is licensed under the X Consortium license:
# Copyright (c) 2001, Aaron S. Lav, asl2@pobox.com
# All rights reserved.
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, and/or sell copies of the Software, and to permit persons
# to whom the Software is furnished to do so, provided that the above
# copyright notice(s) and this permission notice appear in all copies of
# the Software and that both the above copyright notice(s) and this
# permission notice appear in supporting documentation.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
# OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
# INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# Except as contained in this notice, the name of a copyright holder
# shall not be used in advertising or otherwise to promote the sale, use
# or other dealings in this Software without prior written authorization
# of the copyright holder.
# Change history:
# 2002/05/23
# Fix for Python2 compatibility. Thanks to Douglas Bates <bates@stat.wisc.edu>
# Fix to support SUTRS (requires asn1 updates, too)
# 2002/05/28
# Make SUTRS printing a little more useful
# Correctly close connection when done
# Handle receiving diagnostics instead of records a little better
"""<p>PyZ3950 currently is capable of sending and receiving v2 or v3 PDUs
Initialize, Search, Present, Scan, Sort, Close, and Delete. For client
work, you probably want to use ZOOM, which should be in the same
distribution as this file, in zoom.py. The Server class in this file
implements a server, but could use some work. Both interoperate with
the <a href="http://www.indexdata.dk/yaz"> Yaz toolkit</a> and the
client interoperates with a variety of libraries. <p>
Useful resources:
<ul>
<li><a href="http://lcweb.loc.gov/z3950/agency/">
Library of Congress Z39.50 Maintenance Agency Page</a></li>
<li><a href="http://lcweb.loc.gov/z3950/agency/document.html">
Official Specification</a></li>
<li><a href="http://www.loc.gov/z3950/agency/clarify/">Clarifications</a></li>
</ul>
"""
from __future__ import nested_scopes
import getopt
import sys
import exceptions
import random
import socket
import string
import traceback
import codecs
from PyZ3950 import asn1
from PyZ3950 import zmarc
from PyZ3950.zdefs import *
out_encoding = None
trace_recv = 0
trace_init = 0
print_hex = 0
class Z3950Error(Exception):
pass
# Note: following 3 exceptions are defaults, but can be changed by
# calling conn.set_exs
class ConnectionError(Z3950Error): # TCP or other transport error
pass
class ProtocolError(Z3950Error): # Unexpected message or badly formatted
pass
class UnexpectedCloseError(ProtocolError):
pass
vers = '0.62'
default_resultSetName = 'default'
DEFAULT_PORT = 2101
Z3950_VERS = 3 # This is a global switch: do we support V3 at all?
def extract_recs (resp):
(typ, recs) = resp.records
if (typ <> 'responseRecords'):
raise ProtocolError ("Bad records typ " + str (typ) + str (recs))
if len (recs) == 0:
raise ProtocolError ("No records")
fmtoid = None
extract = []
for r in recs:
(typ, data) = r.record
if (typ <> 'retrievalRecord'):
raise ProtocolError ("Bad typ %s data %s" % (str (typ), str(data)))
oid = data.direct_reference
if fmtoid == None:
fmtoid = oid
elif fmtoid <> oid:
raise ProtocolError (
"Differing OIDs %s %s" % (str (fmtoid), str (oid)))
# Not, strictly speaking, an error.
dat = data.encoding
(typ, dat) = dat
if (oid == Z3950_RECSYN_USMARC_ov):
if typ <> 'octet-aligned':
raise ProtocolError ("Weird record EXTERNAL MARC type: " + typ)
extract.append (dat)
return (fmtoid, extract)
def get_formatter (oid):
def printer (x):
print oid, repr (x)
def print_marc (marc):
print str (zmarc.MARC(marc))
def print_sutrs (x):
print "SUTRS:",
if isinstance (x, type ('')):
print x
elif isinstance (x, type (u'')):
if out_encoding == None:
print repr (x)
else:
try:
print x.encode (out_encoding)
except UnicodeError, u:
print "Cannot print %s in current encoding %s" % (
repr (x), out_encoding)
if oid == Z3950_RECSYN_SUTRS_ov:
return print_sutrs
if oid == Z3950_RECSYN_USMARC_ov:
return print_marc
else:
return printer
def disp_resp (resp):
try:
(fmtoid, recs) = extract_recs (resp)
except ProtocolError, val:
print "Bad records", str (val)
formatter = get_formatter (fmtoid)
for rec in recs:
formatter (rec)
class Conn:
rdsz = 65536
def __init__ (self, sock = None, ConnectionError = ConnectionError,
ProtocolError = ProtocolError, UnexpectedCloseError =
UnexpectedCloseError):
self.set_exns (ConnectionError, ProtocolError, UnexpectedCloseError)
if sock == None:
self.sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
else:
self.sock = sock
self.decode_ctx = asn1.IncrementalDecodeCtx (APDU)
self.encode_ctx = asn1.Ctx ()
def set_exns (self, conn, protocol, unexp_close):
self.ConnectionError = conn
self.ProtocolError = protocol
self.UnexpectedCloseError = unexp_close
def set_codec (self, charset_name, charsets_in_records):
self.charset_name = charset_name
self.charsets_in_records = not not charsets_in_records # collapse None and 0
if trace_charset:
print "Setting up codec!", self.charset_name
strip_bom = self.charset_name == 'utf-16'
# XXX should create a new codec which wraps utf-16 but
# strips the Byte Order Mark, or use stream codecs
if self.charset_name <> None:
self.encode_ctx.set_codec (asn1.GeneralString,
codecs.lookup (self.charset_name),
strip_bom)
self.decode_ctx.set_codec (asn1.GeneralString,
codecs.lookup (self.charset_name),
strip_bom)
if not charsets_in_records: # None or 0
register_retrieval_record_oids(self.decode_ctx)
register_retrieval_record_oids(self.encode_ctx)
def readproc (self):
if self.sock == None:
raise self.ConnectionError ('disconnected')
try:
b = self.sock.recv (self.rdsz)
except socket.error, val:
self.sock = None
raise self.ConnectionError ('socket', str (val))
if len (b) == 0: # graceful close
self.sock = None
raise self.ConnectionError ('graceful close')
if trace_recv:
print map (lambda x: hex(ord(x)), b)
return b
def read_PDU (self):
while 1:
if self.decode_ctx.val_count () > 0:
return self.decode_ctx.get_first_decoded ()
try:
b = self.readproc ()
self.decode_ctx.feed (map (ord, b))
except asn1.BERError, val:
raise self.ProtocolError ('ASN1 BER', str(val))
class Server (Conn):
test = 0
def __init__ (self, sock):
Conn.__init__ (self, sock)
self.expecting_init = 1
self.done = 0
self.result_sets = {}
self.charset_name = None
def run (self):
while not self.done:
(typ, val) = self.read_PDU ()
fn = self.fn_dict.get (typ, None)
if fn == None:
raise self.ProtocolError ("Bad typ", typ + " " + str (val))
if typ <> 'initRequest' and self.expecting_init:
raise self.ProtocolError ("Init expected", typ)
fn (self, val)
def send (self, val):
b = self.encode_ctx.encode (APDU, val)
if self.test:
print "Internal Testing"
# a reminder not to leave this switched on by accident
self.decode_ctx.feed (b)
decoded = self.read_PDU ()
assert (val== decoded)
self.sock.send (b)
def do_close (self, reason, info):
close = Close ()
close.closeReason = reason
close.diagnosticInformation = info
self.send (('close', close))
def close (self, parm):
self.done = 1
self.do_close (0, 'Normal close')
def search_child (self, query):
return range (random.randint (2,10))
def search (self, sreq):
if sreq.replaceIndicator == 0 and self.result_sets.has_key (
sreq.resultSetName):
raise self.ProtocolError ("replaceIndicator 0")
result = self.search_child (sreq.query)
sresp = SearchResponse ()
self.result_sets[sreq.resultSetName] = result
sresp.resultCount = len (result)
sresp.numberOfRecordsReturned = 0
sresp.nextResultSetPosition = 1
sresp.searchStatus = 1
sresp.resultSetStatus = 0
sresp.presentStatus = PresentStatus.get_num_from_name ('success')
sresp.records = ('responseRecords', [])
self.send (('searchResponse', sresp))
def format_records (self, start, count, res_set, prefsyn):
l = []
for i in range (start - 1, start + count - 1):
elt = res_set[i]
elt_external = asn1.EXTERNAL ()
elt_external.direct_reference = Z3950_RECSYN_SUTRS_ov
# Not only has this text been extensively translated, but
# it also prefigures Z39.50's separation of Search and Present,
# once rearranged a little.
strings = [
'seek, and ye shall find; ask, and it shall be given you',
u"""Car quiconque demande re\u00e7oit, qui cherche trouve, et \u00e0 quit frappe on ouvrira""", # This (next) verse has non-ASCII characters
u"\u0391\u03b9\u03c4\u03b5\u03b9\u03c4\u03b5, "
u"\u03ba\u03b1\u03b9 \u03b4\u03bf\u03b8\u03b7\u03c3\u03b5\u03c4\u03b1\u03b9 "+
u"\u03c5\u03bc\u03b9\u03bd; \u03b6\u03b7\u03c4\u03b5\u03b9\u03c4\u03b5 " +
u"\u03ba\u03b1\u03b9 \u03b5\u03c5\u03c1\u03b7\u03c3\u03b5\u03c4\u03b5",
u"\u05e8\u05d0\u05d4 \u05d6\u05d4 \u05de\u05e6\u05d0\u05ea\u05d9"]
if self.charsets_in_records:
encode_charset = self.charset_name
else:
encode_charset = 'ascii'
def can_encode (s):
try:
s.encode (encode_charset)
except UnicodeError:
return 0
return 1
if self.charset_name == None:
candidate_strings = [strings[0]]
else:
candidate_strings = [s for s in strings if can_encode (s)]
# Note: this code is for debugging/testing purposes. Usually,
# language/content selection should not be made on the
# basis of the selected charset, and a surrogate diagnostic
# should be generated if the data cannot be encoded.
text = random.choice (candidate_strings)
add_str = " #%d charset %s cir %d" % (elt, encode_charset,
self.charsets_in_records)
elt_external.encoding = ('single-ASN1-type', text + add_str)
n = NamePlusRecord ()
n.name = 'foo'
n.record = ('retrievalRecord', elt_external)
l.append (n)
return l
def present (self, preq):
presp = PresentResponse ()
res_set = self.result_sets [preq.resultSetId]
presp.numberOfRecordsReturned = preq.numberOfRecordsRequested
presp.nextResultSetPosition = preq.resultSetStartPoint + \
preq.numberOfRecordsRequested
presp.presentStatus = 0
presp.records = ('responseRecords',
self.format_records (preq.resultSetStartPoint,
preq.numberOfRecordsRequested,
res_set,
preq.preferredRecordSyntax))
self.send (('presentResponse', presp))
def init (self, ireq):
if trace_init:
print "Init received", ireq
self.v3_flag = (ireq.protocolVersion ['version_3'] and
Z3950_VERS == 3)
ir = InitializeResponse ()
ir.protocolVersion = ProtocolVersion ()
ir.protocolVersion ['version_1'] = 1
ir.protocolVersion ['version_2'] = 1
ir.protocolVersion ['version_3'] = self.v3_flag
val = get_charset_negot (ireq)
charset_name = None
records_in_charsets = 0
if val <> None:
csreq = CharsetNegotReq ()
csreq.unpack_proposal (val)
def rand_choose (list_or_none):
if list_or_none == None or len (list_or_none) == 0:
return None
return random.choice (list_or_none)
charset_name = rand_choose (csreq.charset_list)
if charset_name <> None:
try:
codecs.lookup (charset_name)
except LookupError, l:
charset_name = None
csresp = CharsetNegotResp (
charset_name,
rand_choose (csreq.lang_list),
csreq.records_in_charsets)
records_in_charsets = csresp.records_in_charsets
if trace_charset:
print csreq, csresp
set_charset_negot (ir, csresp.pack_negot_resp (), self.v3_flag)
optionslist = ['search', 'present', 'delSet', 'scan','negotiation']
ir.options = Options ()
for o in optionslist:
ir.options[o] = 1
ir.preferredMessageSize = 0
ir.exceptionalRecordSize = 0
# z9350-2001 3.2.1.1.4, 0 means client should be prepared to accept
# arbitrarily long messages.
ir.implementationId = implementationId
ir.implementationName = 'PyZ3950 Test server'
ir.implementationVersion = impl_vers
ir.result = 1
if trace_charset or trace_init:
print ir
self.expecting_init = 0
self.send (('initResponse', ir))
self.set_codec (charset_name, records_in_charsets)
def sort (self, sreq):
sresp = SortResponse ()
sresp.sortStatus = 0
self.send (('sortResponse', sresp))
def delete (self, dreq):
dresp = DeleteResultSetResponse ()
dresp.deleteOperationStatus = 0
self.send (('deleteResultSetResponse', dresp))
def esrequest (self, esreq):
print "ES", esreq
esresp = ExtendedServicesResponse ()
esresp.operationStatus = ExtendedServicesResponse['operationStatus'].get_num_from_name ('failure')
self.send (('extendedServicesResponse', esresp))
fn_dict = {'searchRequest': search,
'presentRequest': present,
'initRequest' : init,
'close' : close,
'sortRequest' : sort,
'deleteResultSetRequest' : delete,
'extendedServicesRequest': esrequest}
def run_server (test = 0):
listen = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
listen.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen.bind (('', DEFAULT_PORT))
listen.listen (1)
while 1:
(sock,addr) = listen.accept ()
try:
serv = Server (sock)
serv.test = test
serv.run ()
except:
(typ, val, tb) = sys.exc_info ()
if typ == exceptions.KeyboardInterrupt:
print "kbd interrupt, leaving"
raise
print "error %s %s from %s" % (typ, val, addr)
traceback.print_exc(40)
sock.close ()
def extract_apt (rpnQuery):
"""Takes RPNQuery to AttributePlusTerm"""
RPNStruct = rpnQuery.rpn
assert (RPNStruct [0] == 'op')
operand = RPNStruct [1]
assert (operand [0] == 'attrTerm')
return operand [1]
class Client (Conn):
test = 0
def __init__ (self, addr, port = DEFAULT_PORT, optionslist = None,
charset = None, lang = None, user = None, password = None,
preferredMessageSize = 0x100000, group = None,
maximumRecordSize = 0x100000, implementationId = "",
implementationName = "", implementationVersion = "",
ConnectionError = ConnectionError,
ProtocolError = ProtocolError,
UnexpectedCloseError = UnexpectedCloseError):
Conn.__init__ (self, ConnectionError = ConnectionError,
ProtocolError = ProtocolError,
UnexpectedCloseError = UnexpectedCloseError)
try:
self.sock.connect ((addr, port))
except socket.error, val:
self.sock = None
raise self.ConnectionError ('socket', str(val))
try_v3 = Z3950_VERS == 3
if (charset and not isinstance(charset, list)):
charset = [charset]
if (lang and not isinstance(lang, list)):
charset = [lang]
negotiate_charset = charset or lang
if (user or password or group):
authentication = (user, password, group)
else:
authentication = None
InitReq = make_initreq (optionslist, authentication = authentication,
v3 = try_v3,
preferredMessageSize = preferredMessageSize,
maximumRecordSize = maximumRecordSize,
implementationId = implementationId,
implementationName = implementationName,
implementationVersion = implementationVersion,
negotiate_charset = negotiate_charset)
if negotiate_charset:
# languages = ['eng', 'fre', 'enm']
# Thanne longen folk to looken in catalogues
# and clerkes for to seken straunge bookes ...
cnr = CharsetNegotReq (charset, lang, random.choice((0,1,None)))
if trace_charset:
print cnr
set_charset_negot (InitReq, cnr.pack_proposal (), try_v3)
if trace_init:
print "Initialize request", InitReq
self.initresp = self.transact (
('initRequest', InitReq), 'initResponse')
if trace_init:
print "Initialize Response", self.initresp
self.v3_flag = self.initresp.protocolVersion ['version_3']
val = get_charset_negot (self.initresp)
if val <> None:
csr = CharsetNegotResp ()
csr.unpack_negot_resp (val)
if trace_charset:
print "Got csr", str (csr)
self.set_codec (csr.charset, csr.records_in_charsets)
self.search_results = {}
self.max_to_request = 20
self.default_recordSyntax = Z3950_RECSYN_USMARC_ov
def get_option (self, option_name):
return self.initresp.options[option_name]
def transact (self, to_send, expected):
b = self.encode_ctx.encode (APDU, to_send)
if print_hex:
print map (hex, b)
if self.test:
print "Internal Testing"
# a reminder not to leave this switched on by accident
self.decode_ctx.feed (b)
decoded = self.read_PDU ()
print "to_send", to_send, "decoded", decoded
assert (to_send == decoded)
if self.sock == None:
raise self.ConnectionError ('disconnected')
try:
self.sock.send (b)
except socket.error, val:
self.sock = None
raise self.ConnectionError('socket', str(val))
if expected == None:
return
pdu = self.read_PDU ()
(arm, val) = pdu
if self.test:
print "Internal Testing 2"
b = self.encode_ctx.encode (APDU, (arm, val))
self.decode_ctx.feed (b)
redecoded = self.read_PDU ()
if redecoded <> (arm, val):
print "Redecoded", redecoded
print "old", (arm, val)
assert (redecoded == (arm, val))
if arm == expected: # may be 'close'
return val
elif arm == 'close':
raise self.UnexpectedCloseError (
"Server closed connection reason %d diag info %s" % \
(getattr (val, 'closeReason', -1),
getattr (val, 'diagnosticInformation', 'None given')))
else:
raise self.ProtocolError (
"Unexpected response from server %s %s " % (expected,
repr ((arm, val))))
def set_dbnames (self, dbnames):
self.dbnames = dbnames
def search_2 (self, query, rsn = default_resultSetName, **kw):
# We used to check self.initresp.options['search'], but
# support for search is required by the standard, and
# www.cnshb.ru:210 doesn't set the search bit if you negotiate
# v2, but supports search anyway
sreq = make_sreq (query, self.dbnames, rsn, **kw)
recv = self.transact (('searchRequest', sreq), 'searchResponse')
self.search_results [rsn] = recv
return recv
def search (self, query, rsn = default_resultSetName, **kw):
# for backwards compat
recv = self.search_2 (('type_1', query), rsn, **kw)
return recv.searchStatus and (recv.resultCount > 0)
# If searchStatus is failure, check result-set-status -
# -subset - partial, valid results available
# -interim - partial, not necessarily valid
# -none - no result set
# If searchStatus is success, check present-status:
# - success - OK
# - partial-1 - not all, access control
# - partial-2 - not all, won't fit in msg size (but we currently don't ask for
# any records in search, shouldn't happen)
# - partial-3 - not all, resource control (origin)
# - partial-4 - not all, resource control (target)
# - failure - no records, nonsurrogate diagnostic.
def get_count (self, rsn = default_resultSetName):
return self.search_results[rsn].resultCount
def delete (self, rsn):
if not self.initresp.options['delSet']:
return None
delreq = DeleteResultSetRequest ()
delreq.deleteFunction = 0 # list
delreq.resultSetList = [rsn]
return self.transact (('deleteResultSetRequest', delreq),
'deleteResultSetResponse')
def present (self, rsn= default_resultSetName, start = None,
count = None, recsyn = None, esn = None):
# don't check for support in init resp: see search for reasoning
# XXX Azaroth 2004-01-08. This does work when rs is result of sort.
try:
sresp = self.search_results [rsn]
if start == None:
start = sresp.nextResultSetPosition
if count == None:
count = sresp.resultCount
if self.max_to_request > 0:
count = min (self.max_to_request, count)
except:
pass
if recsyn == None:
recsyn = self.default_recordSyntax
preq = PresentRequest ()
preq.resultSetId = rsn
preq.resultSetStartPoint = start
preq.numberOfRecordsRequested = count
preq.preferredRecordSyntax = recsyn
if esn <> None:
preq.recordComposition = ('simple', esn)
return self.transact (('presentRequest', preq), 'presentResponse')
def scan (self, query, **kw):
sreq = ScanRequest ()
sreq.databaseNames = self.dbnames
assert (query[0] == 'type_1' or query [0] == 'type_101')
sreq.attributeSet = query[1].attributeSet
sreq.termListAndStartPoint = extract_apt (query[1])
sreq.numberOfTermsRequested = 20 # default
for (key, val) in kw.items ():
setattr (sreq, key, val)
return self.transact (('scanRequest', sreq), 'scanResponse')
def close (self):
close = Close ()
close.closeReason = 0
close.diagnosticInformation = 'Normal close'
try:
rv = self.transact (('close', close), 'close')
except self.ConnectionError:
rv = None
if self.sock <> None:
self.sock.close ()
self.sock = None
return rv
def mk_compound_query ():
aelt1 = AttributeElement (attributeType = 1,
attributeValue = ('numeric',4))
apt1 = AttributesPlusTerm ()
apt1.attributes = [aelt1]
apt1.term = ('general', '1066')
aelt2 = AttributeElement (attributeType = 1,
attributeValue = ('numeric', 1))
apt2 = AttributesPlusTerm ()
apt2.attributes = [aelt2]
apt2.term = ('general', 'Sellar')
myrpnRpnOp = RpnRpnOp ()
myrpnRpnOp.rpn1 = ('op', ('attrTerm', apt1))
myrpnRpnOp.rpn2 = ('op', ('attrTerm', apt2))
myrpnRpnOp.op = ('and', None)
rpnq = RPNQuery (attributeSet = Z3950_ATTRS_BIB1_ov)
rpnq.rpn = ('rpnRpnOp', myrpnRpnOp)
return rpnq
def mk_simple_query (title):
aelt1 = AttributeElement (attributeType = 1,
attributeValue = ('numeric', 1003))
apt1 = AttributesPlusTerm ()
apt1.attributes = [aelt1]
apt1.term = ('general', title) # XXX or should be characterString, not general, but only when V3.
rpnq = RPNQuery (attributeSet = Z3950_ATTRS_BIB1_ov)
rpnq.rpn = ('op', ('attrTerm', apt1))
return rpnq
def_host = 'LC'
host_dict = {'BIBSYS': ('z3950.bibsys.no', 2100, 'BIBSYS'),
'YAZ': ('127.0.0.1', 9999, 'foo'),
'LCTEST' : ('ilssun2.loc.gov', 7090, 'Voyager'),
'LC' : ('z3950.loc.gov', 7090, 'Voyager'),
'NLC' : ('amicus.nlc-bnc.ca', 210, 'NL'),
'BNC' : ('amicus.nlc-bnc.ca', 210, 'NL'),
# On parle franc,ais aussi.
'LOCAL': ('127.0.0.1', 9999, 'Default'),
'LOCAL2': ('127.0.0.1', 2101, 'foo'),
'BL' :('blpcz.bl.uk', 21021, 'BLPC-ALL'),
'BELLLABS' : ('z3950.bell-labs.com', 210, 'books'),
'BIBHIT' : ('www.bibhit.dk', 210, 'Default'),
'YALE': ('webpac.library.yale.edu', 210, 'YALEOPAC'),
'OXFORD': ('library.ox.ac.uk', 210, 'ADVANCE'),
'OVID': ('z3950.ovid.com', 2213, 'pmed'), # scan only
'UC': ('ipac.lib.uchicago.edu', 210, 'uofc'),
'KUB' : ('dbiref.kub.nl', 1800, 'jel'),
'INDEXDATA' : ('muffin.indexdata.dk', 9004, 'thatt')}
# last two are Zthes servers.
if __name__ == '__main__':
optlist, args = getopt.getopt (sys.argv[1:], 'e:sh:tc:l:')
server = 0
host = def_host
test = 0
charset_list = None
lang_list = None
for (opt, val) in optlist:
if opt == '-s':
server = 1
elif opt == '-h':
host = val
elif opt == '-t':
test = 1
elif opt == '-e':
out_encoding = val
elif opt == '-c':
charset_list = val.split (',')
elif opt == '-l':
lang_list = val.split (',')
if server:
run_server (test)
host = host.upper ()
(name, port, dbname) = host_dict.get (host, host_dict[def_host])
cli = Client (name, port, charset = charset_list,
lang = lang_list)
cli.test = test
cli.set_dbnames ([dbname])
print "Starting search"
# rpnq = mk_simple_query ('Perec, Georges')
# rpnq = mk_simple_query ('Johnson, Kim')
rpnq = mk_compound_query ()
if cli.search (rpnq, smallSetUpperBound = 0, mediumSetPresentNumber = 0,
largeSetLowerBound = 1):
disp_resp (cli.present (recsyn = Z3950_RECSYN_USMARC_ov))
else:
print "Not found"
print "Deleting"
cli.delete (default_resultSetName)
cli.delete ('bogus')
print "Closing"
try:
cli.close ()
except ConnectionError:
# looks like LC, at least, sends a FIN on receipt of Close PDU
# guess we should check for gracefullness of close, and complain
# if not.
pass