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