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