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