Added zlint
[yazpp-moved-to-github.git] / zlint / zlint.cpp
1 /*
2  * Copyright (c) 2004, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: zlint.cpp,v 1.1 2004-02-18 22:11:41 adam Exp $
6  */
7
8 #include <yaz/pquery.h>
9 #include <yaz/options.h>
10 #include <yaz/otherinfo.h>
11 #include <yaz/charneg.h>
12 #include <yaz/sortspec.h>
13 #include <yaz/log.h>
14 #include <yaz++/pdu-assoc.h>
15 #include <yaz++/socket-manager.h>
16 #include <yaz++/z-assoc.h>
17
18 #define REFID_BUF1 "zlint\000check1"
19 #define REFID_LEN1 12
20 #define REFID_BUF2 "zlint\000check2"
21 #define REFID_LEN2 12
22
23 enum test_code {
24     TEST_FINISHED,
25     TEST_CONTINUE,
26 };
27
28 #if 0
29 class Zlint;
30 class Zlint_test {
31 public:
32     virtual void init_send(Zlint *z) = 0;
33     virtual test_code init_recv(Zlint *z, Z_InitResponse *ir) = 0;
34     virtual test_code other_recv(Zlint *z, Z_APDU *ir, Z_InitResponse *ir) = 0;
35 };
36 #endif
37
38 const char *try_syntax [] = {
39     "usmarc",
40     "unimarc",
41     "danmarc",
42     "sutrs",
43     "grs1",
44     "xml",
45     "normarc",
46     0
47 };
48 const char *try_query[] = {
49     "@attr 1=4 petersson",
50     "@attr 1=1016 petersson",
51     "@attr 1=4 kingdom",
52     "@attr 1=1016 kingdom",
53     "@attr 1=62 sword",
54     "sword"
55     "seven",
56     "@attr 1=4 water",
57     "@attr 1=1016 water",
58     "computer",
59     "@attr 1=4 computer",
60     "@attr 1=1016 computer",
61     "water",
62     "join",
63     "about",
64     "map",
65     0,
66 };
67
68 const char *try_sort [] = {
69     "1=4 <",
70     "1=4 >",
71     "1=62 >",
72     "1=62 <",
73     0
74 };
75 const char *try_scan [] = {
76     "@attr 1=4 ab",
77     "@attr 1=1003 ab",
78     "@attr 1=1016 ab",
79     0
80 };
81
82 class Zlint : public Yaz_Z_Assoc {
83     int m_tst_no;
84
85     int m_subtst_no;
86
87     int m_query_no;
88     int m_scan_no;
89     int m_sort_no;
90     int m_record_syntax_no;
91     int m_got_result_set;
92     IYaz_PDU_Observable *m_PDU_Observable;
93     char *m_host;
94     char *m_database;
95     int m_timeout_init;
96     int m_timeout_connect;
97     int m_protocol_version;
98     char m_session_str[20];
99     int initResponseGetVersion(Z_InitResponse *init);
100 public:
101     void prepare();
102     void recv_GDU(Z_GDU *apdu, int len);
103     Zlint(IYaz_PDU_Observable *the_PDU_Observable);
104     void args(int argc, char **argv);
105     void connectNotify();
106     void failNotify();
107     void closeNextTest();
108     void sendTest();
109     int nextTest();
110     void testContinue();
111     void timeoutNotify();
112     IYaz_PDU_Observer *sessionNotify(
113         IYaz_PDU_Observable *the_PDU_Observable, int fd);
114     void connect();
115     Z_ReferenceId *mk_refid(const char *buf, int len);
116 };
117
118 int Zlint::initResponseGetVersion(Z_InitResponse *init)
119 {
120     int no = 0;
121     int off = 0;
122     int i;
123     for (i = 0; i<12; i++)
124         if (ODR_MASK_GET(init->protocolVersion, no))
125         {
126             no = i+1;
127             if (off)
128                 yaz_log(LOG_WARN, "%sbad formatted version");
129         }
130         else
131             off = 1;
132     return no;
133 }
134
135 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
136 {
137     Z_ReferenceId *id = 
138         (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
139     id->size = id->len = len;
140     id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
141     memcpy(id->buf, buf, len);
142     return id;
143 }
144
145 void Zlint::recv_GDU(Z_GDU *gdu, int len)
146 {
147     yaz_log(LOG_LOG, "%sgot PDU", m_session_str);
148     if (gdu->which != Z_GDU_Z3950)
149     {
150         yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
151         closeNextTest();
152     }
153     if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
154     {
155         int i;
156         Z_InitResponse *init = gdu->u.z3950->u.initResponse;
157         int ver = initResponseGetVersion(init);
158         int result = init->result ? *init->result : 0;
159         if (!result)
160             yaz_log(LOG_WARN, "%sinit rejected");
161         switch(m_tst_no)
162         {
163         case 0:
164             if (ver > 3 || ver < 2)
165                 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
166                         m_session_str, ver);
167             m_protocol_version = ver;
168             if (!result)
169                 closeNextTest();
170             else
171             {
172                 close();
173                 nextTest();
174                 connect();
175             }
176             break;
177         case 1:
178             if (ver != 2)
179                 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
180                         m_session_str, ver);
181             closeNextTest();
182             break;
183         case 2:
184             if (ver < 2 || ver > 5)
185                 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
186                         m_session_str,ver);
187             closeNextTest();
188             break;
189         case 3:
190             if (!init->referenceId)
191                 yaz_log(LOG_WARN, "%smissing referenceID from init response",
192                         m_session_str);
193             else if (init->referenceId->len != REFID_LEN1
194                      || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
195                 yaz_log(LOG_WARN, "%sreference ID does not match");
196             closeNextTest();
197             break;
198         case 4:
199             if (m_subtst_no == 0)
200             {
201                 if (!init->referenceId)
202                     yaz_log(LOG_WARN, "%smissing referenceID from first init response",
203                             m_session_str);
204                 else if (init->referenceId->len != REFID_LEN1
205                          || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
206                     yaz_log(LOG_WARN, "%sreference ID does not match");
207                 m_subtst_no++;
208             }
209             else
210             {
211                 if (!init->referenceId)
212                     yaz_log(LOG_WARN, "%smissing referenceID from second init response",
213                             m_session_str);
214                 else if (init->referenceId->len != REFID_LEN2
215                          || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
216                     yaz_log(LOG_WARN, "%sreference ID does not match");
217                 closeNextTest();
218             }       
219             break;
220         case 5:
221             if (init->options)
222             {
223                 int i;
224                 int no_set = 0;
225                 int no_reset = 0;
226                 for (i = 0; i <= 24; i++)
227                     if (ODR_MASK_GET(init->options, i))
228                         no_set++;
229                     else
230                         no_reset++;
231                 if (no_set < 2)
232                     yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
233                             m_session_str);
234                 if (no_reset == 0)
235                     yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
236                             m_session_str);
237             }
238             closeNextTest();
239             break;
240         case 6:
241             if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
242             {
243                 Z_CharSetandLanguageNegotiation *p =
244                     yaz_get_charneg_record(init->otherInfo);
245                 
246                 if (p) {
247                     
248                     char *charset=NULL, *lang=NULL;
249                     int selected;
250                     NMEM m = nmem_create();
251                     
252                     yaz_get_response_charneg(m, p, &charset, &lang,
253                                              &selected);
254                     yaz_log(LOG_LOG, "%sAccepted character set : %s",
255                             m_session_str, charset);
256                     yaz_log(LOG_LOG, "%sAccepted code language : %s",
257                             m_session_str, lang ? lang : "none");
258                     yaz_log(LOG_LOG, "%sAccepted records in ...: %d",
259                             m_session_str, selected );
260                     nmem_destroy(m);
261                 }
262             }
263             closeNextTest();
264             break;
265         case 7:
266             if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
267                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
268
269             if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
270                 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
271             if (m_subtst_no < 3)
272             {
273                 close();
274                 m_subtst_no++;
275                 connect();
276             }
277             else
278                 closeNextTest();
279             break;
280         case 9:
281             if (result && ODR_MASK_GET(init->options, Z_Options_scan))
282                 sendTest();
283             else
284                 closeNextTest();
285             break;
286         case 10:
287             if (result && ODR_MASK_GET(init->options, Z_Options_sort))
288                 sendTest();
289             else
290                 closeNextTest();
291             break;
292         default:
293             if (result)
294                 sendTest();
295             else
296                 closeNextTest();
297         }
298     }
299     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
300     {
301         Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
302         switch(m_tst_no)
303         {
304         case 8:
305             if (sr->records && (sr->records->which == Z_Records_NSD 
306                                 || 
307                                 sr->records->which == Z_Records_multipleNSD))
308             {
309                 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
310                 m_query_no++;
311                 sendTest();
312             }
313             else if (!sr->resultCount || *sr->resultCount == 0)
314             {
315                 m_query_no++;
316                 sendTest();
317             }
318             else
319             {
320                 yaz_log(LOG_LOG, "%sgot %d result count with %s",
321                         m_session_str, *sr->resultCount,
322                         try_query[m_query_no]);
323                 m_got_result_set = 1;
324                 sendTest();
325             }
326             break;
327         default:
328             closeNextTest();
329         }
330     }
331     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
332     {
333         Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
334         switch(m_tst_no)
335         {
336         case 8:
337             if (sr->records && (sr->records->which == Z_Records_NSD 
338                                 || 
339                                 sr->records->which == Z_Records_multipleNSD))
340             {
341                 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
342                         m_session_str, try_syntax[m_record_syntax_no]);
343             }
344             else if (sr->records && sr->records->which == Z_Records_DBOSD
345                      && sr->records->u.databaseOrSurDiagnostics->num_records>0
346                      && sr->records->u.databaseOrSurDiagnostics->records[0])
347             {
348                 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
349                 {
350                     Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
351                     Odr_oid *expectRecordSyntax =
352                         yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
353                                             try_syntax[m_record_syntax_no]);
354                     if (oid_oidcmp(expectRecordSyntax,
355                                ext->direct_reference))
356                         yaz_log(LOG_WARN, "%spresent bad record type for %s",
357                                 m_session_str,
358                                 try_syntax[m_record_syntax_no]);
359                     else
360                         yaz_log(LOG_LOG, "%spresent OK for %s", m_session_str,
361                                 try_syntax[m_record_syntax_no]);
362                 }
363                 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
364                     yaz_log(LOG_LOG, "%spresent returned SD %s", m_session_str,
365                             try_syntax[m_record_syntax_no]);
366                 else
367                     yaz_log(LOG_WARN, "%spresent returned fragment %s",
368                             m_session_str,
369                             try_syntax[m_record_syntax_no]);
370             }
371             else
372             {
373                 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
374                 
375             }
376             m_record_syntax_no++;
377             sendTest();
378         }
379     }
380     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
381     {
382         Z_ScanResponse *sr =  gdu->u.z3950->u.scanResponse;
383         switch(m_tst_no)
384         {
385         case 9:
386             if (sr->entries->nonsurrogateDiagnostics)
387             {
388                 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
389                         try_scan[m_scan_no]);
390                 m_scan_no++;
391                 sendTest();
392             }
393             else if (sr->entries->entries && sr->entries->num_entries > 0)
394             {
395                 yaz_log(LOG_LOG, "%sscan OK for %s", m_session_str,
396                         try_scan[m_scan_no]);
397                 closeNextTest();
398             }
399             else
400             {
401                 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
402                         m_session_str,
403                         try_scan[m_scan_no]);
404                 m_scan_no++;
405                 sendTest();
406             }
407             break;
408         default:
409             closeNextTest();
410         }
411     }
412     else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
413     {
414         Z_SortResponse *sr =  gdu->u.z3950->u.sortResponse;
415         switch(m_tst_no)
416         {
417         case 10:
418             if (sr->diagnostics)
419             {
420                 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
421                         try_sort[m_sort_no]);
422                 m_sort_no++;
423                 sendTest();
424             }
425             else
426             {
427                 yaz_log(LOG_LOG, "%ssort OK for %s", m_session_str,
428                         try_sort[m_sort_no]);
429                 closeNextTest();
430             }
431             break;
432         default:
433             closeNextTest();
434         }
435     }
436     else
437         closeNextTest();
438 }
439
440 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) : 
441     Yaz_Z_Assoc(the_PDU_Observable)
442 {
443     m_PDU_Observable = the_PDU_Observable;
444     m_host = 0;
445     m_database = 0;
446     m_timeout_connect = 30;
447     m_timeout_init = 30;
448     m_tst_no = 0;
449     m_subtst_no = 0;
450     m_protocol_version = 0;
451     sprintf(m_session_str, "%d ", m_tst_no);
452 }
453
454 void Zlint::connectNotify()
455 {
456     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
457     Z_InitRequest *init = apdu->u.initRequest;
458     int len;
459     Z_OtherInformation **oi;
460
461     timeout(m_timeout_init);
462
463     switch(m_tst_no)
464     {
465     case 0:
466         /* check if target properly negotiates to v3 .. */
467         ODR_MASK_ZERO(init->protocolVersion);
468         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
469         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
470         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
471         break;
472     case 1:
473         /* check if target properly negotiates to v2 .. */
474         ODR_MASK_ZERO(init->protocolVersion);
475         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
476         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
477         break;
478     case 2:
479         /* check latest version of target  - up to v9 */
480         ODR_MASK_ZERO(init->protocolVersion);
481         int i;
482         for (i = 0; i< 9; i++)
483             ODR_MASK_SET(init->protocolVersion, i);
484         break;
485     case 3:
486         /* send refID in init request */
487         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
488         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
489         break;
490     case 4:
491         /* send double init with differnet refID's */
492         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
493         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
494         init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
495         send_Z_PDU(apdu, &len);
496         
497         apdu = create_Z_PDU(Z_APDU_initRequest);
498         init = apdu->u.initRequest;
499
500         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
501         ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
502
503         init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
504         break;
505     case 5:
506         /* set all options.. see what target really supports .. */
507         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
508         ODR_MASK_ZERO(init->options);
509         for (i = 0; i <= 24; i++)
510             ODR_MASK_SET(init->options, i);
511         break;
512     case 6:
513         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
514         yaz_oi_APDU(apdu, &oi);
515         if (oi)
516         {
517             Z_OtherInformationUnit *p0;
518             const char *negotiationCharset[] = {
519                 "UTF-8",
520                 "UTF-16",
521                 "UCS-2",
522                 "UCS-4",
523                 "ISO-8859-1"
524             };
525             char *yazLang = 0;
526
527             if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
528                 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
529
530                 p0->which = Z_OtherInfo_externallyDefinedInfo;
531                 p0->information.externallyDefinedInfo =
532
533                     yaz_set_proposal_charneg(
534                         odr_encode(),
535                         negotiationCharset, 5,
536                         (const char**)&yazLang, yazLang ? 1 : 0, 1);
537             }
538         }
539         break;
540     case 7:
541         *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
542         *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
543         break;
544     case 8:
545         /* search */
546         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
547         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
548         break;
549     case 9:
550         /* scan */
551         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
552         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
553         ODR_MASK_SET(init->options, Z_Options_scan);
554         break;
555     case 10:
556         /* sort */
557         ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
558         ODR_MASK_SET(init->options, Z_Options_namedResultSets);
559         ODR_MASK_SET(init->options, Z_Options_sort);
560         break;
561     }
562     int r = send_Z_PDU(apdu, &len);
563 }
564
565 int Zlint::nextTest()
566 {
567     m_subtst_no = 0;
568     while(m_tst_no >= 0)
569     {
570         m_tst_no++;
571         sprintf(m_session_str, "%d ", m_tst_no);
572         switch(m_tst_no)
573         {
574         case 0:
575             return 1;
576         case 1:
577             return 1;
578         case 2:
579             return 1;
580         case 3:
581             return 1;
582         case 4:
583             return 1;
584         case 5:
585             return 1;
586         case 6:
587             return 1;
588         case 7:
589             return 1;
590         case 8:
591             m_query_no = 0;
592             m_record_syntax_no = 0;
593             m_got_result_set = 0;
594             return 1;
595         case 9:
596             m_scan_no = 0;
597             return 1;
598         case 10:
599             m_sort_no = 0;
600             return 1;
601         default:
602             m_tst_no = -1;
603         }
604     }
605     return 0;
606 }
607
608 // current test failed badly - goto next or stop..
609 void Zlint::closeNextTest()
610 {
611     close();
612     if (m_tst_no != 0)
613     {
614         nextTest();
615         connect();
616     }
617 }
618
619 void Zlint::failNotify()
620 {
621     yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
622     testContinue();
623 }
624
625 void Zlint::timeoutNotify()
626 {
627     yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
628     testContinue();
629 }
630
631 void Zlint::testContinue()
632 {
633     close();
634     switch(m_tst_no)
635     {
636     case 8:
637         if (m_got_result_set)
638         {
639             // must search again to establish.. keep query
640             m_got_result_set = 0;
641             m_record_syntax_no++;
642         }
643         else
644         {
645             // try new search ..
646             m_query_no++;
647         }
648         connect();
649         return;
650     case 9:
651         m_scan_no++;
652         connect();
653         return;
654     }
655     nextTest();
656 }
657
658 void Zlint::sendTest()
659 {
660     Z_APDU *apdu;
661     switch(m_tst_no)
662     {
663     case 8:
664         if (m_got_result_set == 0)
665         {
666             apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
667             Z_SearchRequest *sr;
668             sr = apdu->u.searchRequest;
669             sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
670             if (try_query[m_query_no] && sr)
671             {
672                 sr->query->which = Z_Query_type_1;
673                 Z_RPNQuery *rpn;
674                 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
675                 
676                 sr->databaseNames = &m_database;
677                 sr->num_databaseNames = 1;
678                 
679                 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
680
681                 yaz_pqf_destroy (pqf_parser);
682
683                 if (rpn)
684                 {
685                     int len;
686                     yaz_log(LOG_LOG, "%spqf: %s",
687                             m_session_str, try_query[m_query_no]);
688
689                     sr->query->u.type_1 = rpn;
690                     send_Z_PDU(apdu, &len);
691                 }
692                 else
693                     closeNextTest();
694             }
695             else
696             {
697                 yaz_log(LOG_WARN, "%sunable to get any hit count", 
698                         m_session_str);
699                 closeNextTest();
700             }
701         }
702         else if (m_got_result_set && try_syntax[m_record_syntax_no])
703         {
704             int len;
705             apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
706             Z_PresentRequest *pr = apdu->u.presentRequest;
707             *pr->numberOfRecordsRequested = 1;
708             *pr->resultSetStartPoint = 1;
709             
710             pr->preferredRecordSyntax =
711                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
712                                     try_syntax[m_record_syntax_no]);
713             send_Z_PDU(apdu, &len);
714         }
715         else
716             closeNextTest();
717         break;
718     case 9:
719         apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
720         if (apdu && try_scan[m_scan_no])
721         {
722             int len;
723             YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
724             Z_ScanRequest *sr = apdu->u.scanRequest;
725             sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
726                                                      odr_encode(),
727                                                      &sr->attributeSet,
728                                                      try_scan[m_scan_no]);
729
730             sr->databaseNames = &m_database;
731             sr->num_databaseNames = 1;
732
733             yaz_pqf_destroy (pqf_parser);
734             send_Z_PDU(apdu, &len);
735         }
736         else
737             closeNextTest();
738         break;
739     case 10:
740         apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
741         if (apdu && try_sort[m_sort_no])
742         {
743             char *setstring = "default";
744             int len;
745             Z_SortRequest *sr = apdu->u.sortRequest;
746
747             sr->num_inputResultSetNames = 1;
748             sr->num_inputResultSetNames = 1;
749             sr->inputResultSetNames = (Z_InternationalString **)
750                 odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
751             sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
752             sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
753             sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
754             send_Z_PDU(apdu, &len);
755         }
756         else
757             closeNextTest();
758         break;
759     default:
760         closeNextTest();
761     }
762 }
763
764 IYaz_PDU_Observer *Zlint::sessionNotify(
765     IYaz_PDU_Observable *the_PDU_Observable, int fd)
766 {
767     return 0;
768 }
769
770 void Zlint::connect()
771 {
772     if (m_host && m_tst_no != -1)
773     {
774         yaz_log(LOG_LOG, "%sconnecting to %s", m_session_str, m_host);
775         timeout(m_timeout_connect);
776         client(m_host);
777     }
778 }
779
780 void Zlint::args(int argc, char **argv)
781 {
782     char *arg;
783     int ret;
784     while ((ret = options("v", argv, argc, &arg)) != -2)
785     {
786         switch (ret)
787         {
788         case 'v':
789             break;
790         case 0:
791             if (arg)
792             {
793                 const char *basep;
794                 m_host = xstrdup(arg);
795                 cs_get_host_args(m_host, &basep);
796                 if (!basep || !*basep)
797                     basep = "Default";
798                 m_database = xstrdup(basep);
799             }
800             break;
801         }
802     }
803 }
804
805 int main(int argc, char **argv)
806 {
807     Yaz_SocketManager mySocketManager;
808     Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
809     
810     z.args(argc, argv);
811
812     z.connect();
813     while (mySocketManager.processEvent() > 0)
814         ;
815     exit (0);
816 }