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