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