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