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