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