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