Added zlint
authorAdam Dickmeiss <adam@indexdata.dk>
Wed, 18 Feb 2004 22:11:41 +0000 (22:11 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Wed, 18 Feb 2004 22:11:41 +0000 (22:11 +0000)
Makefile.am
configure.in
zlint/Makefile.am [new file with mode: 0644]
zlint/zlint.cpp [new file with mode: 0644]

index 0e1b58e..a7d9a15 100644 (file)
@@ -1,6 +1,6 @@
 AUTOMAKE_OPTIONS = foreign
 
-SUBDIRS = src include zoom etc doc 
+SUBDIRS = src include zlint zoom etc doc 
 
 aclocaldir=$(datadir)/aclocal
 aclocal_DATA = yazpp.m4
index e29ffe5..7ea8a4f 100644 (file)
@@ -101,6 +101,7 @@ AC_OUTPUT([
        src/Makefile
        include/Makefile
        include/yaz++/Makefile
+       zlint/Makefile
        yaz++-config
        zoom/Makefile
        doc/Makefile
diff --git a/zlint/Makefile.am b/zlint/Makefile.am
new file mode 100644 (file)
index 0000000..b0ebe93
--- /dev/null
@@ -0,0 +1,9 @@
+## $Id: Makefile.am,v 1.1 2004-02-18 22:11:41 adam Exp $
+
+AM_CXXFLAGS = $(YAZINC) -I$(srcdir)/../include $(XSLT_CFLAGS)
+
+zlint_SOURCES=zlint.cpp
+
+bin_PROGRAMS = zlint
+
+LDADD=../src/libyazcpp.la $(YAZLALIB) $(XSLT_LIBS)
diff --git a/zlint/zlint.cpp b/zlint/zlint.cpp
new file mode 100644 (file)
index 0000000..e50de1e
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2004, Index Data.
+ * See the file LICENSE for details.
+ * 
+ * $Id: zlint.cpp,v 1.1 2004-02-18 22:11:41 adam Exp $
+ */
+
+#include <yaz/pquery.h>
+#include <yaz/options.h>
+#include <yaz/otherinfo.h>
+#include <yaz/charneg.h>
+#include <yaz/sortspec.h>
+#include <yaz/log.h>
+#include <yaz++/pdu-assoc.h>
+#include <yaz++/socket-manager.h>
+#include <yaz++/z-assoc.h>
+
+#define REFID_BUF1 "zlint\000check1"
+#define REFID_LEN1 12
+#define REFID_BUF2 "zlint\000check2"
+#define REFID_LEN2 12
+
+enum test_code {
+    TEST_FINISHED,
+    TEST_CONTINUE,
+};
+
+#if 0
+class Zlint;
+class Zlint_test {
+public:
+    virtual void init_send(Zlint *z) = 0;
+    virtual test_code init_recv(Zlint *z, Z_InitResponse *ir) = 0;
+    virtual test_code other_recv(Zlint *z, Z_APDU *ir, Z_InitResponse *ir) = 0;
+};
+#endif
+
+const char *try_syntax [] = {
+    "usmarc",
+    "unimarc",
+    "danmarc",
+    "sutrs",
+    "grs1",
+    "xml",
+    "normarc",
+    0
+};
+const char *try_query[] = {
+    "@attr 1=4 petersson",
+    "@attr 1=1016 petersson",
+    "@attr 1=4 kingdom",
+    "@attr 1=1016 kingdom",
+    "@attr 1=62 sword",
+    "sword"
+    "seven",
+    "@attr 1=4 water",
+    "@attr 1=1016 water",
+    "computer",
+    "@attr 1=4 computer",
+    "@attr 1=1016 computer",
+    "water",
+    "join",
+    "about",
+    "map",
+    0,
+};
+
+const char *try_sort [] = {
+    "1=4 <",
+    "1=4 >",
+    "1=62 >",
+    "1=62 <",
+    0
+};
+const char *try_scan [] = {
+    "@attr 1=4 ab",
+    "@attr 1=1003 ab",
+    "@attr 1=1016 ab",
+    0
+};
+
+class Zlint : public Yaz_Z_Assoc {
+    int m_tst_no;
+
+    int m_subtst_no;
+
+    int m_query_no;
+    int m_scan_no;
+    int m_sort_no;
+    int m_record_syntax_no;
+    int m_got_result_set;
+    IYaz_PDU_Observable *m_PDU_Observable;
+    char *m_host;
+    char *m_database;
+    int m_timeout_init;
+    int m_timeout_connect;
+    int m_protocol_version;
+    char m_session_str[20];
+    int initResponseGetVersion(Z_InitResponse *init);
+public:
+    void prepare();
+    void recv_GDU(Z_GDU *apdu, int len);
+    Zlint(IYaz_PDU_Observable *the_PDU_Observable);
+    void args(int argc, char **argv);
+    void connectNotify();
+    void failNotify();
+    void closeNextTest();
+    void sendTest();
+    int nextTest();
+    void testContinue();
+    void timeoutNotify();
+    IYaz_PDU_Observer *sessionNotify(
+       IYaz_PDU_Observable *the_PDU_Observable, int fd);
+    void connect();
+    Z_ReferenceId *mk_refid(const char *buf, int len);
+};
+
+int Zlint::initResponseGetVersion(Z_InitResponse *init)
+{
+    int no = 0;
+    int off = 0;
+    int i;
+    for (i = 0; i<12; i++)
+       if (ODR_MASK_GET(init->protocolVersion, no))
+       {
+           no = i+1;
+           if (off)
+               yaz_log(LOG_WARN, "%sbad formatted version");
+       }
+       else
+           off = 1;
+    return no;
+}
+
+Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
+{
+    Z_ReferenceId *id = 
+       (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
+    id->size = id->len = len;
+    id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
+    memcpy(id->buf, buf, len);
+    return id;
+}
+
+void Zlint::recv_GDU(Z_GDU *gdu, int len)
+{
+    yaz_log(LOG_LOG, "%sgot PDU", m_session_str);
+    if (gdu->which != Z_GDU_Z3950)
+    {
+       yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
+       closeNextTest();
+    }
+    if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
+    {
+       int i;
+       Z_InitResponse *init = gdu->u.z3950->u.initResponse;
+       int ver = initResponseGetVersion(init);
+       int result = init->result ? *init->result : 0;
+       if (!result)
+           yaz_log(LOG_WARN, "%sinit rejected");
+       switch(m_tst_no)
+       {
+       case 0:
+           if (ver > 3 || ver < 2)
+               yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
+                       m_session_str, ver);
+           m_protocol_version = ver;
+           if (!result)
+               closeNextTest();
+           else
+           {
+               close();
+               nextTest();
+               connect();
+           }
+           break;
+       case 1:
+           if (ver != 2)
+               yaz_log(LOG_WARN, "%sgot version %d, expected 2",
+                       m_session_str, ver);
+           closeNextTest();
+           break;
+       case 2:
+           if (ver < 2 || ver > 5)
+               yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
+                       m_session_str,ver);
+           closeNextTest();
+           break;
+       case 3:
+           if (!init->referenceId)
+               yaz_log(LOG_WARN, "%smissing referenceID from init response",
+                       m_session_str);
+           else if (init->referenceId->len != REFID_LEN1
+                    || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
+               yaz_log(LOG_WARN, "%sreference ID does not match");
+           closeNextTest();
+           break;
+       case 4:
+           if (m_subtst_no == 0)
+           {
+               if (!init->referenceId)
+                   yaz_log(LOG_WARN, "%smissing referenceID from first init response",
+                           m_session_str);
+               else if (init->referenceId->len != REFID_LEN1
+                        || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
+                   yaz_log(LOG_WARN, "%sreference ID does not match");
+               m_subtst_no++;
+           }
+           else
+           {
+               if (!init->referenceId)
+                   yaz_log(LOG_WARN, "%smissing referenceID from second init response",
+                           m_session_str);
+               else if (init->referenceId->len != REFID_LEN2
+                        || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
+                   yaz_log(LOG_WARN, "%sreference ID does not match");
+               closeNextTest();
+           }       
+           break;
+       case 5:
+           if (init->options)
+           {
+               int i;
+               int no_set = 0;
+               int no_reset = 0;
+               for (i = 0; i <= 24; i++)
+                   if (ODR_MASK_GET(init->options, i))
+                       no_set++;
+                   else
+                       no_reset++;
+               if (no_set < 2)
+                   yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
+                           m_session_str);
+               if (no_reset == 0)
+                   yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
+                           m_session_str);
+           }
+           closeNextTest();
+           break;
+       case 6:
+           if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
+           {
+               Z_CharSetandLanguageNegotiation *p =
+                   yaz_get_charneg_record(init->otherInfo);
+               
+               if (p) {
+                   
+                   char *charset=NULL, *lang=NULL;
+                   int selected;
+                   NMEM m = nmem_create();
+                   
+                   yaz_get_response_charneg(m, p, &charset, &lang,
+                                            &selected);
+                   yaz_log(LOG_LOG, "%sAccepted character set : %s",
+                           m_session_str, charset);
+                   yaz_log(LOG_LOG, "%sAccepted code language : %s",
+                           m_session_str, lang ? lang : "none");
+                   yaz_log(LOG_LOG, "%sAccepted records in ...: %d",
+                           m_session_str, selected );
+                   nmem_destroy(m);
+               }
+           }
+           closeNextTest();
+           break;
+       case 7:
+           if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
+               yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
+
+           if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
+               yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
+           if (m_subtst_no < 3)
+           {
+               close();
+               m_subtst_no++;
+               connect();
+           }
+           else
+               closeNextTest();
+           break;
+       case 9:
+           if (result && ODR_MASK_GET(init->options, Z_Options_scan))
+               sendTest();
+           else
+               closeNextTest();
+           break;
+       case 10:
+           if (result && ODR_MASK_GET(init->options, Z_Options_sort))
+               sendTest();
+           else
+               closeNextTest();
+           break;
+       default:
+           if (result)
+               sendTest();
+           else
+               closeNextTest();
+       }
+    }
+    else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
+    {
+       Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
+       switch(m_tst_no)
+       {
+       case 8:
+           if (sr->records && (sr->records->which == Z_Records_NSD 
+                               || 
+                               sr->records->which == Z_Records_multipleNSD))
+           {
+               yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
+               m_query_no++;
+               sendTest();
+           }
+           else if (!sr->resultCount || *sr->resultCount == 0)
+           {
+               m_query_no++;
+               sendTest();
+           }
+           else
+           {
+               yaz_log(LOG_LOG, "%sgot %d result count with %s",
+                       m_session_str, *sr->resultCount,
+                       try_query[m_query_no]);
+               m_got_result_set = 1;
+               sendTest();
+           }
+           break;
+       default:
+           closeNextTest();
+       }
+    }
+    else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
+    {
+       Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
+       switch(m_tst_no)
+       {
+       case 8:
+           if (sr->records && (sr->records->which == Z_Records_NSD 
+                               || 
+                               sr->records->which == Z_Records_multipleNSD))
+           {
+               yaz_log(LOG_LOG, "%spresent returned NSD for %s",
+                       m_session_str, try_syntax[m_record_syntax_no]);
+           }
+           else if (sr->records && sr->records->which == Z_Records_DBOSD
+                    && sr->records->u.databaseOrSurDiagnostics->num_records>0
+                    && sr->records->u.databaseOrSurDiagnostics->records[0])
+           {
+               if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
+               {
+                   Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
+                   Odr_oid *expectRecordSyntax =
+                       yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
+                                           try_syntax[m_record_syntax_no]);
+                   if (oid_oidcmp(expectRecordSyntax,
+                              ext->direct_reference))
+                       yaz_log(LOG_WARN, "%spresent bad record type for %s",
+                               m_session_str,
+                               try_syntax[m_record_syntax_no]);
+                   else
+                       yaz_log(LOG_LOG, "%spresent OK for %s", m_session_str,
+                               try_syntax[m_record_syntax_no]);
+               }
+               else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
+                   yaz_log(LOG_LOG, "%spresent returned SD %s", m_session_str,
+                           try_syntax[m_record_syntax_no]);
+               else
+                   yaz_log(LOG_WARN, "%spresent returned fragment %s",
+                           m_session_str,
+                           try_syntax[m_record_syntax_no]);
+           }
+           else
+           {
+               yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
+               
+           }
+           m_record_syntax_no++;
+           sendTest();
+       }
+    }
+    else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
+    {
+       Z_ScanResponse *sr =  gdu->u.z3950->u.scanResponse;
+       switch(m_tst_no)
+       {
+       case 9:
+           if (sr->entries->nonsurrogateDiagnostics)
+           {
+               yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
+                       try_scan[m_scan_no]);
+               m_scan_no++;
+               sendTest();
+           }
+           else if (sr->entries->entries && sr->entries->num_entries > 0)
+           {
+               yaz_log(LOG_LOG, "%sscan OK for %s", m_session_str,
+                       try_scan[m_scan_no]);
+               closeNextTest();
+           }
+           else
+           {
+               yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
+                       m_session_str,
+                       try_scan[m_scan_no]);
+               m_scan_no++;
+               sendTest();
+           }
+           break;
+       default:
+           closeNextTest();
+       }
+    }
+    else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
+    {
+       Z_SortResponse *sr =  gdu->u.z3950->u.sortResponse;
+       switch(m_tst_no)
+       {
+       case 10:
+           if (sr->diagnostics)
+           {
+               yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
+                       try_sort[m_sort_no]);
+               m_sort_no++;
+               sendTest();
+           }
+           else
+           {
+               yaz_log(LOG_LOG, "%ssort OK for %s", m_session_str,
+                       try_sort[m_sort_no]);
+               closeNextTest();
+           }
+           break;
+       default:
+           closeNextTest();
+       }
+    }
+    else
+       closeNextTest();
+}
+
+Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) : 
+    Yaz_Z_Assoc(the_PDU_Observable)
+{
+    m_PDU_Observable = the_PDU_Observable;
+    m_host = 0;
+    m_database = 0;
+    m_timeout_connect = 30;
+    m_timeout_init = 30;
+    m_tst_no = 0;
+    m_subtst_no = 0;
+    m_protocol_version = 0;
+    sprintf(m_session_str, "%d ", m_tst_no);
+}
+
+void Zlint::connectNotify()
+{
+    Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
+    Z_InitRequest *init = apdu->u.initRequest;
+    int len;
+    Z_OtherInformation **oi;
+
+    timeout(m_timeout_init);
+
+    switch(m_tst_no)
+    {
+    case 0:
+       /* check if target properly negotiates to v3 .. */
+       ODR_MASK_ZERO(init->protocolVersion);
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       break;
+    case 1:
+       /* check if target properly negotiates to v2 .. */
+       ODR_MASK_ZERO(init->protocolVersion);
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
+       break;
+    case 2:
+       /* check latest version of target  - up to v9 */
+       ODR_MASK_ZERO(init->protocolVersion);
+       int i;
+       for (i = 0; i< 9; i++)
+           ODR_MASK_SET(init->protocolVersion, i);
+       break;
+    case 3:
+       /* send refID in init request */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
+       break;
+    case 4:
+       /* send double init with differnet refID's */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
+       init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
+       send_Z_PDU(apdu, &len);
+       
+       apdu = create_Z_PDU(Z_APDU_initRequest);
+       init = apdu->u.initRequest;
+
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
+
+       init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
+       break;
+    case 5:
+       /* set all options.. see what target really supports .. */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_ZERO(init->options);
+       for (i = 0; i <= 24; i++)
+           ODR_MASK_SET(init->options, i);
+       break;
+    case 6:
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       yaz_oi_APDU(apdu, &oi);
+       if (oi)
+       {
+           Z_OtherInformationUnit *p0;
+           const char *negotiationCharset[] = {
+               "UTF-8",
+               "UTF-16",
+               "UCS-2",
+               "UCS-4",
+               "ISO-8859-1"
+           };
+           char *yazLang = 0;
+
+           if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
+               ODR_MASK_SET(init->options, Z_Options_negotiationModel);
+
+               p0->which = Z_OtherInfo_externallyDefinedInfo;
+               p0->information.externallyDefinedInfo =
+
+                   yaz_set_proposal_charneg(
+                       odr_encode(),
+                       negotiationCharset, 5,
+                       (const char**)&yazLang, yazLang ? 1 : 0, 1);
+           }
+       }
+       break;
+    case 7:
+       *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
+       *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
+       break;
+    case 8:
+       /* search */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_SET(init->options, Z_Options_namedResultSets);
+       break;
+    case 9:
+       /* scan */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_SET(init->options, Z_Options_namedResultSets);
+       ODR_MASK_SET(init->options, Z_Options_scan);
+       break;
+    case 10:
+       /* sort */
+       ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
+       ODR_MASK_SET(init->options, Z_Options_namedResultSets);
+       ODR_MASK_SET(init->options, Z_Options_sort);
+       break;
+    }
+    int r = send_Z_PDU(apdu, &len);
+}
+
+int Zlint::nextTest()
+{
+    m_subtst_no = 0;
+    while(m_tst_no >= 0)
+    {
+       m_tst_no++;
+       sprintf(m_session_str, "%d ", m_tst_no);
+       switch(m_tst_no)
+       {
+       case 0:
+           return 1;
+       case 1:
+           return 1;
+       case 2:
+           return 1;
+       case 3:
+           return 1;
+       case 4:
+           return 1;
+       case 5:
+           return 1;
+       case 6:
+           return 1;
+       case 7:
+           return 1;
+       case 8:
+           m_query_no = 0;
+           m_record_syntax_no = 0;
+           m_got_result_set = 0;
+           return 1;
+       case 9:
+           m_scan_no = 0;
+           return 1;
+       case 10:
+           m_sort_no = 0;
+           return 1;
+       default:
+           m_tst_no = -1;
+       }
+    }
+    return 0;
+}
+
+// current test failed badly - goto next or stop..
+void Zlint::closeNextTest()
+{
+    close();
+    if (m_tst_no != 0)
+    {
+       nextTest();
+       connect();
+    }
+}
+
+void Zlint::failNotify()
+{
+    yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
+    testContinue();
+}
+
+void Zlint::timeoutNotify()
+{
+    yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
+    testContinue();
+}
+
+void Zlint::testContinue()
+{
+    close();
+    switch(m_tst_no)
+    {
+    case 8:
+       if (m_got_result_set)
+       {
+           // must search again to establish.. keep query
+           m_got_result_set = 0;
+           m_record_syntax_no++;
+       }
+       else
+       {
+           // try new search ..
+           m_query_no++;
+       }
+       connect();
+       return;
+    case 9:
+       m_scan_no++;
+       connect();
+       return;
+    }
+    nextTest();
+}
+
+void Zlint::sendTest()
+{
+    Z_APDU *apdu;
+    switch(m_tst_no)
+    {
+    case 8:
+       if (m_got_result_set == 0)
+       {
+           apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
+           Z_SearchRequest *sr;
+           sr = apdu->u.searchRequest;
+           sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
+           if (try_query[m_query_no] && sr)
+           {
+               sr->query->which = Z_Query_type_1;
+               Z_RPNQuery *rpn;
+               YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
+               
+               sr->databaseNames = &m_database;
+               sr->num_databaseNames = 1;
+               
+               rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
+
+               yaz_pqf_destroy (pqf_parser);
+
+               if (rpn)
+               {
+                   int len;
+                   yaz_log(LOG_LOG, "%spqf: %s",
+                           m_session_str, try_query[m_query_no]);
+
+                   sr->query->u.type_1 = rpn;
+                   send_Z_PDU(apdu, &len);
+               }
+               else
+                   closeNextTest();
+           }
+           else
+           {
+               yaz_log(LOG_WARN, "%sunable to get any hit count", 
+                       m_session_str);
+               closeNextTest();
+           }
+       }
+       else if (m_got_result_set && try_syntax[m_record_syntax_no])
+       {
+           int len;
+           apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
+           Z_PresentRequest *pr = apdu->u.presentRequest;
+           *pr->numberOfRecordsRequested = 1;
+           *pr->resultSetStartPoint = 1;
+           
+           pr->preferredRecordSyntax =
+               yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
+                                   try_syntax[m_record_syntax_no]);
+           send_Z_PDU(apdu, &len);
+       }
+       else
+           closeNextTest();
+       break;
+    case 9:
+       apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
+       if (apdu && try_scan[m_scan_no])
+       {
+           int len;
+           YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
+           Z_ScanRequest *sr = apdu->u.scanRequest;
+           sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
+                                                    odr_encode(),
+                                                    &sr->attributeSet,
+                                                    try_scan[m_scan_no]);
+
+           sr->databaseNames = &m_database;
+           sr->num_databaseNames = 1;
+
+           yaz_pqf_destroy (pqf_parser);
+           send_Z_PDU(apdu, &len);
+       }
+       else
+           closeNextTest();
+       break;
+    case 10:
+       apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
+       if (apdu && try_sort[m_sort_no])
+       {
+           char *setstring = "default";
+           int len;
+           Z_SortRequest *sr = apdu->u.sortRequest;
+
+           sr->num_inputResultSetNames = 1;
+           sr->num_inputResultSetNames = 1;
+           sr->inputResultSetNames = (Z_InternationalString **)
+               odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
+           sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
+           sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
+           sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
+           send_Z_PDU(apdu, &len);
+       }
+       else
+           closeNextTest();
+       break;
+    default:
+       closeNextTest();
+    }
+}
+
+IYaz_PDU_Observer *Zlint::sessionNotify(
+    IYaz_PDU_Observable *the_PDU_Observable, int fd)
+{
+    return 0;
+}
+
+void Zlint::connect()
+{
+    if (m_host && m_tst_no != -1)
+    {
+       yaz_log(LOG_LOG, "%sconnecting to %s", m_session_str, m_host);
+       timeout(m_timeout_connect);
+       client(m_host);
+    }
+}
+
+void Zlint::args(int argc, char **argv)
+{
+    char *arg;
+    int ret;
+    while ((ret = options("v", argv, argc, &arg)) != -2)
+    {
+        switch (ret)
+       {
+       case 'v':
+           break;
+       case 0:
+           if (arg)
+           {
+               const char *basep;
+               m_host = xstrdup(arg);
+               cs_get_host_args(m_host, &basep);
+               if (!basep || !*basep)
+                   basep = "Default";
+               m_database = xstrdup(basep);
+           }
+           break;
+       }
+    }
+}
+
+int main(int argc, char **argv)
+{
+    Yaz_SocketManager mySocketManager;
+    Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
+    
+    z.args(argc, argv);
+
+    z.connect();
+    while (mySocketManager.processEvent() > 0)
+       ;
+    exit (0);
+}