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