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