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