bda730ff32ed418a4ffdf42b9db116fa736080a0
[yazpp-moved-to-github.git] / src / yaz-my-client.cpp
1 /*
2  * Copyright (c) 1998-2001, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: yaz-my-client.cpp,v 1.11 2002-10-09 12:50:26 adam Exp $
6  */
7
8 #include <yaz/log.h>
9 #include <yaz/options.h>
10 #include <yaz/diagbib1.h>
11 #include <yaz/marcdisp.h>
12 #include <yaz++/ir-assoc.h>
13 #include <yaz++/pdu-assoc.h>
14 #include <yaz++/socket-manager.h>
15
16 #if HAVE_YAZ_URSULA_H
17 #include <yaz/zes-ursula.h>
18 #endif
19
20 extern "C" {
21 #if HAVE_READLINE_READLINE_H
22 #include <readline/readline.h>
23 #endif
24 #if HAVE_READLINE_HISTORY_H
25 #include <readline/history.h>
26 #endif
27 }
28
29 class YAZ_EXPORT MyClient : public Yaz_IR_Assoc {
30 private:
31     int m_interactive_flag;
32     char m_thisCommand[1024];
33     char m_lastCommand[1024];
34     int m_setOffset;
35     Yaz_SocketManager *m_socketManager;
36 public:
37     MyClient(IYaz_PDU_Observable *the_PDU_Observable,
38              Yaz_SocketManager *the_SocketManager);
39     IYaz_PDU_Observer *sessionNotify(
40         IYaz_PDU_Observable *the_PDU_Observable, int fd);
41     int args(Yaz_SocketManager *socketManager, int argc, char **argv);
42     int interactive(Yaz_SocketManager *socketManager);
43     int wait();
44     void recv_initResponse(Z_InitResponse *initResponse);
45     void recv_searchResponse(Z_SearchResponse *searchResponse);
46     void recv_presentResponse(Z_PresentResponse *presentResponse);
47     void recv_records (Z_Records *records);
48     void recv_diagrecs(Z_DiagRec **pp, int num);
49     void recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset);
50     void recv_record(Z_DatabaseRecord *record, int offset,
51                      const char *databaseName);
52     void recv_textRecord(int type, const char *buf, size_t len);
53     void recv_genericRecord(Z_GenericRecord *r);
54 #if HAVE_YAZ_URSULA_H
55     void recv_extendedServicesResponse(Z_ExtendedServicesResponse *extendedServicesResponse);
56 #endif
57     void display_genericRecord(Z_GenericRecord *r, int level);
58     void display_variant(Z_Variant *v, int level);
59     void connectNotify();
60     void failNotify();
61     void timeoutNotify();
62     char *get_cookie (Z_OtherInformation **oi);
63     int processCommand(const char *cmd);
64     const char *MyClient::getCommand();
65     int cmd_open(char *host);
66     int cmd_connect(char *host);
67     int cmd_quit(char *args);
68     int cmd_close(char *args);
69     int cmd_find(char *args);
70     int cmd_show(char *args);
71     int cmd_cookie(char *args);
72     int cmd_init(char *args);
73     int cmd_format(char *args);
74     int cmd_proxy(char *args);
75 #if HAVE_YAZ_URSULA_H
76     int cmd_ursula(char *args);
77     int cmd_ursula_renew(char *args);
78 #endif
79 };
80
81
82 void MyClient::connectNotify()
83 {
84     printf ("Connection accepted by target\n");
85     set_lastReceived(-1);
86 }
87
88 void MyClient::timeoutNotify()
89 {
90     printf ("Connection timeout\n");
91     close();
92 }
93
94 void MyClient::failNotify()
95 {
96     printf ("Connection closed by target\n");
97     set_lastReceived(-1);
98 }
99
100 IYaz_PDU_Observer *MyClient::sessionNotify(
101     IYaz_PDU_Observable *the_PDU_Observable, int fd)
102
103     return new MyClient(the_PDU_Observable, m_socketManager);
104 }
105
106 MyClient::MyClient(IYaz_PDU_Observable *the_PDU_Observable,
107                    Yaz_SocketManager *the_socketManager) :
108     Yaz_IR_Assoc (the_PDU_Observable)
109 {
110     m_setOffset = 1;
111     m_interactive_flag = 1;
112     m_thisCommand[0] = '\0';
113     m_lastCommand[0] = '\0';
114     m_socketManager = the_socketManager;
115 }
116
117 void usage(char *prog)
118 {
119     fprintf (stderr, "%s: [-v log] [-c cookie] [-p proxy] [zurl]\n", prog);
120     exit (1);
121 }
122
123 char *MyClient::get_cookie(Z_OtherInformation **otherInfo)
124 {
125     int oid[OID_SIZE];
126     Z_OtherInformationUnit *oi;
127     struct oident ent;
128     ent.proto = PROTO_Z3950;
129     ent.oclass = CLASS_USERINFO;
130     ent.value = (oid_value) VAL_COOKIE;
131
132     if (oid_ent_to_oid (&ent, oid) && 
133         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
134         oi->which == Z_OtherInfo_characterInfo)
135         return oi->information.characterInfo;
136     return 0;
137 }
138
139 void MyClient::recv_initResponse(Z_InitResponse *initResponse)
140 {
141     printf ("Got InitResponse. Status ");
142     if (*initResponse->result)
143     {
144         printf ("Ok\n");
145
146         const char *p = get_cookie (&initResponse->otherInfo);
147         if (p)
148         {
149             printf ("cookie = %s\n", p);
150             set_cookie(p);
151         }
152     }
153     else
154         printf ("Fail\n");
155 }
156
157 void MyClient::recv_diagrecs(Z_DiagRec **pp, int num)
158 {
159     int i;
160     oident *ent;
161     Z_DefaultDiagFormat *r;
162
163     printf("Diagnostic message(s) from database:\n");
164     for (i = 0; i<num; i++)
165     {
166         Z_DiagRec *p = pp[i];
167         if (p->which != Z_DiagRec_defaultFormat)
168         {
169             printf("Diagnostic record not in default format.\n");
170             return;
171         }
172         else
173             r = p->u.defaultFormat;
174         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
175             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
176             printf("Missing or unknown diagset\n");
177         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
178 #ifdef ASN_COMPILED
179         switch (r->which)
180         {
181         case Z_DefaultDiagFormat_v2Addinfo:
182             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
183             break;
184         case Z_DefaultDiagFormat_v3Addinfo:
185             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
186             break;
187         }
188 #else
189         if (r->addinfo && *r->addinfo)
190             printf(" -- '%s'\n", r->addinfo);
191         else
192             printf("\n");
193 #endif
194     }
195 }
196
197 void MyClient::recv_textRecord(int type, const char *buf, size_t len)
198 {
199     fwrite (buf, 1, len, stdout);
200     fputc ('\n', stdout);
201 }
202
203 void MyClient::display_variant(Z_Variant *v, int level)
204 {
205     int i;
206
207     for (i = 0; i < v->num_triples; i++)
208     {
209         printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
210             *v->triples[i]->type);
211         if (v->triples[i]->which == Z_Triple_internationalString)
212             printf(",value=%s\n", v->triples[i]->value.internationalString);
213         else
214             printf("\n");
215     }
216 }
217
218 void MyClient::display_genericRecord(Z_GenericRecord *r, int level)
219 {
220     int i;
221
222     if (!r)
223         return;
224     for (i = 0; i < r->num_elements; i++)
225     {
226         Z_TaggedElement *t;
227
228         printf("%*s", level * 4, "");
229         t = r->elements[i];
230         printf("(");
231         if (t->tagType)
232             printf("%d,", *t->tagType);
233         else
234             printf("?,");
235         if (t->tagValue->which == Z_StringOrNumeric_numeric)
236             printf("%d) ", *t->tagValue->u.numeric);
237         else
238             printf("%s) ", t->tagValue->u.string);
239         if (t->content->which == Z_ElementData_subtree)
240         {
241             printf("\n");
242             display_genericRecord(t->content->u.subtree, level+1);
243         }
244         else if (t->content->which == Z_ElementData_string)
245             printf("%s\n", t->content->u.string);
246         else if (t->content->which == Z_ElementData_numeric)
247             printf("%d\n", *t->content->u.numeric);
248         else if (t->content->which == Z_ElementData_oid)
249         {
250             int *ip = t->content->u.oid;
251             oident *oent;
252
253             if ((oent = oid_getentbyoid(t->content->u.oid)))
254                 printf("OID: %s\n", oent->desc);
255             else
256             {
257                 printf("{");
258                 while (ip && *ip >= 0)
259                     printf(" %d", *(ip++));
260                 printf(" }\n");
261             }
262         }
263         else if (t->content->which == Z_ElementData_noDataRequested)
264             printf("[No data requested]\n");
265         else if (t->content->which == Z_ElementData_elementEmpty)
266             printf("[Element empty]\n");
267         else if (t->content->which == Z_ElementData_elementNotThere)
268             printf("[Element not there]\n");
269         else
270             printf("??????\n");
271         if (t->appliedVariant)
272             display_variant(t->appliedVariant, level+1);
273         if (t->metaData && t->metaData->supportedVariants)
274         {
275             int c;
276
277             printf("%*s---- variant list\n", (level+1)*4, "");
278             for (c = 0; c < t->metaData->num_supportedVariants; c++)
279             {
280                 printf("%*svariant #%d\n", (level+1)*4, "", c);
281                 display_variant(t->metaData->supportedVariants[c], level + 2);
282             }
283         }
284     }
285 }
286
287 void MyClient::recv_genericRecord(Z_GenericRecord *r)
288 {
289     display_genericRecord(r, 0);
290 }
291
292 void MyClient::recv_record(Z_DatabaseRecord *record, int offset,
293                            const char *databaseName)
294 {
295     Z_External *r = (Z_External*) record;
296     oident *ent = oid_getentbyoid(r->direct_reference);
297
298     /*
299      * Tell the user what we got.
300      */
301     if (r->direct_reference)
302     {
303         printf("Record type: ");
304         if (ent)
305             printf("%s\n", ent->desc);
306     }
307     /* Check if this is a known, ASN.1 type tucked away in an octet string */
308     Z_ext_typeent *etype = z_ext_getentbyref(ent->value);
309     if (ent && (r->which == Z_External_octet || r->which == Z_External_single)
310         && (etype = z_ext_getentbyref(ent->value)))
311
312     {
313         void *rr;
314         /*
315          * Call the given decoder to process the record.
316          */
317         odr_setbuf(odr_decode(), (char*)record->u.octet_aligned->buf,
318                    record->u.octet_aligned->len, 0);
319         if (!(*etype->fun)(odr_decode(), (char **)&rr, 0, 0))
320         {
321             odr_perror(odr_decode(), "Decoding constructed record.");
322             fprintf(stderr, "[Near %d]\n", odr_offset(odr_decode()));
323             fprintf(stderr, "Packet dump:\n---------\n");
324             odr_dumpBER(stderr, (char*)record->u.octet_aligned->buf,
325                         record->u.octet_aligned->len);
326             fprintf(stderr, "---------\n");
327         }
328         if (etype->what == Z_External_sutrs)
329         {
330             Z_SUTRS *sutrs = (Z_SUTRS *) rr;
331             recv_textRecord ((int) VAL_SUTRS, (const char *) sutrs->buf,
332                              (size_t) sutrs->len);
333         }
334         return;
335     }
336     if (r->which == Z_External_octet && record->u.octet_aligned->len)
337     {
338         switch (ent->value)
339         {
340         case VAL_ISO2709:
341         case VAL_UNIMARC:
342         case VAL_INTERMARC:
343         case VAL_USMARC:
344         case VAL_UKMARC:
345         case VAL_NORMARC:
346         case VAL_LIBRISMARC:
347         case VAL_DANMARC:
348         case VAL_FINMARC:
349         case VAL_MAB:
350         case VAL_CANMARC:
351         case VAL_SBN:
352         case VAL_PICAMARC:
353         case VAL_AUSMARC:
354         case VAL_IBERMARC:
355         case VAL_CATMARC:
356         case VAL_MALMARC:
357         case VAL_JPMARC:
358         case VAL_SWEMARC:
359         case VAL_SIGLEMARC:
360         case VAL_ISDSMARC:
361         case VAL_RUSMARC:
362             marc_display((char*) record->u.octet_aligned->buf,0);
363             break;
364         default:
365             recv_textRecord((int) ent->value,
366                             (const char *) record->u.octet_aligned->buf,
367                             (size_t) record->u.octet_aligned->len);
368         }
369     }
370     else if (ent && ent->value == VAL_SUTRS && r->which == Z_External_sutrs)
371         recv_textRecord((int) VAL_SUTRS, (const char *) r->u.sutrs->buf,
372                         (size_t) r->u.sutrs->len);
373     else if (ent && ent->value == VAL_GRS1 && r->which == Z_External_grs1)
374         recv_genericRecord(r->u.grs1);
375     else 
376     {
377         printf("Unknown record representation.\n");
378         if (!z_External(odr_print(), &r, 0, 0))
379         {
380             odr_perror(odr_print(), "Printing external");
381             odr_reset(odr_print());
382         }
383     }    
384 }
385
386 void MyClient::recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset)
387 {
388     if (zpr->databaseName)
389         printf("[%s]", zpr->databaseName);
390     if (zpr->which == Z_NamePlusRecord_surrogateDiagnostic)
391         recv_diagrecs(&zpr->u.surrogateDiagnostic, 1);
392     else
393         recv_record(zpr->u.databaseRecord, offset, zpr->databaseName);
394 }
395
396 void MyClient::recv_records (Z_Records *records)
397 {
398 #ifdef ASN_COMPILED
399     Z_DiagRec dr, *dr_p = &dr;
400 #endif
401     if (!records)
402         return;
403     int i;
404     switch (records->which)
405     {
406     case Z_Records_DBOSD:
407         for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++)
408             recv_namePlusRecord(records->u.databaseOrSurDiagnostics->
409                                 records[i], i + m_setOffset);
410         m_setOffset += records->u.databaseOrSurDiagnostics->num_records;
411         break;
412     case Z_Records_NSD:
413 #ifdef ASN_COMPILED
414         dr.which = Z_DiagRec_defaultFormat;
415         dr.u.defaultFormat = records->u.nonSurrogateDiagnostic;
416         recv_diagrecs (&dr_p, 1);
417 #else
418         recv_diagrecs (&records->u.nonSurrogateDiagnostic, 1);
419 #endif
420         break;
421     case Z_Records_multipleNSD:
422         recv_diagrecs (records->u.multipleNonSurDiagnostics->diagRecs,
423                        records->u.multipleNonSurDiagnostics->num_diagRecs);
424         break;
425     }
426 }
427
428 void MyClient::recv_searchResponse(Z_SearchResponse *searchResponse)
429 {
430     printf ("Got SearchResponse. Status ");
431     if (!*searchResponse->searchStatus)
432     {
433         printf ("Fail\n");
434     }
435     else
436     {
437         printf ("Ok\n");
438         printf ("Hits: %d\n", *searchResponse->resultCount);
439     }
440     recv_records (searchResponse->records);
441 }
442
443 void MyClient::recv_presentResponse(Z_PresentResponse *presentResponse)
444 {
445     printf ("Got PresentResponse\n");
446     recv_records (presentResponse->records);
447 }
448
449 #if HAVE_YAZ_URSULA_H
450 void MyClient::recv_extendedServicesResponse(Z_ExtendedServicesResponse *extendedServicesResponse)
451 {
452     printf("Got ESresponse\n");
453     printf(" OperationStatus=%d with %d diagnostics:\n", 
454         *extendedServicesResponse->operationStatus,
455          extendedServicesResponse->num_diagnostics);
456     recv_diagrecs(extendedServicesResponse->diagnostics,
457                   extendedServicesResponse->num_diagnostics);
458     //TODO: Add more info !
459 }
460 #endif
461
462 int MyClient::wait()
463 {
464     set_lastReceived(0);
465     while (m_socketManager->processEvent() > 0)
466     {
467         if (get_lastReceived())
468             return 1;
469     }
470     return 0;
471 }
472
473
474 #define C_PROMPT "Z>"
475
476 int MyClient::cmd_connect(char *host)
477 {
478     client (host);
479     timeout (10);
480     wait ();
481     timeout (0);
482     return 1;
483 }
484
485 int MyClient::cmd_open(char *host)
486 {
487     client (host);
488     timeout (10);
489     wait ();
490     timeout (0);
491     send_initRequest();
492     wait ();
493     return 1;
494 }
495
496 int MyClient::cmd_init(char *args)
497 {
498     if (send_initRequest() >= 0)
499         wait();
500     else
501         close();
502     return 1;
503 }
504
505 int MyClient::cmd_quit(char *args)
506 {
507     return 0;
508 }
509
510 int MyClient::cmd_close(char *args)
511 {
512     close();
513     return 1;
514 }
515
516 int MyClient::cmd_find(char *args)
517 {
518     Yaz_Z_Query query;
519
520     if (query.set_rpn(args) <= 0)
521     {
522         printf ("Bad RPN query\n");
523         return 1;
524     }
525     if (send_searchRequest(&query) >= 0)
526         wait();
527     else
528         printf ("Not connected\n");
529     return 1;
530 }
531
532 int MyClient::cmd_show(char *args)
533 {
534     int start = m_setOffset, number = 1;
535
536     sscanf (args, "%d %d", &start, &number);
537     m_setOffset = start;
538     if (send_presentRequest(start, number) >= 0)
539         wait();
540     else
541         printf ("Not connected\n");
542     return 1;
543 }
544
545 int MyClient::cmd_cookie(char *args)
546 {
547     set_cookie(*args ? args : 0);
548     return 1;
549 }
550
551 int MyClient::cmd_format(char *args)
552 {
553     set_preferredRecordSyntax(args);
554     return 1;
555 }
556
557 int MyClient::cmd_proxy(char *args)
558 {
559     set_proxy(args);
560     return 1;
561 }
562
563 #if HAVE_YAZ_URSULA_H
564 int MyClient::cmd_ursula(char *args)
565 {
566     Z_APDU *apdu = create_Z_PDU(Z_APDU_extendedServicesRequest);
567     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
568
569     req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003");
570 //  req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003.9.1000.105.3");
571     
572     Z_External *ext = (Z_External *) odr_malloc(odr_encode(), sizeof(*ext));
573     req->taskSpecificParameters = ext;
574     ext->direct_reference = req->packageType;
575     ext->descriptor = 0;
576     ext->indirect_reference = 0;
577     
578     ext->which = Z_External_octet;
579     ext->u.single_ASN1_type = (Odr_oct *)
580         odr_malloc (odr_encode(), sizeof(Odr_oct));
581
582     Z_UrsPDU *pdu = (Z_UrsPDU *) odr_malloc (odr_encode(), sizeof(*pdu));
583     pdu->which = Z_UrsPDU_request;
584     pdu->u.request = (Z_UrsRequest *)
585         odr_malloc (odr_encode(), sizeof(*pdu->u.request));
586     pdu->u.request->libraryNo = odr_strdup(odr_encode(), "000200");
587     pdu->u.request->borrowerTickerNo = odr_strdup(odr_encode(),"1234567973");
588     pdu->u.request->disposalType = 0;
589     pdu->u.request->lastUseDate = odr_strdup(odr_encode(),"20011224");
590 #ifdef SKIPTHIS
591     pdu->u.request->num_items = 0;
592     pdu->u.request->items = (Z_UrsRequestItem **) odr_nullval();
593 #else
594     pdu->u.request->num_items = 1;
595     pdu->u.request->items = (Z_UrsRequestItem **) 
596                 odr_malloc(odr_encode(), 1 * sizeof(Z_UrsRequestItem*) );
597     pdu->u.request->items[0] = (Z_UrsRequestItem*)
598                 odr_malloc(odr_encode(), sizeof(Z_UrsRequestItem) );
599     pdu->u.request->items[0]->id = odr_strdup(odr_encode(),"002231336x");
600     pdu->u.request->items[0]->titlePartNo=odr_strdup(odr_encode(),"31");
601 #endif
602     
603     pdu->u.request->counter = odr_strdup(odr_encode(),"HB");
604     pdu->u.request->priority = 0;
605     pdu->u.request->disposalNote = 0;
606     pdu->u.request->overrule=(bool_t*)odr_malloc(odr_encode(),sizeof(bool_t));
607     *pdu->u.request->overrule = false;
608
609     if (!z_UrsPDU (odr_encode(), &pdu, 0, ""))
610     {
611         yaz_log (LOG_LOG, "ursula encoding failed");
612         return 1;
613     }
614     char *buf = 
615             odr_getbuf (odr_encode(), &ext->u.single_ASN1_type->len, 0);
616     
617     ext->u.single_ASN1_type->buf = (unsigned char*)
618         odr_malloc (odr_encode(), ext->u.single_ASN1_type->len);
619     memcpy (ext->u.single_ASN1_type->buf, buf, ext->u.single_ASN1_type->len);
620     ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len;
621     
622     if (send_Z_PDU(apdu) >= 0)
623         wait();
624     return 1;
625 }
626
627 int MyClient::cmd_ursula_renew(char *args)
628 {
629     Z_APDU *apdu = create_Z_PDU(Z_APDU_extendedServicesRequest);
630     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
631
632     req->packageType = odr_getoidbystr(odr_encode(), "1.2.840.10003");
633     
634     Z_External *ext = (Z_External *) odr_malloc(odr_encode(), sizeof(*ext));
635     req->taskSpecificParameters = ext;
636     ext->direct_reference = req->packageType;
637     ext->descriptor = 0;
638     ext->indirect_reference = 0;
639     
640     ext->which = Z_External_octet;
641     ext->u.single_ASN1_type = (Odr_oct *)
642         odr_malloc (odr_encode(), sizeof(Odr_oct));
643
644     Z_UrsPDU *pdu = (Z_UrsPDU *) odr_malloc (odr_encode(), sizeof(*pdu));
645     pdu->which = Z_UrsPDU_renewal;
646     pdu->u.renewal = (Z_UrsRenewal *)
647            odr_malloc (odr_encode(), sizeof(*pdu->u.renewal));
648     pdu->u.renewal->libraryNo = odr_strdup(odr_encode(), "000200");
649     pdu->u.renewal->borrowerTicketNo = odr_strdup(odr_encode(),"1234567973");
650     pdu->u.renewal->num_copies=1;
651     pdu->u.renewal->copies = (Z_InternationalString **)
652             odr_malloc(odr_encode(),1* sizeof(Z_InternationalString *) );
653     pdu->u.renewal->copies[0]= odr_strdup(odr_encode(), "000035238");
654     pdu->u.renewal->newReturnDate=odr_strdup(odr_encode(), "20011224");
655     pdu->u.renewal->overrule=(bool_t*)odr_malloc(odr_encode(),sizeof(bool_t));
656     *pdu->u.renewal->overrule=false;
657
658     if (!z_UrsPDU (odr_encode(), &pdu, 0, ""))
659     {
660         yaz_log (LOG_LOG, "ursula encoding failed");
661         return 1;
662     }
663     char *buf = 
664         odr_getbuf (odr_encode(), &ext->u.single_ASN1_type->len, 0);
665     
666     ext->u.single_ASN1_type->buf = (unsigned char*)
667         odr_malloc (odr_encode(), ext->u.single_ASN1_type->len);
668     memcpy (ext->u.single_ASN1_type->buf, buf, ext->u.single_ASN1_type->len);
669     ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len;
670     
671     if (send_Z_PDU(apdu) >= 0)
672         wait();
673     return 1;
674 }
675
676
677 #endif
678
679 int MyClient::processCommand(const char *commandLine)
680 {
681     char cmdStr[1024], cmdArgs[1024];
682     cmdArgs[0] = '\0';
683     cmdStr[0] = '\0';
684     static struct {
685         char *cmd;
686         int (MyClient::*fun)(char *arg);
687         char *ad;
688     } cmd[] = {
689         {"open", &MyClient::cmd_open, "<host>[':'<port>][/<database>]"},
690         {"connect", &MyClient::cmd_connect, "<host>[':'<port>][/<database>]"},
691         {"quit", &MyClient::cmd_quit, ""},
692         {"close", &MyClient::cmd_close, ""},
693         {"find", &MyClient::cmd_find, "<query>"},
694         {"show", &MyClient::cmd_show, "[<start> [<number>]]"},
695         {"cookie", &MyClient::cmd_cookie, "<cookie>"},
696         {"init", &MyClient::cmd_init, ""},
697         {"format", &MyClient::cmd_format, "<record-syntax>"},
698         {"proxy", &MyClient::cmd_proxy, "<host>:[':'<port>]"},
699 #if HAVE_YAZ_URSULA_H
700         {"ursula", &MyClient::cmd_ursula, ""},
701         {"ursula_request", &MyClient::cmd_ursula, ""},
702         {"ursreq", &MyClient::cmd_ursula, ""},
703         {"ursnew", &MyClient::cmd_ursula_renew, ""},
704         {"ursula_renew", &MyClient::cmd_ursula_renew, ""},
705 #endif
706         {0,0,0}
707     };
708     
709     if (sscanf(commandLine, "%s %[^;]", cmdStr, cmdArgs) < 1)
710         return 1;
711     int i;
712     for (i = 0; cmd[i].cmd; i++)
713         if (!strncmp(cmd[i].cmd, cmdStr, strlen(cmdStr)))
714             break;
715     
716     int res = 1;
717     if (cmd[i].cmd) // Invoke command handler
718         res = (this->*cmd[i].fun)(cmdArgs);
719     else            // Dump help screen
720     {
721         printf("Unknown command: %s.\n", cmdStr);
722         printf("Currently recognized commands:\n");
723         for (i = 0; cmd[i].cmd; i++)
724             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
725     }
726     return res;
727 }
728
729 const char *MyClient::getCommand()
730 {
731 #if HAVE_READLINE_READLINE_H
732     // Read using GNU readline
733     char *line_in;
734     line_in=readline(C_PROMPT);
735     if (!line_in)
736         return 0;
737 #if HAVE_READLINE_HISTORY_H
738     if (*line_in)
739         add_history(line_in);
740 #endif
741     strncpy(m_thisCommand,line_in, 1023);
742     m_thisCommand[1023] = '\0';
743     free (line_in);
744 #else    
745     // Read using fgets(3)
746     printf (C_PROMPT);
747     fflush(stdout);
748     if (!fgets(m_thisCommand, 1023, stdin))
749         return 0;
750 #endif
751     // Remove trailing whitespace
752     char *cp = m_thisCommand + strlen(m_thisCommand);
753     while (cp != m_thisCommand && strchr("\t \n", cp[-1]))
754         cp--;
755     *cp = '\0';
756     cp = m_thisCommand;
757     // Remove leading spaces...
758     while (*cp && strchr ("\t \n", *cp))
759         cp++;
760     // Save command if non-empty
761     if (*cp != '\0')
762         strcpy (m_lastCommand, cp);
763     return m_lastCommand;
764 }
765
766 int MyClient::interactive(Yaz_SocketManager *socketManager)
767 {
768     const char *cmd;
769     if (!m_interactive_flag)
770         return 0;
771     while ((cmd = getCommand()))
772     {
773         if (!processCommand(cmd))
774             break;
775     }
776     return 0;
777 }
778
779 int MyClient::args(Yaz_SocketManager *socketManager, int argc, char **argv)
780 {
781     char *host = 0;
782     char *proxy = 0;
783     char *arg;
784     char *prog = argv[0];
785     int ret;
786
787     while ((ret = options("c:p:v:q", argv, argc, &arg)) != -2)
788     {
789         switch (ret)
790         {
791         case 0:
792             if (host)
793             {
794                 usage(prog);
795                 return 1;
796             }
797             host = arg;
798             break;
799         case 'p':
800             if (proxy)
801             {
802                 usage(prog);
803                 return 1;
804             }
805             set_proxy(arg);
806             break;
807         case 'c':
808             set_cookie(arg);
809             break;
810         case 'v':
811             yaz_log_init_level (yaz_log_mask_str(arg));
812             break;
813         case 'q':
814             m_interactive_flag = 0;
815             break;
816         default:
817             usage(prog);
818             return 1;
819         }
820     }
821     if (host)
822     {
823         client (host);
824         timeout (10);
825         wait ();
826         timeout (0);
827         send_initRequest();
828         wait ();
829     }
830     return 0;
831 }
832
833 int main(int argc, char **argv)
834 {
835     Yaz_SocketManager mySocketManager;
836     Yaz_PDU_Assoc *some = new Yaz_PDU_Assoc(&mySocketManager);
837
838     MyClient z(some, &mySocketManager);
839
840     if (z.args(&mySocketManager, argc, argv))
841         exit (1);
842     if (z.interactive(&mySocketManager))
843         exit (1);
844     return 0;
845 }