d82a8ffc5f12e13b5ab2e75d7f62e40f354a31f8
[yaz-moved-to-github.git] / client / client.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: client.c,v $
7  * Revision 1.18  1995-08-28 12:21:27  quinn
8  * Client can now ask for simple element set names.
9  *
10  * Revision 1.17  1995/08/17  12:45:02  quinn
11  * Fixed minor problems with GRS-1. Added support in c&s.
12  *
13  * Revision 1.16  1995/08/15  12:00:04  quinn
14  * Updated External
15  *
16  * Revision 1.15  1995/06/22  09:28:03  quinn
17  * Fixed bug in SUTRS processing.
18  *
19  * Revision 1.14  1995/06/19  12:37:41  quinn
20  * Added BER dumper.
21  *
22  * Revision 1.13  1995/06/16  10:29:11  quinn
23  * *** empty log message ***
24  *
25  * Revision 1.12  1995/06/15  07:44:57  quinn
26  * Moving to v3.
27  *
28  * Revision 1.11  1995/06/14  15:26:40  quinn
29  * *** empty log message ***
30  *
31  * Revision 1.10  1995/06/06  14:56:58  quinn
32  * Better diagnostics.
33  *
34  * Revision 1.9  1995/06/06  08:15:19  quinn
35  * Cosmetic.
36  *
37  * Revision 1.8  1995/06/05  10:52:22  quinn
38  * Added SCAN.
39  *
40  * Revision 1.7  1995/06/02  09:50:09  quinn
41  * Smallish.
42  *
43  * Revision 1.6  1995/05/31  08:29:21  quinn
44  * Nothing significant.
45  *
46  * Revision 1.5  1995/05/29  08:10:47  quinn
47  * Moved oid.c to util.
48  *
49  * Revision 1.4  1995/05/22  15:30:13  adam
50  * Client uses prefix query notation.
51  *
52  * Revision 1.3  1995/05/22  15:06:53  quinn
53  * *** empty log message ***
54  *
55  * Revision 1.2  1995/05/22  14:56:40  quinn
56  * *** empty log message ***
57  *
58  * Revision 1.1  1995/05/22  11:30:31  quinn
59  * Added prettier client.
60  *
61  *
62  */
63
64 /*
65  * This is the obligatory little toy client, whose primary purpose is
66  * to illustrate the use of the YAZ service-level API.
67  */
68
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <sys/time.h>
72 #include <assert.h>
73 #ifdef _AIX
74 #include <sys/select.h>
75 #endif
76
77 #include <comstack.h>
78 #include <tcpip.h>
79 #ifdef USE_XTIMOSI
80 #include <xmosi.h>
81 #endif
82
83 #include <proto.h>
84 #include <marcdisp.h>
85 #include <diagbib1.h>
86
87 #ifdef RPN_QUERY
88 #ifdef PREFIX_QUERY
89 #include <pquery.h>
90 #else
91 #include <yaz-ccl.h>
92 #endif
93 #endif
94
95 #define C_PROMPT "Z> "
96
97 static ODR out, in, print;              /* encoding and decoding streams */
98 static COMSTACK conn = 0;               /* our z-association */
99 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
100 static char database[512] = "Default";  /* Database name */
101 static int setnumber = 0;               /* current result set number */
102 static int smallSetUpperBound = 0;
103 static int largeSetLowerBound = 1;
104 static int mediumSetPresentNumber = 0;
105 static Z_ElementSetNames *elementSetNames = 0; 
106 static int setno = 1;                   /* current set offset */
107 static int protocol = PROTO_Z3950;      /* current app protocol */
108 static int recordsyntax = VAL_USMARC;
109 static ODR_MEM session_mem;                /* memory handle for init-response */
110 static Z_InitResponse *session = 0;        /* session parameters */
111 static char last_scan[512] = "0";
112 static char last_cmd[100] = "?";
113 #ifdef RPN_QUERY
114 #ifndef PREFIX_QUERY
115 static CCL_bibset bibset;               /* CCL bibset handle */
116 #endif
117 #endif
118
119 static void send_apdu(Z_APDU *a)
120 {
121     char *buf;
122     int len;
123
124     if (!z_APDU(out, &a, 0))
125     {
126         odr_perror(out, "Encoding APDU");
127         exit(1);
128     }
129     buf = odr_getbuf(out, &len, 0);
130     odr_reset(out); /* release the APDU */
131     if (cs_put(conn, buf, len) < 0)
132     {
133         fprintf(stderr, "cs_put: %s", cs_errlist[cs_errno(conn)]);
134         exit(1);
135     }
136 }
137
138 /* INIT SERVICE ------------------------------- */
139
140 static void send_initRequest()
141 {
142     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
143     Z_InitRequest *req = apdu->u.initRequest;
144
145     ODR_MASK_SET(req->options, Z_Options_search);
146     ODR_MASK_SET(req->options, Z_Options_present);
147     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
148     ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl);
149     ODR_MASK_SET(req->options, Z_Options_scan);
150
151     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
152     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
153
154     req->idAuthentication = auth;
155
156     send_apdu(apdu);
157     printf("Sent initrequest.\n");
158 }
159
160 static int process_initResponse(Z_InitResponse *res)
161 {
162     /* save session parameters for later use */
163     session_mem = odr_extract_mem(in);
164     session = res;
165
166     if (!*res->result)
167         printf("Connection rejected by target.\n");
168     else
169         printf("Connection accepted by target.\n");
170     if (res->implementationId)
171         printf("ID     : %s\n", res->implementationId);
172     if (res->implementationName)
173         printf("Name   : %s\n", res->implementationName);
174     if (res->implementationVersion)
175         printf("Version: %s\n", res->implementationVersion);
176     if (res->userInformationField)
177     {
178         printf("UserInformationfield:\n");
179         if (!z_External(print, (Z_External**)&res-> userInformationField,
180             0))
181         {
182             odr_perror(print, "Printing userinfo\n");
183             odr_reset(print);
184         }
185         if (res->userInformationField->which == ODR_EXTERNAL_octet)
186         {
187             printf("Guessing visiblestring:\n");
188             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
189         }
190     }
191     return 0;
192 }
193
194 int cmd_open(char *arg)
195 {
196     void *add;
197     char type[100], addr[100];
198     CS_TYPE t;
199
200     if (conn)
201     {
202         printf("Already connected.\n");
203         return 0;
204     }
205     if (!*arg || sscanf(arg, "%[^:]:%s", type, addr) < 2)
206     {
207         fprintf(stderr, "Usage: open (osi|tcp) ':' [tsel '/']host[':'port]\n");
208         return 0;
209     }
210 #ifdef USE_XTIMOSI
211     if (!strcmp(type, "osi"))
212     {
213         if (!(add = mosi_strtoaddr(addr)))
214         {
215             perror(arg);
216             return 0;
217         }
218         t = mosi_type;
219         protocol = PROTO_SR;
220     }
221     else
222 #endif
223     if (!strcmp(type, "tcp"))
224     {
225         if (!(add = tcpip_strtoaddr(addr)))
226         {
227             perror(arg);
228             return 0;
229         }
230         t = tcpip_type;
231         protocol = PROTO_Z3950;
232     }
233     else
234     {
235         fprintf(stderr, "Bad type: %s\n", type);
236         return 0;
237     }
238     if (!(conn = cs_create(t, 1, protocol)))
239     {
240         perror("cs_create");
241         return 0;
242     }
243     printf("Connecting...");
244     fflush(stdout);
245     if (cs_connect(conn, add) < 0)
246     {
247         perror("connect");
248         cs_close(conn);
249         conn = 0;
250         return 0;
251     }
252     printf("Ok.\n");
253     send_initRequest();
254     return 2;
255 }
256
257 int cmd_authentication(char *arg)
258 {
259     static Z_IdAuthentication au;
260     static char open[256];
261
262     if (!*arg)
263     {
264         printf("Auth field set to null\n");
265         auth = 0;
266         return 1;
267     }
268     auth = &au;
269     au.which = Z_IdAuthentication_open;
270     au.u.open = open;
271     strcpy(open, arg);
272     return 1;
273 }
274
275 /* SEARCH SERVICE ------------------------------ */
276
277 void display_grs1(Z_GenericRecord *r, int level)
278 {
279     int i;
280
281     if (!r)
282         return;
283     for (i = 0; i < r->num_elements; i++)
284     {
285         Z_TaggedElement *t;
286
287         printf("%*s", level * 4, "");
288         t = r->elements[i];
289         printf("(");
290         if (t->tagType)
291             printf("%d,", *t->tagType);
292         else
293             printf("?,");
294         if (t->tagValue->which == Z_StringOrNumeric_numeric)
295             printf("%d) ", *t->tagValue->u.numeric);
296         else
297             printf("%s) ", t->tagValue->u.string);
298         if (t->content->which == Z_ElementData_subtree)
299         {
300             printf("\n");
301             display_grs1(t->content->u.subtree, level+1);
302         }
303         else if (t->content->which == Z_ElementData_string)
304             printf("%s\n", t->content->u.string);
305         else
306             printf("??????\n");
307     }
308 }
309
310 void display_record(Z_DatabaseRecord *p)
311 {
312     Z_External *r = (Z_External*) p;
313     oident *ent = oid_getentbyoid(r->direct_reference);
314
315     if (r->direct_reference)
316     {
317         printf("Record type: ");
318         if (ent)
319             printf("%s\n", ent->desc);
320         else if (!odr_oid(print, &r->direct_reference, 0))
321         {
322             odr_perror(print, "print oid");
323             odr_reset(print);
324         }
325     }
326     if (r->which == ODR_EXTERNAL_octet && p->u.octet_aligned->len)
327         marc_display ((char*)p->u.octet_aligned->buf, stdout);
328     else if (ent->value == VAL_SUTRS)
329     {
330         if (r->which != Z_External_sutrs)
331         {
332             printf("Expecting single SUTRS type for SUTRS.\n");
333             return;
334         }
335         printf("%.*s", r->u.sutrs->len, r->u.sutrs->buf);
336     }
337     else if (ent->value == VAL_GRS1)
338     {
339         if (r->which != Z_External_grs1)
340         {
341             printf("Expecting single GRS type for GRS.\n");
342             return;
343         }
344         display_grs1(r->u.grs1, 0);
345     }
346     else 
347     {
348         printf("Unknown record representation.\n");
349         if (!z_External(print, &r, 0))
350         {
351             odr_perror(print, "Printing external");
352             odr_reset(print);
353         }
354     }
355 }
356
357 static void display_diagrec(Z_DiagRec *p)
358 {
359     oident *ent;
360 #ifdef Z_95
361     Z_DefaultDiagFormat *r;
362 #else
363     Z_DiagRec *r = p;
364 #endif
365
366     printf("Diagnostic message from database:\n");
367 #ifdef Z_95
368     if (p->which != Z_DiagRec_defaultFormat)
369     {
370         printf("Diagnostic record not in default format.\n");
371         return;
372     }
373     else
374         r = p->u.defaultFormat;
375 #endif
376     if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
377         ent->class != CLASS_DIAGSET || ent->value != VAL_BIB1)
378         printf("Missing or unknown diagset\n");
379     printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
380     if (r->addinfo && *r->addinfo)
381         printf(" -- %s\n", r->addinfo);
382     else
383         printf("\n");
384 }
385
386 static void display_nameplusrecord(Z_NamePlusRecord *p)
387 {
388     if (p->databaseName)
389         printf("[%s]", p->databaseName);
390     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
391         display_diagrec(p->u.surrogateDiagnostic);
392     else
393         display_record(p->u.databaseRecord);
394 }
395
396 static void display_records(Z_Records *p)
397 {
398     int i;
399
400     if (p->which == Z_Records_NSD)
401         display_diagrec(p->u.nonSurrogateDiagnostic);
402     else
403     {
404         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
405         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
406             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
407     }
408 }
409
410 static int send_searchRequest(char *arg)
411 {
412     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
413     Z_SearchRequest *req = apdu->u.searchRequest;
414     char *databaseNames = database;
415     Z_Query query;
416 #ifdef RPN_QUERY
417 #ifndef PREFIX_QUERY
418     struct ccl_rpn_node *rpn;
419     int error, pos;
420 #endif
421 #endif
422     char setstring[100];
423 #ifdef RPN_QUERY
424     Z_RPNQuery *RPNquery;
425     oident bib1;
426 #else
427     Odr_oct ccl_query;
428 #endif
429
430 #ifdef RPN_QUERY
431 #ifndef PREFIX_QUERY
432     rpn = ccl_find_str(bibset, arg, &error, &pos);
433     if (error)
434     {
435         printf("CCL ERROR: %s\n", ccl_err_msg(error));
436         return 0;
437     }
438 #endif
439 #endif
440
441     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
442     {
443         static unsigned char big[2100];
444         static Odr_oct bigo;
445
446         /* send a very big referenceid to test transport stack etc. */
447         memset(big, 'A', 2100);
448         bigo.len = bigo.size = 2100;
449         bigo.buf = big;
450         req->referenceId = &bigo;
451     }
452
453     if (setnumber >= 0)
454     {
455         sprintf(setstring, "%d", ++setnumber);
456         req->resultSetName = setstring;
457     }
458     *req->smallSetUpperBound = smallSetUpperBound;
459     *req->largeSetLowerBound = largeSetLowerBound;
460     *req->mediumSetPresentNumber = mediumSetPresentNumber;
461     if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
462         mediumSetPresentNumber > 0))
463     {
464         oident prefsyn;
465
466         prefsyn.proto = protocol;
467         prefsyn.class = CLASS_RECSYN;
468         prefsyn.value = recordsyntax;
469         req->preferredRecordSyntax = odr_oiddup(out, oid_getoidbyent(&prefsyn));
470         req->smallSetElementSetNames =
471             req->mediumSetElementSetNames = elementSetNames;
472     }
473     req->num_databaseNames = 1;
474     req->databaseNames = &databaseNames;
475
476     req->query = &query;
477
478 #ifdef RPN_QUERY
479     query.which = Z_Query_type_1;
480
481 #ifndef PREFIX_QUERY
482     assert((RPNquery = ccl_rpn_query(rpn)));
483 #else
484     RPNquery = p_query_rpn (out, arg);
485     if (!RPNquery)
486     {
487         printf("Prefix query error\n");
488         return 0;
489     }
490 #endif
491     bib1.proto = protocol;
492     bib1.class = CLASS_ATTSET;
493     bib1.value = VAL_BIB1;
494     RPNquery->attributeSetId = oid_getoidbyent(&bib1);
495     query.u.type_1 = RPNquery;
496 #else
497     query.which = Z_Query_type_2;
498     query.u.type_2 = &ccl_query;
499     ccl_query.buf = (unsigned char*) arg;
500     ccl_query.len = strlen(arg);
501 #endif
502
503     send_apdu(apdu);
504     setno = 1;
505     printf("Sent searchRequest.\n");
506     return 2;
507 }
508
509 static int process_searchResponse(Z_SearchResponse *res)
510 {
511     if (*res->searchStatus)
512         printf("Search was a success.\n");
513     else
514         printf("Search was a bloomin' failure.\n");
515     printf("Number of hits: %d, setno %d\n",
516         *res->resultCount, setnumber);
517     printf("records returned: %d\n",
518         *res->numberOfRecordsReturned);
519     setno += *res->numberOfRecordsReturned;
520     if (res->records)
521         display_records(res->records);
522     return 0;
523 }
524
525 static int cmd_find(char *arg)
526 {
527     if (!*arg)
528     {
529         printf("Find what?\n");
530         return 0;
531     }
532     if (!conn)
533     {
534         printf("Not connected yet\n");
535         return 0;
536     }
537     if (!send_searchRequest(arg))
538         return 0;
539     return 2;
540 }
541
542 static int cmd_ssub(char *arg)
543 {
544     if (!(smallSetUpperBound = atoi(arg)))
545         return 0;
546     return 1;
547 }
548
549 static int cmd_lslb(char *arg)
550 {
551     if (!(largeSetLowerBound = atoi(arg)))
552         return 0;
553     return 1;
554 }
555
556 static int cmd_mspn(char *arg)
557 {
558     if (!(mediumSetPresentNumber = atoi(arg)))
559         return 0;
560     return 1;
561 }
562
563 static int cmd_status(char *arg)
564 {
565     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
566     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
567     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
568     return 1;
569 }
570
571 static int cmd_base(char *arg)
572 {
573     if (!*arg)
574     {
575         printf("Usage: base <database>\n");
576         return 0;
577     }
578     strcpy(database, arg);
579     return 1;
580 }
581
582 static int cmd_setnames(char *arg)
583 {
584     if (setnumber < 0)
585     {
586         printf("Set numbering enabled.\n");
587         setnumber = 0;
588     }
589     else
590     {
591         printf("Set numbering disabled.\n");
592         setnumber = -1;
593     }
594     return 1;
595 }
596
597 /* PRESENT SERVICE ----------------------------- */
598
599 static int send_presentRequest(char *arg)
600 {
601     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
602     Z_PresentRequest *req = apdu->u.presentRequest;
603     Z_RecordComposition compo;
604     oident prefsyn;
605     int nos = 1;
606     char *p;
607     char setstring[100];
608
609     if ((p = strchr(arg, '+')))
610     {
611         nos = atoi(p + 1);
612         *p = 0;
613     }
614     if (*arg)
615         setno = atoi(arg);
616
617     if (setnumber >= 0)
618     {
619         sprintf(setstring, "%d", setnumber);
620         req->resultSetId = setstring;
621     }
622     req->resultSetStartPoint = &setno;
623     req->numberOfRecordsRequested = &nos;
624     prefsyn.proto = protocol;
625     prefsyn.class = CLASS_RECSYN;
626     prefsyn.value = recordsyntax;
627     req->preferredRecordSyntax = oid_getoidbyent(&prefsyn);
628     if (elementSetNames)
629     {
630         req->recordComposition = &compo;
631         compo.which = Z_RecordComp_simple;
632         compo.u.simple = elementSetNames;
633     }
634     send_apdu(apdu);
635     printf("Sent presentRequest (%d+%d).\n", setno, nos);
636     return 2;
637 }
638
639 static int cmd_show(char *arg)
640 {
641     if (!send_presentRequest(arg))
642         return 0;
643     return 2;
644 }
645
646 int cmd_quit(char *arg)
647 {
648     printf("See you later, alligator.\n");
649     exit(0);
650 }
651
652 int cmd_cancel(char *arg)
653 {
654     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
655     Z_TriggerResourceControlRequest *req =
656         apdu->u.triggerResourceControlRequest;
657     bool_t false = 0;
658     
659     if (!session)
660     {
661         printf("Session not initialized yet\n");
662         return 0;
663     }
664     if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
665     {
666         printf("Target doesn't support cancel (trigger resource ctrl)\n");
667         return 0;
668     }
669     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
670     req->resultSetWanted = &false;
671
672     send_apdu(apdu);
673     printf("Sent cancel request\n");
674     return 2;
675 }
676
677 int send_scanrequest(char *string, int pp, int num)
678 {
679     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
680     Z_ScanRequest *req = apdu->u.scanRequest;
681     char *db = database;
682     oident attset;
683
684     req->num_databaseNames = 1;
685     req->databaseNames = &db;
686     attset.proto = protocol;
687     attset.class = CLASS_ATTSET;
688     attset.value = VAL_BIB1;
689     req->attributeSet = oid_getoidbyent(&attset);
690     req->termListAndStartPoint = p_query_scan(out, string);
691     req->numberOfTermsRequested = &num;
692     req->preferredPositionInResponse = &pp;
693     send_apdu(apdu);
694     return 2;
695 }
696
697 void display_term(Z_TermInfo *t)
698 {
699     if (t->term->which == Z_Term_general)
700     {
701         printf("%.*s (%d)\n", t->term->u.general->len, t->term->u.general->buf,
702             t->globalOccurrences ? *t->globalOccurrences : -1);
703         sprintf(last_scan, "%.*s", t->term->u.general->len,
704             t->term->u.general->buf);
705     }
706     else
707         printf("Term type not general.\n");
708 }
709
710 void process_scanResponse(Z_ScanResponse *res)
711 {
712     int i;
713
714     printf("SCAN: %d entries, position=%d\n", *res->numberOfEntriesReturned,
715         *res->positionOfTerm);
716     if (*res->scanStatus != Z_Scan_success)
717         printf("Scan returned code %d\n", *res->scanStatus);
718     if (!res->entries)
719         return;
720     if (res->entries->which == Z_ListEntries_entries)
721     {
722         Z_Entries *ent = res->entries->u.entries;
723
724         for (i = 0; i < ent->num_entries; i++)
725             if (ent->entries[i]->which == Z_Entry_termInfo)
726             {
727                 printf("%c ", i + 1 == *res->positionOfTerm ? '*' : ' ');
728                 display_term(ent->entries[i]->u.termInfo);
729             }
730             else
731                 display_diagrec(ent->entries[i]->u.surrogateDiagnostic);
732     }
733     else
734         display_diagrec(res->entries->u.nonSurrogateDiagnostics->diagRecs[0]);
735 }
736
737 int cmd_scan(char *arg)
738 {
739     if (!session)
740     {
741         printf("Session not initialized yet\n");
742         return 0;
743     }
744     if (!ODR_MASK_GET(session->options, Z_Options_scan))
745     {
746         printf("Target doesn't support scan\n");
747         return 0;
748     }
749     if (*arg)
750     {
751         if (send_scanrequest(arg, 5, 20) < 0)
752             return 0;
753     }
754     else
755         if (send_scanrequest(last_scan, 1, 20) < 0)
756             return 0;
757     return 2;
758 }
759
760 int cmd_format(char *arg)
761 {
762     if (!arg || !*arg)
763     {
764         printf("Usage: format <recordsyntax>\n");
765         return 0;
766     }
767     if (!strcmp(arg, "sutrs"))
768     {
769         printf("Preferred format is SUTRS.\n");
770         recordsyntax = VAL_SUTRS;
771         return 1;
772     }
773     else if (!strcmp(arg, "usmarc"))
774     {
775         printf("Preferred format is USMARC\n");
776         recordsyntax = VAL_USMARC;
777         return 1;
778     }
779     else if (!strcmp(arg, "danmarc"))
780     {
781         printf("Preferred format is DANMARC\n");
782         recordsyntax = VAL_DANMARC;
783         return 1;
784     }
785     else if (!strcmp(arg, "grs1"))
786     {
787         printf("Preferred format is GRS1\n");
788         recordsyntax = VAL_GRS1;
789         return 1;
790     }
791     else
792     {
793         printf("Specify one of {sutrs,usmarc,danmarc,grs1}.\n");
794         return 0;
795     }
796 }
797
798 int cmd_elements(char *arg)
799 {
800     static Z_ElementSetNames esn;
801     static char what[100];
802
803     if (!arg || !*arg)
804     {
805         printf("Usage: elements <esn>\n");
806         return 0;
807     }
808     strcpy(what, arg);
809     esn.which = Z_ElementSetNames_generic;
810     esn.u.generic = what;
811     elementSetNames = &esn;
812     return 1;
813 }
814
815 static void initialize(void)
816 {
817 #ifdef RPN_QUERY
818 #ifndef PREFIX_QUERY
819     FILE *inf;
820 #endif
821 #endif
822
823     if (!(out = odr_createmem(ODR_ENCODE)) ||
824         !(in = odr_createmem(ODR_DECODE)) ||
825         !(print = odr_createmem(ODR_PRINT)))
826     {
827         fprintf(stderr, "failed to allocate ODR streams\n");
828         exit(1);
829     }
830     setvbuf(stdout, 0, _IONBF, 0);
831
832 #ifdef RPN_QUERY
833 #ifndef PREFIX_QUERY
834     bibset = ccl_qual_mk (); 
835     inf = fopen ("default.bib", "r");
836     if (inf)
837     {
838         ccl_qual_file (bibset, inf);
839         fclose (inf);
840     }
841 #endif
842 #endif
843 }
844
845 static int client(void)
846 {
847     static struct {
848         char *cmd;
849         int (*fun)(char *arg);
850         char *ad;
851     } cmd[] = {
852         {"open", cmd_open, "('tcp'|'osi')':'[<TSEL>'/']<HOST>[':'<PORT>]"},
853         {"quit", cmd_quit, ""},
854         {"find", cmd_find, "<CCL-QUERY>"},
855         {"base", cmd_base, "<BASE-NAME>"},
856         {"show", cmd_show, "<REC#>['+'<#RECS>]"},
857         {"scan", cmd_scan, "<TERM>"},
858         {"authentication", cmd_authentication, "<ACCTSTRING>"},
859         {"lslb", cmd_lslb, "<largeSetLowerBound>"},
860         {"ssub", cmd_ssub, "<smallSetUpperBound>"},
861         {"mspn", cmd_mspn, "<mediumSetPresentNumber>"},
862         {"status", cmd_status, ""},
863         {"setnames", cmd_setnames, ""},
864         {"cancel", cmd_cancel, ""},
865         {"format", cmd_format, "<recordsyntax>"},
866         {"elements", cmd_elements, "<elementSetName>"},
867         {0,0}
868     };
869     char *netbuffer= 0;
870     int netbufferlen = 0;
871     int i;
872     Z_APDU *apdu;
873
874     while (1)
875     {
876         int res;
877         fd_set input;
878         char line[1024], word[1024], arg[1024];
879
880         FD_ZERO(&input);
881         FD_SET(0, &input);
882         if (conn)
883             FD_SET(cs_fileno(conn), &input);
884         if ((res = select(20, &input, 0, 0, 0)) < 0)
885         {
886             perror("select");
887             exit(1);
888         }
889         if (!res)
890             continue;
891         if (FD_ISSET(0, &input))
892         {
893             /* quick & dirty way to get a command line. */
894             if (!gets(line))
895                 break;
896             if ((res = sscanf(line, "%s %[^;]", word, arg)) <= 0)
897             {
898                 strcpy(word, last_cmd);
899                 *arg = '\0';
900             }
901             else if (res == 1)
902                 *arg = 0;
903             strcpy(last_cmd, word);
904             for (i = 0; cmd[i].cmd; i++)
905                 if (!strncmp(cmd[i].cmd, word, strlen(word)))
906                 {
907                     res = (*cmd[i].fun)(arg);
908                     break;
909                 }
910             if (!cmd[i].cmd) /* dump our help-screen */
911             {
912                 printf("Unknown command: %s.\n", word);
913                 printf("Currently recognized commands:\n");
914                 for (i = 0; cmd[i].cmd; i++)
915                     printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
916                 res = 1;
917             }
918             if (res < 2)
919                 printf(C_PROMPT);
920         }
921         if (conn && FD_ISSET(cs_fileno(conn), &input))
922         {
923             do
924             {
925                 if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
926                 {
927                     perror("cs_get");
928                     exit(1);
929                 }
930                 if (!res)
931                 {
932                     printf("Target closed connection.\n");
933                     exit(1);
934                 }
935                 odr_reset(in); /* release APDU from last round */
936                 odr_setbuf(in, netbuffer, res, 0);
937                 if (!z_APDU(in, &apdu, 0))
938                 {
939                     odr_perror(in, "Decoding incoming APDU");
940                     fprintf(stderr, "Packet dump:\n---------\n");
941                     odr_dumpBER(stderr, netbuffer, res);
942                     fprintf(stderr, "---------\n");
943                     exit(1);
944                 }
945 #if 0
946                 if (!z_APDU(print, &apdu, 0))
947                 {
948                     odr_perror(print, "Failed to print incoming APDU");
949                     odr_reset(print);
950                     continue;
951                 }
952 #endif
953                 switch(apdu->which)
954                 {
955                     case Z_APDU_initResponse:
956                         process_initResponse(apdu->u.initResponse);
957                         break;
958                     case Z_APDU_searchResponse:
959                         process_searchResponse(apdu->u.searchResponse);
960                         break;
961                     case Z_APDU_scanResponse:
962                         process_scanResponse(apdu->u.scanResponse);
963                         break;
964                     case Z_APDU_presentResponse:
965                         printf("Received presentResponse.\n");
966                         setno +=
967                             *apdu->u.presentResponse->numberOfRecordsReturned;
968                         if (apdu->u.presentResponse->records)
969                             display_records(apdu->u.presentResponse->records);
970                         else
971                             printf("No records.\n");
972                         break;
973                     default:
974                         printf("Received unknown APDU type (%d).\n", 
975                             apdu->which);
976                         exit(1);
977                 }
978                 printf("Z> ");
979                 fflush(stdout);
980             }
981             while (cs_more(conn));
982         }
983     }
984     return 0;
985 }
986
987 int main(int argc, char **argv)
988 {
989     initialize();
990     if (argc > 1)
991         cmd_open(argv[1]);
992     else
993         printf(C_PROMPT);
994     return client();
995 }