cf0de2671e032a71f41558d30845a5e5891a6a78
[yaz-moved-to-github.git] / client / client.c
1 /*
2  * Copyright (c) 1995-1999, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: client.c,v $
7  * Revision 1.75  1999-02-01 15:37:32  adam
8  * Fixed minor bug introduced by previous commit.
9  *
10  * Revision 1.74  1999/02/01 15:35:21  adam
11  * Added XML display.
12  *
13  * Revision 1.73  1998/10/20 13:55:43  quinn
14  * Fixed Scan bug in asn and client
15  *
16  * Revision 1.72  1998/10/20 13:23:15  quinn
17  * changed preferred pos to 1
18  *
19  * Revision 1.71  1998/10/20 13:21:43  adam
20  * Fixed scan response handler.
21  *
22  * Revision 1.70  1998/09/22 09:40:37  adam
23  * Minor changes in sort spec.
24  *
25  * Revision 1.69  1998/08/19 16:10:06  adam
26  * Changed som member names of DeleteResultSetRequest/Response.
27  *
28  * Revision 1.68  1998/07/20 12:37:06  adam
29  * Added 'delete <resultset>' command. Changed open command so that
30  * it reconnects if already connected.
31  *
32  * Revision 1.67  1998/06/09 13:55:06  adam
33  * Minor changes.
34  *
35  * Revision 1.66  1998/05/18 13:06:53  adam
36  * Changed the way attribute sets are handled by the retriaval module.
37  * Extended Explain conversion / schema.
38  * Modified server and client to work with ASN.1 compiled protocol handlers.
39  *
40  * Revision 1.65  1998/03/31 15:13:19  adam
41  * Development towards compiled ASN.1.
42  *
43  * Revision 1.64  1998/03/31 11:07:44  adam
44  * Furhter work on UNIverse resource report.
45  * Added Extended Services handling in frontend server.
46  *
47  * Revision 1.63  1998/03/05 08:05:10  adam
48  * Added a few casts to make C++ happy.
49  *
50  * Revision 1.62  1998/02/11 11:53:33  adam
51  * Changed code so that it compiles as C++.
52  *
53  * Revision 1.61  1998/02/10 11:03:06  adam
54  * Implemented command refid. Client prints reference-ID's, when present,
55  * in responses.
56  *
57  * Revision 1.60  1998/01/29 14:08:52  adam
58  * Better sort diagnostics.
59  *
60  * Revision 1.59  1998/01/29 13:17:56  adam
61  * Added sort.
62  *
63  * Revision 1.58  1998/01/07 13:51:45  adam
64  * Minor change.
65  *
66  * Revision 1.57  1998/01/07 12:58:22  adam
67  * Using fgets instead of gets.
68  *
69  * Revision 1.56  1997/11/05 09:18:31  adam
70  * The client handles records with no associated syntax.
71  *
72  * Revision 1.55  1997/10/31 12:20:08  adam
73  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
74  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
75  * Bug fix: missing fclose in data1_read_espec1.
76  *
77  * Revision 1.54  1997/10/27 13:52:46  adam
78  * Header yaz-util includes all YAZ utility header files.
79  *
80  * Revision 1.53  1997/09/29 13:18:59  adam
81  * Added function, oid_ent_to_oid, to replace the function
82  * oid_getoidbyent, which is not thread safe.
83  *
84  * Revision 1.52  1997/09/29 07:20:31  adam
85  * Client code uses nmem_init.
86  *
87  * Revision 1.51  1997/09/26 09:41:55  adam
88  * Updated client to handle multiple diagnostics.
89  *
90  * Revision 1.50  1997/09/17 12:10:29  adam
91  * YAZ version 1.4.
92  *
93  * Revision 1.49  1997/09/04 13:45:17  adam
94  * Added UNImarc to list of available syntaxes.
95  *
96  * Revision 1.48  1997/09/01 08:48:44  adam
97  * New windows NT/95 port using MSV5.0. Only a few changes made
98  * to avoid warnings. Sub project created: client.dsp.
99  *
100  * Revision 1.47  1997/07/01 13:49:56  adam
101  * Take care of case when invalid target is specified on command line.
102  *
103  * Revision 1.46  1997/06/23 10:30:18  adam
104  * Added call to ccl_rpn_delete in search. Added ODR stream "out"
105  * as parameter to ccl_rpn_query to release RPN query.
106  *
107  * Revision 1.45  1997/05/14 06:53:29  adam
108  * C++ support.
109  *
110  * Revision 1.44  1997/05/05 11:20:35  adam
111  * Client uses "options" utility and marc dump filename may be specified
112  * as an option (-m <file>).
113  *
114  * Revision 1.43  1996/11/08 11:03:26  adam
115  * Client accepts multiple database names.
116  *
117  * Revision 1.42  1996/10/08 10:44:57  quinn
118  * Resolved conflicts.
119  *
120  * Revision 1.41  1996/10/07  15:29:03  quinn
121  * Work
122  *
123  * Revision 1.40  1996/08/29  14:19:34  quinn
124  * Fixed conflict (CVS)
125  *
126  * Revision 1.39  1996/08/27  10:43:22  quinn
127  * Made select() optional
128  *
129  * Revision 1.38  1996/08/12  14:09:11  adam
130  * Default prefix query attribute set defined by using p_query_attset.
131  *
132  * Revision 1.37  1996/07/06  19:58:29  quinn
133  * System headerfiles gathered in yconfig
134  *
135  * Revision 1.36  1996/06/10  08:53:47  quinn
136  * Added Summary
137  *
138  * Revision 1.35  1996/06/03  09:45:50  quinn
139  * Added display of OIDs in the GRS routine.
140  *
141  * Revision 1.34  1996/05/09  07:26:49  quinn
142  * *** empty log message ***
143  *
144  * Revision 1.33  1996/05/09  07:25:22  quinn
145  * Small
146  *
147  * Revision 1.32  1996/03/15  11:05:33  adam
148  * The user can set the preferred query type (prefix, ccl, ..) with the
149  * querytype command.
150  *
151  * Revision 1.31  1996/02/20  12:51:54  quinn
152  * Fixed problems with EXTERNAL.
153  *
154  * Revision 1.30  1996/02/12  18:18:09  quinn
155  * Fidgeting.
156  *
157  * Revision 1.29  1996/01/02  08:57:25  quinn
158  * Changed enums in the ASN.1 .h files to #defines. Changed oident.class to oclass
159  *
160  * Revision 1.28  1995/12/14  11:09:31  quinn
161  * Added Explain record syntax to the format command.
162  *
163  * Revision 1.27  1995/12/12  16:37:02  quinn
164  * Added destroy element to data1_node.
165  *
166  * Revision 1.26  1995/12/12  14:11:00  quinn
167  * Minimal.
168  *
169  * Revision 1.25  1995/11/13  09:27:22  quinn
170  * Fiddling with the variant stuff.
171  *
172  * Revision 1.24  1995/10/30  12:41:13  quinn
173  * Added hostname lookup for server.
174  *
175  * Revision 1.23  1995/10/18  16:12:30  quinn
176  * Better diagnostics.
177  *
178  * Revision 1.22  1995/10/11  14:49:12  quinn
179  * Smallish.
180  *
181  * Revision 1.21  1995/09/29  17:01:47  quinn
182  * More Windows work
183  *
184  * Revision 1.20  1995/08/29  14:24:13  quinn
185  * Added second half of close-handshake
186  *
187  * Revision 1.19  1995/08/29  11:17:28  quinn
188  * Added code to receive close
189  *
190  * Revision 1.18  1995/08/28  12:21:27  quinn
191  * Client can now ask for simple element set names.
192  *
193  * Revision 1.17  1995/08/17  12:45:02  quinn
194  * Fixed minor problems with GRS-1. Added support in c&s.
195  *
196  * Revision 1.16  1995/08/15  12:00:04  quinn
197  * Updated External
198  *
199  * Revision 1.15  1995/06/22  09:28:03  quinn
200  * Fixed bug in SUTRS processing.
201  *
202  * Revision 1.14  1995/06/19  12:37:41  quinn
203  * Added BER dumper.
204  *
205  * Revision 1.13  1995/06/16  10:29:11  quinn
206  * *** empty log message ***
207  *
208  * Revision 1.12  1995/06/15  07:44:57  quinn
209  * Moving to v3.
210  *
211  * Revision 1.11  1995/06/14  15:26:40  quinn
212  * *** empty log message ***
213  *
214  * Revision 1.10  1995/06/06  14:56:58  quinn
215  * Better diagnostics.
216  *
217  * Revision 1.9  1995/06/06  08:15:19  quinn
218  * Cosmetic.
219  *
220  * Revision 1.8  1995/06/05  10:52:22  quinn
221  * Added SCAN.
222  *
223  * Revision 1.7  1995/06/02  09:50:09  quinn
224  * Smallish.
225  *
226  * Revision 1.6  1995/05/31  08:29:21  quinn
227  * Nothing significant.
228  *
229  * Revision 1.5  1995/05/29  08:10:47  quinn
230  * Moved oid.c to util.
231  *
232  * Revision 1.4  1995/05/22  15:30:13  adam
233  * Client uses prefix query notation.
234  *
235  * Revision 1.3  1995/05/22  15:06:53  quinn
236  * *** empty log message ***
237  *
238  * Revision 1.2  1995/05/22  14:56:40  quinn
239  * *** empty log message ***
240  *
241  * Revision 1.1  1995/05/22  11:30:31  quinn
242  * Added prettier client.
243  *
244  *
245  */
246
247 /*
248  * This is the obligatory little toy client, whose primary purpose is
249  * to illustrate the use of the YAZ service-level API.
250  */
251
252 #include <stdio.h>
253 #include <stdlib.h>
254 #include <time.h>
255 #include <assert.h>
256
257 #include <yaz-util.h>
258
259 #include <comstack.h>
260 #include <tcpip.h>
261 #ifdef USE_XTIMOSI
262 #include <xmosi.h>
263 #endif
264
265 #include <proto.h>
266 #include <marcdisp.h>
267 #include <diagbib1.h>
268
269 #include <pquery.h>
270
271 #if CCL2RPN
272 #include <yaz-ccl.h>
273 #endif
274
275 #define C_PROMPT "Z> "
276
277 static ODR out, in, print;              /* encoding and decoding streams */
278 static COMSTACK conn = 0;               /* our z-association */
279 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
280 static char *databaseNames[128];
281 static int num_databaseNames = 0;
282 static int setnumber = 0;               /* current result set number */
283 static int smallSetUpperBound = 0;
284 static int largeSetLowerBound = 1;
285 static int mediumSetPresentNumber = 0;
286 static Z_ElementSetNames *elementSetNames = 0; 
287 static int setno = 1;                   /* current set offset */
288 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
289 static enum oid_value recordsyntax = VAL_USMARC;
290 static int sent_close = 0;
291 static NMEM session_mem = NULL;      /* memory handle for init-response */
292 static Z_InitResponse *session = 0;     /* session parameters */
293 static char last_scan[512] = "0";
294 static char last_cmd[100] = "?";
295 static FILE *marcdump = 0;
296 static char *refid = NULL;
297
298 typedef enum {
299     QueryType_Prefix,
300     QueryType_CCL,
301     QueryType_CCL2RPN
302 } QueryType;
303
304 static QueryType queryType = QueryType_Prefix;
305
306 #if CCL2RPN
307 static CCL_bibset bibset;               /* CCL bibset handle */
308 #endif
309
310 static void send_apdu(Z_APDU *a)
311 {
312     char *buf;
313     int len;
314
315     if (!z_APDU(out, &a, 0))
316     {
317         odr_perror(out, "Encoding APDU");
318         exit(1);
319     }
320     buf = odr_getbuf(out, &len, 0);
321     if (cs_put(conn, buf, len) < 0)
322     {
323         fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
324         exit(1);
325     }
326     odr_reset(out); /* release the APDU structure  */
327 }
328
329 static void print_refid (Z_ReferenceId *id)
330 {
331     if (id)
332     {
333         printf ("ReferenceId: '%.*s'\n", id->len, id->buf);
334     }
335 }
336
337 static Z_ReferenceId *set_refid (ODR out)
338 {
339     Z_ReferenceId *id;
340     if (!refid)
341         return 0;
342     id = (Z_ReferenceId *) odr_malloc (out, sizeof(*id));
343     id->size = id->len = strlen(refid);
344     id->buf = (unsigned char *) odr_malloc (out, id->len);
345     memcpy (id->buf, refid, id->len);
346     return id;
347 }   
348
349 /* INIT SERVICE ------------------------------- */
350
351 static void send_initRequest()
352 {
353     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
354     Z_InitRequest *req = apdu->u.initRequest;
355
356     ODR_MASK_SET(req->options, Z_Options_search);
357     ODR_MASK_SET(req->options, Z_Options_present);
358     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
359     ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl);
360     ODR_MASK_SET(req->options, Z_Options_scan);
361     ODR_MASK_SET(req->options, Z_Options_sort);
362     ODR_MASK_SET(req->options, Z_Options_extendedServices);
363     ODR_MASK_SET(req->options, Z_Options_delSet);
364
365     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
366     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
367     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
368
369     *req->maximumRecordSize = 1024*1024;
370
371     req->idAuthentication = auth;
372
373     send_apdu(apdu);
374     printf("Sent initrequest.\n");
375 }
376
377 static int process_initResponse(Z_InitResponse *res)
378 {
379     /* save session parameters for later use */
380     session_mem = odr_extract_mem(in);
381     session = res;
382
383     if (!*res->result)
384         printf("Connection rejected by target.\n");
385     else
386         printf("Connection accepted by target.\n");
387     if (res->implementationId)
388         printf("ID     : %s\n", res->implementationId);
389     if (res->implementationName)
390         printf("Name   : %s\n", res->implementationName);
391     if (res->implementationVersion)
392         printf("Version: %s\n", res->implementationVersion);
393     if (res->userInformationField)
394     {
395         printf("UserInformationfield:\n");
396         if (!z_External(print, (Z_External**)&res-> userInformationField,
397             0))
398         {
399             odr_perror(print, "Printing userinfo\n");
400             odr_reset(print);
401         }
402         if (res->userInformationField->which == Z_External_octet)
403         {
404             printf("Guessing visiblestring:\n");
405             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
406         }
407         odr_reset (print);
408     }
409     return 0;
410 }
411
412 static int cmd_base(char *arg)
413 {
414     int i;
415     char *cp;
416
417     if (!*arg)
418     {
419         printf("Usage: base <database> <database> ...\n");
420         return 0;
421     }
422     for (i = 0; i<num_databaseNames; i++)
423         xfree (databaseNames[i]);
424     num_databaseNames = 0;
425     while (1)
426     {
427         if (!(cp = strchr(arg, ' ')))
428             cp = arg + strlen(arg);
429         if (cp - arg < 1)
430             break;
431         databaseNames[num_databaseNames] = (char *)xmalloc (1 + cp - arg);
432         memcpy (databaseNames[num_databaseNames], arg, cp - arg);
433         databaseNames[num_databaseNames++][cp - arg] = '\0';
434         if (!*cp)
435             break;
436         arg = cp+1;
437     }
438     return 1;
439 }
440
441
442 int cmd_open(char *arg)
443 {
444     void *add;
445     char type[100], addr[100], base[100];
446     CS_TYPE t;
447
448     if (conn)
449     {
450         printf("Already connected.\n");
451
452         cs_close (conn);
453         conn = NULL;
454         if (session_mem)
455         {
456             nmem_destroy (session_mem);
457             session_mem = NULL;
458         }
459     }
460     base[0] = '\0';
461     if (!*arg || sscanf(arg, "%[^:]:%[^/]/%s", type, addr, base) < 2)
462     {
463         fprintf(stderr, "Usage: open (osi|tcp) ':' [tsel '/']host[':'port]\n");
464         return 0;
465     }
466     if (*base)
467         cmd_base (base);
468     if (!strcmp(type, "tcp"))
469     {
470         t = tcpip_type;
471         protocol = PROTO_Z3950;
472     }
473     else
474 #ifdef USE_XTIMOSI
475     if (!strcmp(type, "osi"))
476     {
477         t = mosi_type;
478         protocol = PROTO_SR;
479     }
480     else
481 #endif
482     {
483         fprintf(stderr, "Bad type: %s\n", type);
484         return 0;
485     }
486     if (!(conn = cs_create(t, 1, protocol)))
487     {
488         perror("cs_create");
489         return 0;
490     }
491     if (!(add = cs_straddr(conn, addr)))
492     {
493         perror(arg);
494         return 0;
495     }
496     printf("Connecting...");
497     fflush(stdout);
498     if (cs_connect(conn, add) < 0)
499     {
500         perror("connect");
501         cs_close(conn);
502         conn = 0;
503         return 0;
504     }
505     printf("Ok.\n");
506     send_initRequest();
507     return 2;
508 }
509
510 int cmd_authentication(char *arg)
511 {
512     static Z_IdAuthentication au;
513     static char open[256];
514
515     if (!*arg)
516     {
517         printf("Auth field set to null\n");
518         auth = 0;
519         return 1;
520     }
521     auth = &au;
522     au.which = Z_IdAuthentication_open;
523     au.u.open = open;
524     strcpy(open, arg);
525     return 1;
526 }
527
528 /* SEARCH SERVICE ------------------------------ */
529
530 static void display_variant(Z_Variant *v, int level)
531 {
532     int i;
533
534     for (i = 0; i < v->num_triples; i++)
535     {
536         printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
537             *v->triples[i]->type);
538         if (v->triples[i]->which == Z_Triple_internationalString)
539             printf(",value=%s\n", v->triples[i]->value.internationalString);
540         else
541             printf("\n");
542     }
543 }
544
545 static void display_grs1(Z_GenericRecord *r, int level)
546 {
547     int i;
548
549     if (!r)
550         return;
551     for (i = 0; i < r->num_elements; i++)
552     {
553         Z_TaggedElement *t;
554
555         printf("%*s", level * 4, "");
556         t = r->elements[i];
557         printf("(");
558         if (t->tagType)
559             printf("%d,", *t->tagType);
560         else
561             printf("?,");
562         if (t->tagValue->which == Z_StringOrNumeric_numeric)
563             printf("%d) ", *t->tagValue->u.numeric);
564         else
565             printf("%s) ", t->tagValue->u.string);
566         if (t->content->which == Z_ElementData_subtree)
567         {
568             printf("\n");
569             display_grs1(t->content->u.subtree, level+1);
570         }
571         else if (t->content->which == Z_ElementData_string)
572             printf("%s\n", t->content->u.string);
573         else if (t->content->which == Z_ElementData_numeric)
574             printf("%d\n", *t->content->u.numeric);
575         else if (t->content->which == Z_ElementData_oid)
576         {
577             int *ip = t->content->u.oid;
578             oident *oent;
579
580             if ((oent = oid_getentbyoid(t->content->u.oid)))
581                 printf("OID: %s\n", oent->desc);
582             else
583             {
584                 printf("{");
585                 while (ip && *ip >= 0)
586                     printf(" %d", *(ip++));
587                 printf(" }\n");
588             }
589         }
590         else if (t->content->which == Z_ElementData_noDataRequested)
591             printf("[No data requested]\n");
592         else if (t->content->which == Z_ElementData_elementEmpty)
593             printf("[Element empty]\n");
594         else if (t->content->which == Z_ElementData_elementNotThere)
595             printf("[Element not there]\n");
596         else
597             printf("??????\n");
598         if (t->appliedVariant)
599             display_variant(t->appliedVariant, level+1);
600         if (t->metaData && t->metaData->supportedVariants)
601         {
602             int c;
603
604             printf("%*s---- variant list\n", (level+1)*4, "");
605             for (c = 0; c < t->metaData->num_supportedVariants; c++)
606             {
607                 printf("%*svariant #%d\n", (level+1)*4, "", c);
608                 display_variant(t->metaData->supportedVariants[c], level + 2);
609             }
610         }
611     }
612 }
613
614 static void display_record(Z_DatabaseRecord *p)
615 {
616     Z_External *r = (Z_External*) p;
617     oident *ent = oid_getentbyoid(r->direct_reference);
618
619     /*
620      * Tell the user what we got.
621      */
622     if (r->direct_reference)
623     {
624         printf("Record type: ");
625         if (ent)
626             printf("%s\n", ent->desc);
627         else if (!odr_oid(print, &r->direct_reference, 0))
628         {
629             odr_perror(print, "print oid");
630             odr_reset(print);
631         }
632     }
633     /* Check if this is a known, ASN.1 type tucked away in an octet string */
634     if (ent && r->which == Z_External_octet)
635     {
636         Z_ext_typeent *type = z_ext_getentbyref(ent->value);
637         void *rr;
638
639         if (type)
640         {
641             /*
642              * Call the given decoder to process the record.
643              */
644             odr_setbuf(in, (char*)p->u.octet_aligned->buf,
645                 p->u.octet_aligned->len, 0);
646             if (!(*type->fun)(in, (char **)&rr, 0))
647             {
648                 odr_perror(in, "Decoding constructed record.");
649                 fprintf(stderr, "[Near %d]\n", odr_offset(in));
650                 fprintf(stderr, "Packet dump:\n---------\n");
651                 odr_dumpBER(stderr, (char*)p->u.octet_aligned->buf,
652                     p->u.octet_aligned->len);
653                 fprintf(stderr, "---------\n");
654                 exit(1);
655             }
656             /*
657              * Note: we throw away the original, BER-encoded record here.
658              * Do something else with it if you want to keep it.
659              */
660             r->u.sutrs = (Z_SUTRS *) rr; /* we don't actually check the type here. */
661             r->which = type->what;
662         }
663     }
664     if (ent && ent->value == VAL_SOIF)
665         printf("%.*s", r->u.octet_aligned->len, r->u.octet_aligned->buf);
666     else if (r->which == Z_External_octet && p->u.octet_aligned->len)
667     {
668         const char *octet_buf = (char*)p->u.octet_aligned->buf;
669         if (ent->value == VAL_TEXT_XML || ent->value == VAL_APPLICATION_XML)
670         {
671             fwrite (octet_buf, 1, p->u.octet_aligned->len, stdout);
672             printf ("\n");
673         }
674         else
675             marc_display (octet_buf, NULL);
676         if (marcdump)
677             fwrite (octet_buf, 1, p->u.octet_aligned->len, marcdump);
678     }
679     else if (ent && ent->value == VAL_SUTRS)
680     {
681         if (r->which != Z_External_sutrs)
682         {
683             printf("Expecting single SUTRS type for SUTRS.\n");
684             return;
685         }
686         printf("%.*s", r->u.sutrs->len, r->u.sutrs->buf);
687     }
688     else if (ent && ent->value == VAL_GRS1)
689     {
690         if (r->which != Z_External_grs1)
691         {
692             printf("Expecting single GRS type for GRS.\n");
693             return;
694         }
695         display_grs1(r->u.grs1, 0);
696     }
697     else 
698     {
699         printf("Unknown record representation.\n");
700         if (!z_External(print, &r, 0))
701         {
702             odr_perror(print, "Printing external");
703             odr_reset(print);
704         }
705     }
706 }
707
708
709 static void display_diagrecs(Z_DiagRec **pp, int num)
710 {
711     int i;
712     oident *ent;
713     Z_DefaultDiagFormat *r;
714
715     printf("Diagnostic message(s) from database:\n");
716     for (i = 0; i<num; i++)
717     {
718         Z_DiagRec *p = pp[i];
719         if (p->which != Z_DiagRec_defaultFormat)
720         {
721             printf("Diagnostic record not in default format.\n");
722             return;
723         }
724         else
725             r = p->u.defaultFormat;
726         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
727             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
728             printf("Missing or unknown diagset\n");
729         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
730 #ifdef ASN_COMPILED
731         switch (r->which)
732         {
733         case Z_DefaultDiagFormat_v2Addinfo:
734             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
735             break;
736         case Z_DefaultDiagFormat_v3Addinfo:
737             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
738             break;
739         }
740 #else
741         if (r->addinfo && *r->addinfo)
742             printf(" -- '%s'\n", r->addinfo);
743         else
744             printf("\n");
745 #endif
746     }
747 }
748
749
750 static void display_nameplusrecord(Z_NamePlusRecord *p)
751 {
752     if (p->databaseName)
753         printf("[%s]", p->databaseName);
754     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
755         display_diagrecs(&p->u.surrogateDiagnostic, 1);
756     else
757         display_record(p->u.databaseRecord);
758 }
759
760 static void display_records(Z_Records *p)
761 {
762     int i;
763
764     if (p->which == Z_Records_NSD)
765     {
766 #ifdef ASN_COMPILED
767         Z_DiagRec dr, *dr_p = &dr;
768         dr.which = Z_DiagRec_defaultFormat;
769         dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
770         display_diagrecs (&dr_p, 1);
771 #else
772         display_diagrecs (&p->u.nonSurrogateDiagnostic, 1);
773 #endif
774     }
775     else if (p->which == Z_Records_multipleNSD)
776         display_diagrecs (p->u.multipleNonSurDiagnostics->diagRecs,
777                           p->u.multipleNonSurDiagnostics->num_diagRecs);
778     else 
779     {
780         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
781         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
782             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
783     }
784 }
785
786 static int send_deleteResultSetRequest(char *arg)
787 {
788     Z_APDU *apdu = zget_APDU(out, Z_APDU_deleteResultSetRequest);
789     Z_DeleteResultSetRequest *req = apdu->u.deleteResultSetRequest;
790
791     req->referenceId = set_refid (out);
792
793     req->num_resultSetList = 1;
794     req->resultSetList = (char **)
795         odr_malloc (out, sizeof(*req->resultSetList));
796     *req->resultSetList = arg;
797     req->deleteFunction = (int *)
798         odr_malloc (out, sizeof(*req->deleteFunction));
799     *req->deleteFunction = Z_DeleteRequest_list;
800     
801     send_apdu(apdu);
802     printf("Sent deleteResultSetRequest.\n");
803     return 2;
804 }
805
806 static int send_searchRequest(char *arg)
807 {
808     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
809     Z_SearchRequest *req = apdu->u.searchRequest;
810     Z_Query query;
811     int oid[OID_SIZE];
812 #if CCL2RPN
813     struct ccl_rpn_node *rpn = NULL;
814     int error, pos;
815     oident bib1;
816 #endif
817     char setstring[100];
818     Z_RPNQuery *RPNquery;
819     Odr_oct ccl_query;
820
821 #if CCL2RPN
822     if (queryType == QueryType_CCL2RPN)
823     {
824         rpn = ccl_find_str(bibset, arg, &error, &pos);
825         if (error)
826         {
827             printf("CCL ERROR: %s\n", ccl_err_msg(error));
828             return 0;
829         }
830     }
831 #endif
832     req->referenceId = set_refid (out);
833     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
834     {
835         static unsigned char big[2100];
836         static Odr_oct bigo;
837
838         /* send a very big referenceid to test transport stack etc. */
839         memset(big, 'A', 2100);
840         bigo.len = bigo.size = 2100;
841         bigo.buf = big;
842         req->referenceId = &bigo;
843     }
844     
845     if (setnumber >= 0)
846     {
847         sprintf(setstring, "%d", ++setnumber);
848         req->resultSetName = setstring;
849     }
850     *req->smallSetUpperBound = smallSetUpperBound;
851     *req->largeSetLowerBound = largeSetLowerBound;
852     *req->mediumSetPresentNumber = mediumSetPresentNumber;
853     if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
854         mediumSetPresentNumber > 0))
855     {
856         oident prefsyn;
857
858         prefsyn.proto = protocol;
859         prefsyn.oclass = CLASS_RECSYN;
860         prefsyn.value = recordsyntax;
861         req->preferredRecordSyntax =
862             odr_oiddup(out, oid_ent_to_oid(&prefsyn, oid));
863         req->smallSetElementSetNames =
864             req->mediumSetElementSetNames = elementSetNames;
865     }
866     req->num_databaseNames = num_databaseNames;
867     req->databaseNames = databaseNames;
868
869     req->query = &query;
870
871     switch (queryType)
872     {
873     case QueryType_Prefix:
874         query.which = Z_Query_type_1;
875         RPNquery = p_query_rpn (out, protocol, arg);
876         if (!RPNquery)
877         {
878             printf("Prefix query error\n");
879             return 0;
880         }
881         query.u.type_1 = RPNquery;
882         break;
883     case QueryType_CCL:
884         query.which = Z_Query_type_2;
885         query.u.type_2 = &ccl_query;
886         ccl_query.buf = (unsigned char*) arg;
887         ccl_query.len = strlen(arg);
888         break;
889 #if CCL2RPN
890     case QueryType_CCL2RPN:
891         query.which = Z_Query_type_1;
892         RPNquery = ccl_rpn_query(out, rpn);
893         assert(RPNquery);
894         bib1.proto = protocol;
895         bib1.oclass = CLASS_ATTSET;
896         bib1.value = VAL_BIB1;
897         RPNquery->attributeSetId = oid_ent_to_oid(&bib1, oid);
898         query.u.type_1 = RPNquery;
899         ccl_rpn_delete (rpn);
900         break;
901 #endif
902     default:
903         printf ("Unsupported query type\n");
904         return 0;
905     }
906     send_apdu(apdu);
907     setno = 1;
908     printf("Sent searchRequest.\n");
909     return 2;
910 }
911
912 static int process_searchResponse(Z_SearchResponse *res)
913 {
914     printf ("Received SearchResponse.\n");
915     print_refid (res->referenceId);
916     if (*res->searchStatus)
917         printf("Search was a success.\n");
918     else
919         printf("Search was a bloomin' failure.\n");
920     printf("Number of hits: %d, setno %d\n",
921         *res->resultCount, setnumber);
922     printf("records returned: %d\n",
923         *res->numberOfRecordsReturned);
924     setno += *res->numberOfRecordsReturned;
925     if (res->records)
926         display_records(res->records);
927     return 0;
928 }
929
930 static void print_level(int iLevel)
931 {
932     int i;
933     for (i = 0; i < iLevel * 4; i++)
934         printf(" ");
935 }
936
937 static void print_int(int iLevel, const char *pTag, int *pInt)
938 {
939     if (pInt != NULL)
940     {
941         print_level(iLevel);
942         printf("%s: %d\n", pTag, *pInt);
943     }
944 }
945
946 static void print_string(int iLevel, const char *pTag, const char *pString)
947 {
948     if (pString != NULL)
949     {
950         print_level(iLevel);
951         printf("%s: %s\n", pTag, pString);
952     }
953 }
954
955 static void print_oid(int iLevel, const char *pTag, Odr_oid *pOid)
956 {
957     if (pOid != NULL)
958     {
959         int *pInt = pOid;
960
961         print_level(iLevel);
962         printf("%s:", pTag);
963         for (; *pInt != -1; pInt++)
964             printf(" %d", *pInt);
965         printf("\n");
966     }
967 }
968
969 static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
970 {
971     if (referenceId != NULL)
972     {
973         int i;
974
975         print_level(iLevel);
976         printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
977         for (i = 0; i < referenceId->len; i++)
978             printf("%c", referenceId->buf[i]);
979         printf("\n");
980     }
981 }
982
983 static void print_string_or_numeric(int iLevel, const char *pTag, Z_StringOrNumeric *pStringNumeric)
984 {
985     if (pStringNumeric != NULL)
986     {
987         switch (pStringNumeric->which)
988         {
989             case Z_StringOrNumeric_string:
990                 print_string(iLevel, pTag, pStringNumeric->u.string);
991                 break;
992
993             case Z_StringOrNumeric_numeric:
994                 print_int(iLevel, pTag, pStringNumeric->u.numeric);
995                 break;
996
997             default:
998                 print_level(iLevel);
999                 printf("%s: valid type for Z_StringOrNumeric\n", pTag);
1000                 break;
1001         }
1002     }
1003 }
1004
1005 static void print_universe_report_duplicate(int iLevel, Z_UniverseReportDuplicate *pUniverseReportDuplicate)
1006 {
1007     if (pUniverseReportDuplicate != NULL)
1008     {
1009         print_level(iLevel);
1010         printf("Universe Report Duplicate: \n");
1011         iLevel++;
1012         print_string_or_numeric(iLevel, "Hit No", pUniverseReportDuplicate->hitno);
1013     }
1014 }
1015
1016 static void print_universe_report_hits(int iLevel, Z_UniverseReportHits *pUniverseReportHits)
1017 {
1018     if (pUniverseReportHits != NULL)
1019     {
1020         print_level(iLevel);
1021         printf("Universe Report Hits: \n");
1022         iLevel++;
1023         print_string_or_numeric(iLevel, "Database", pUniverseReportHits->database);
1024         print_string_or_numeric(iLevel, "Hits", pUniverseReportHits->hits);
1025     }
1026 }
1027
1028 static void print_universe_report(int iLevel, Z_UniverseReport *pUniverseReport)
1029 {
1030     if (pUniverseReport != NULL)
1031     {
1032         print_level(iLevel);
1033         printf("Universe Report: \n");
1034         iLevel++;
1035         print_int(iLevel, "Total Hits", pUniverseReport->totalHits);
1036         switch (pUniverseReport->which)
1037         {
1038             case Z_UniverseReport_databaseHits:
1039                 print_universe_report_hits(iLevel, pUniverseReport->u.databaseHits);
1040                 break;
1041
1042             case Z_UniverseReport_duplicate:
1043                 print_universe_report_duplicate(iLevel, pUniverseReport->u.duplicate);
1044                 break;
1045
1046             default:
1047                 print_level(iLevel);
1048                 printf("Type: %d\n", pUniverseReport->which);
1049                 break;
1050         }
1051     }
1052 }
1053
1054 static void print_external(int iLevel, Z_External *pExternal)
1055 {
1056     if (pExternal != NULL)
1057     {
1058         print_level(iLevel);
1059         printf("External: \n");
1060         iLevel++;
1061         print_oid(iLevel, "Direct Reference", pExternal->direct_reference);
1062         print_int(iLevel, "InDirect Reference", pExternal->indirect_reference);
1063         print_string(iLevel, "Descriptor", pExternal->descriptor);
1064         switch (pExternal->which)
1065         {
1066             case Z_External_universeReport:
1067                 print_universe_report(iLevel, pExternal->u.universeReport);
1068                 break;
1069
1070             default:
1071                 print_level(iLevel);
1072                 printf("Type: %d\n", pExternal->which);
1073                 break;
1074         }
1075     }
1076 }
1077
1078 static int process_resourceControlRequest (Z_ResourceControlRequest *req)
1079 {
1080     printf ("Received ResourceControlRequest.\n");
1081     print_referenceId(1, req->referenceId);
1082     print_int(1, "Suspended Flag", req->suspendedFlag);
1083     print_int(1, "Partial Results Available", req->partialResultsAvailable);
1084     print_int(1, "Response Required", req->responseRequired);
1085     print_int(1, "Triggered Request Flag", req->triggeredRequestFlag);
1086     print_external(1, req->resourceReport);
1087     return 0;
1088 }
1089
1090 void process_ESResponse(Z_ExtendedServicesResponse *res)
1091 {
1092     printf("process_ESResponse\n");
1093 }
1094
1095 static Z_External *CreateItemOrderExternal(int itemno)
1096 {
1097     Z_External *r = (Z_External *) odr_malloc(out, sizeof(Z_External));
1098     oident ItemOrderRequest;
1099   
1100     ItemOrderRequest.proto = PROTO_Z3950;
1101     ItemOrderRequest.oclass = CLASS_EXTSERV;
1102     ItemOrderRequest.value = VAL_ITEMORDER;
1103  
1104     r->direct_reference = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest)); 
1105     r->indirect_reference = (int *) odr_malloc(out,sizeof(int));
1106     *r->indirect_reference = 0;
1107
1108     r->descriptor = "Extended services item order";
1109
1110     r->which = Z_External_itemOrder;
1111
1112     r->u.itemOrder = (Z_ItemOrder *) odr_malloc(out,sizeof(Z_ItemOrder));
1113     memset(r->u.itemOrder, 0, sizeof(Z_ItemOrder));
1114 #ifdef ASN_COMPILED
1115     r->u.itemOrder->which=Z_IOItemOrder_esRequest;
1116 #else
1117     r->u.itemOrder->which=Z_ItemOrder_esRequest;
1118 #endif
1119
1120     r->u.itemOrder->u.esRequest = (Z_IORequest *) 
1121         odr_malloc(out,sizeof(Z_IORequest));
1122     memset(r->u.itemOrder->u.esRequest, 0, sizeof(Z_IORequest));
1123
1124     r->u.itemOrder->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
1125         odr_malloc(out,sizeof(Z_IOOriginPartToKeep));
1126     memset(r->u.itemOrder->u.esRequest->toKeep, 0, sizeof(Z_IOOriginPartToKeep));
1127     r->u.itemOrder->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
1128         odr_malloc(out,sizeof(Z_IOOriginPartNotToKeep));
1129     memset(r->u.itemOrder->u.esRequest->notToKeep, 0, sizeof(Z_IOOriginPartNotToKeep));
1130
1131     r->u.itemOrder->u.esRequest->toKeep->supplDescription = NULL;
1132     r->u.itemOrder->u.esRequest->toKeep->contact = NULL;
1133     r->u.itemOrder->u.esRequest->toKeep->addlBilling = NULL;
1134
1135     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem =
1136         (Z_IOResultSetItem *) odr_malloc(out, sizeof(Z_IOResultSetItem));
1137     memset(r->u.itemOrder->u.esRequest->notToKeep->resultSetItem, 0, sizeof(Z_IOResultSetItem));
1138     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->resultSetId = "1";
1139
1140     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item =
1141         (int *) odr_malloc(out, sizeof(int));
1142     *r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item = itemno;
1143
1144     r->u.itemOrder->u.esRequest->notToKeep->itemRequest = NULL;
1145     return r;
1146 }
1147
1148 /* II : Added to do DALI Item Order Extended services request */
1149 static int send_itemorder(char *arg)
1150 {
1151     int itemno = -1;
1152     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest );
1153     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1154     oident ItemOrderRequest;
1155
1156     if (*arg)
1157         itemno = atoi(arg);
1158
1159     /* Set up item order request */
1160
1161     /* Function being performed by this extended services request */
1162     req->function = (int *) odr_malloc(out, sizeof(int));
1163     *req->function = Z_ExtendedServicesRequest_create;
1164
1165     /* Package type, Using protocol ILL ( But that's not in the oid.h file yet */
1166     /* create an object of class Extended Service, value Item Order            */
1167     ItemOrderRequest.proto = PROTO_Z3950;
1168     ItemOrderRequest.oclass = CLASS_EXTSERV;
1169     ItemOrderRequest.value = VAL_ITEMORDER;
1170     req->packageType = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest));
1171     req->packageName = "1.Extendedserveq";
1172
1173     /* ** taskSpecificParameters ** */
1174     req->taskSpecificParameters = CreateItemOrderExternal(itemno);
1175
1176     /* waitAction - Create the ILL request and that's it */
1177     *req->waitAction = Z_ExtendedServicesRequest_wait;
1178
1179     send_apdu(apdu);
1180     return 0;
1181 }
1182
1183 /* II : Added to do DALI Item Order Extended services request */
1184 static int cmd_itemorder(char *arg)
1185 {
1186     printf("Item order request\n");
1187     fflush(stdout);
1188
1189     send_itemorder(arg);
1190     return(2);
1191 }
1192
1193 static int cmd_find(char *arg)
1194 {
1195     if (!*arg)
1196     {
1197         printf("Find what?\n");
1198         return 0;
1199     }
1200     if (!conn)
1201     {
1202         printf("Not connected yet\n");
1203         return 0;
1204     }
1205     if (!send_searchRequest(arg))
1206         return 0;
1207     return 2;
1208 }
1209
1210 static int cmd_delete(char *arg)
1211 {
1212     if (!*arg)
1213     {
1214         printf("Delete what?\n");
1215         return 0;
1216     }
1217     if (!conn)
1218     {
1219         printf("Not connected yet\n");
1220         return 0;
1221     }
1222     if (!send_deleteResultSetRequest(arg))
1223         return 0;
1224     return 2;
1225 }
1226
1227 static int cmd_ssub(char *arg)
1228 {
1229     if (!(smallSetUpperBound = atoi(arg)))
1230         return 0;
1231     return 1;
1232 }
1233
1234 static int cmd_lslb(char *arg)
1235 {
1236     if (!(largeSetLowerBound = atoi(arg)))
1237         return 0;
1238     return 1;
1239 }
1240
1241 static int cmd_mspn(char *arg)
1242 {
1243     if (!(mediumSetPresentNumber = atoi(arg)))
1244         return 0;
1245     return 1;
1246 }
1247
1248 static int cmd_status(char *arg)
1249 {
1250     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
1251     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
1252     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
1253     return 1;
1254 }
1255
1256 static int cmd_setnames(char *arg)
1257 {
1258     if (setnumber < 0)
1259     {
1260         printf("Set numbering enabled.\n");
1261         setnumber = 0;
1262     }
1263     else
1264     {
1265         printf("Set numbering disabled.\n");
1266         setnumber = -1;
1267     }
1268     return 1;
1269 }
1270
1271 /* PRESENT SERVICE ----------------------------- */
1272
1273 static int send_presentRequest(char *arg)
1274 {
1275     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
1276     Z_PresentRequest *req = apdu->u.presentRequest;
1277     Z_RecordComposition compo;
1278     oident prefsyn;
1279     int nos = 1;
1280     int oid[OID_SIZE];
1281     char *p;
1282     char setstring[100];
1283
1284     req->referenceId = set_refid (out);
1285     if ((p = strchr(arg, '+')))
1286     {
1287         nos = atoi(p + 1);
1288         *p = 0;
1289     }
1290     if (*arg)
1291         setno = atoi(arg);
1292     if (p && (p=strchr(p+1, '+')))
1293     {
1294         strcpy (setstring, p+1);
1295         req->resultSetId = setstring;
1296     }
1297     else if (setnumber >= 0)
1298     {
1299         sprintf(setstring, "%d", setnumber);
1300         req->resultSetId = setstring;
1301     }
1302 #if 0
1303     if (1)
1304     {
1305         static Z_Range range;
1306         static Z_Range *rangep = &range;
1307     req->num_ranges = 1;
1308 #endif
1309     req->resultSetStartPoint = &setno;
1310     req->numberOfRecordsRequested = &nos;
1311     prefsyn.proto = protocol;
1312     prefsyn.oclass = CLASS_RECSYN;
1313     prefsyn.value = recordsyntax;
1314     req->preferredRecordSyntax = oid_ent_to_oid(&prefsyn, oid);
1315
1316     if (elementSetNames)
1317     {
1318         req->recordComposition = &compo;
1319         compo.which = Z_RecordComp_simple;
1320         compo.u.simple = elementSetNames;
1321     }
1322     send_apdu(apdu);
1323     printf("Sent presentRequest (%d+%d).\n", setno, nos);
1324     return 2;
1325 }
1326
1327 void process_close(Z_Close *req)
1328 {
1329     Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
1330     Z_Close *res = apdu->u.close;
1331
1332     static char *reasons[] =
1333     {
1334         "finished",
1335         "shutdown",
1336         "system problem",
1337         "cost limit reached",
1338         "resources",
1339         "security violation",
1340         "protocolError",
1341         "lack of activity",
1342         "peer abort",
1343         "unspecified"
1344     };
1345
1346     printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
1347         req->diagnosticInformation ? req->diagnosticInformation : "NULL");
1348     if (sent_close)
1349     {
1350         cs_close (conn);
1351         conn = NULL;
1352         if (session_mem)
1353         {
1354             nmem_destroy (session_mem);
1355             session_mem = NULL;
1356         }
1357         sent_close = 0;
1358     }
1359     else
1360     {
1361         *res->closeReason = Z_Close_finished;
1362         send_apdu(apdu);
1363         printf("Sent response.\n");
1364         sent_close = 1;
1365     }
1366 }
1367
1368 static int cmd_show(char *arg)
1369 {
1370     if (!conn)
1371     {
1372         printf("Not connected yet\n");
1373         return 0;
1374     }
1375     if (!send_presentRequest(arg))
1376         return 0;
1377     return 2;
1378 }
1379
1380 int cmd_quit(char *arg)
1381 {
1382     printf("See you later, alligator.\n");
1383     exit(0);
1384     return 0;
1385 }
1386
1387 int cmd_cancel(char *arg)
1388 {
1389     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
1390     Z_TriggerResourceControlRequest *req =
1391         apdu->u.triggerResourceControlRequest;
1392     bool_t rfalse = 0;
1393     
1394     if (!conn)
1395     {
1396         printf("Session not initialized yet\n");
1397         return 0;
1398     }
1399     if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
1400     {
1401         printf("Target doesn't support cancel (trigger resource ctrl)\n");
1402         return 0;
1403     }
1404     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
1405     req->resultSetWanted = &rfalse;
1406
1407     send_apdu(apdu);
1408     printf("Sent cancel request\n");
1409     return 2;
1410 }
1411
1412 int send_scanrequest(char *string, int pp, int num)
1413 {
1414     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
1415     Z_ScanRequest *req = apdu->u.scanRequest;
1416     
1417     if (!(req->termListAndStartPoint =
1418           p_query_scan(out, protocol, &req->attributeSet, string)))
1419     {
1420         printf("Prefix query error\n");
1421         return -1;
1422     }
1423     req->referenceId = set_refid (out);
1424     req->num_databaseNames = num_databaseNames;
1425     req->databaseNames = databaseNames;
1426     req->numberOfTermsRequested = &num;
1427     req->preferredPositionInResponse = &pp;
1428     send_apdu(apdu);
1429     return 2;
1430 }
1431
1432 int send_sortrequest(char *arg, int newset)
1433 {
1434     Z_APDU *apdu = zget_APDU(out, Z_APDU_sortRequest);
1435     Z_SortRequest *req = apdu->u.sortRequest;
1436     Z_SortKeySpecList *sksl = (Z_SortKeySpecList *)odr_malloc (out, sizeof(*sksl));
1437     char setstring[32];
1438     char sort_string[32], sort_flags[32];
1439     int off;
1440     int oid[OID_SIZE];
1441     oident bib1;
1442
1443     if (setnumber >= 0)
1444         sprintf (setstring, "%d", setnumber);
1445     else
1446         sprintf (setstring, "default");
1447
1448     req->referenceId = set_refid (out);
1449
1450 #ifdef ASN_COMPILED
1451     req->num_inputResultSetNames = 1;
1452     req->inputResultSetNames = (Z_InternationalString **)
1453         odr_malloc (out, sizeof(*req->inputResultSetNames));
1454     req->inputResultSetNames[0] = (char *)
1455         odr_malloc (out, strlen(setstring)+1);
1456     strcpy (req->inputResultSetNames[0], setstring);
1457 #else
1458     req->inputResultSetNames =
1459         (Z_StringList *)odr_malloc (out, sizeof(*req->inputResultSetNames));
1460     req->inputResultSetNames->num_strings = 1;
1461     req->inputResultSetNames->strings =
1462         (char **)odr_malloc (out, sizeof(*req->inputResultSetNames->strings));
1463     req->inputResultSetNames->strings[0] =
1464         (char *)odr_malloc (out, strlen(setstring)+1);
1465     strcpy (req->inputResultSetNames->strings[0], setstring);
1466 #endif
1467
1468     if (newset && setnumber >= 0)
1469         sprintf (setstring, "%d", ++setnumber);
1470
1471     req->sortedResultSetName = (char *)odr_malloc (out, strlen(setstring)+1);
1472     strcpy (req->sortedResultSetName, setstring);
1473
1474     req->sortSequence = sksl;
1475     sksl->num_specs = 0;
1476     sksl->specs = (Z_SortKeySpec **)odr_malloc (out, sizeof(sksl->specs) * 20);
1477     
1478     bib1.proto = protocol;
1479     bib1.oclass = CLASS_ATTSET;
1480     bib1.value = VAL_BIB1;
1481     while ((sscanf (arg, "%31s %31s%n", sort_string, sort_flags, &off)) == 2 
1482            && off > 1)
1483     {
1484         int i;
1485         char *sort_string_sep;
1486         Z_SortKeySpec *sks = (Z_SortKeySpec *)odr_malloc (out, sizeof(*sks));
1487         Z_SortKey *sk = (Z_SortKey *)odr_malloc (out, sizeof(*sk));
1488
1489         arg += off;
1490         sksl->specs[sksl->num_specs++] = sks;
1491         sks->sortElement = (Z_SortElement *)odr_malloc (out, sizeof(*sks->sortElement));
1492         sks->sortElement->which = Z_SortElement_generic;
1493         sks->sortElement->u.generic = sk;
1494         
1495         if ((sort_string_sep = strchr (sort_string, '=')))
1496         {
1497             Z_AttributeElement *el = (Z_AttributeElement *)odr_malloc (out, sizeof(*el));
1498             sk->which = Z_SortKey_sortAttributes;
1499             sk->u.sortAttributes =
1500                 (Z_SortAttributes *)odr_malloc (out, sizeof(*sk->u.sortAttributes));
1501             sk->u.sortAttributes->id = oid_ent_to_oid(&bib1, oid);
1502             sk->u.sortAttributes->list =
1503                 (Z_AttributeList *)odr_malloc (out, sizeof(*sk->u.sortAttributes->list));
1504             sk->u.sortAttributes->list->num_attributes = 1;
1505             sk->u.sortAttributes->list->attributes =
1506                 (Z_AttributeElement **)odr_malloc (out,
1507                             sizeof(*sk->u.sortAttributes->list->attributes));
1508             sk->u.sortAttributes->list->attributes[0] = el;
1509             el->attributeSet = 0;
1510             el->attributeType = (int *)odr_malloc (out, sizeof(*el->attributeType));
1511             *el->attributeType = atoi (sort_string);
1512             el->which = Z_AttributeValue_numeric;
1513             el->value.numeric = (int *)odr_malloc (out, sizeof(*el->value.numeric));
1514             *el->value.numeric = atoi (sort_string_sep + 1);
1515         }
1516         else
1517         {
1518             sk->which = Z_SortKey_sortField;
1519             sk->u.sortField = (char *)odr_malloc (out, strlen(sort_string)+1);
1520             strcpy (sk->u.sortField, sort_string);
1521         }
1522         sks->sortRelation = (int *)odr_malloc (out, sizeof(*sks->sortRelation));
1523         *sks->sortRelation = Z_SortRelation_ascending;
1524         sks->caseSensitivity = (int *)odr_malloc (out, sizeof(*sks->caseSensitivity));
1525         *sks->caseSensitivity = Z_SortCase_caseSensitive;
1526
1527 #ifdef ASN_COMPILED
1528         sks->which = Z_SortKeySpec_null;
1529         sks->u.null = odr_nullval ();
1530 #else
1531         sks->missingValueAction = NULL;
1532 #endif
1533
1534         for (i = 0; sort_flags[i]; i++)
1535         {
1536             switch (sort_flags[i])
1537             {
1538             case 'a':
1539             case 'A':
1540             case '>':
1541                 *sks->sortRelation = Z_SortRelation_descending;
1542                 break;
1543             case 'd':
1544             case 'D':
1545             case '<':
1546                 *sks->sortRelation = Z_SortRelation_ascending;
1547                 break;
1548             case 'i':
1549             case 'I':
1550                 *sks->caseSensitivity = Z_SortCase_caseInsensitive;
1551                 break;
1552             case 'S':
1553             case 's':
1554                 *sks->caseSensitivity = Z_SortCase_caseSensitive;
1555                 break;
1556             }
1557         }
1558     }
1559     if (!sksl->num_specs)
1560     {
1561         printf ("Missing sort specifications\n");
1562         return -1;
1563     }
1564     send_apdu(apdu);
1565     return 2;
1566 }
1567
1568 void display_term(Z_TermInfo *t)
1569 {
1570     if (t->term->which == Z_Term_general)
1571     {
1572         printf("%.*s (%d)\n", t->term->u.general->len, t->term->u.general->buf,
1573             t->globalOccurrences ? *t->globalOccurrences : -1);
1574         sprintf(last_scan, "%.*s", t->term->u.general->len,
1575             t->term->u.general->buf);
1576     }
1577     else
1578         printf("Term type not general.\n");
1579 }
1580
1581 void process_scanResponse(Z_ScanResponse *res)
1582 {
1583     int i;
1584     Z_Entry **entries = NULL;
1585     int num_entries = 0;
1586    
1587     printf("Received ScanResponse\n"); 
1588     print_refid (res->referenceId);
1589     printf("%d entries", *res->numberOfEntriesReturned);
1590     if (res->positionOfTerm)
1591         printf (", position=%d", *res->positionOfTerm); 
1592     printf ("\n");
1593     if (*res->scanStatus != Z_Scan_success)
1594         printf("Scan returned code %d\n", *res->scanStatus);
1595     if (!res->entries)
1596         return;
1597     if ((entries = res->entries->entries))
1598         num_entries = res->entries->num_entries;
1599     for (i = 0; i < num_entries; i++)
1600     {
1601         int pos_term = res->positionOfTerm ? *res->positionOfTerm : -1;
1602         if (entries[i]->which == Z_Entry_termInfo)
1603         {
1604             printf("%c ", i + 1 == pos_term ? '*' : ' ');
1605             display_term(entries[i]->u.termInfo);
1606         }
1607         else
1608             display_diagrecs(&entries[i]->u.surrogateDiagnostic, 1);
1609     }
1610     if (res->entries->nonsurrogateDiagnostics)
1611         display_diagrecs (res->entries->nonsurrogateDiagnostics,
1612                           res->entries->num_nonsurrogateDiagnostics);
1613 }
1614
1615 void process_sortResponse(Z_SortResponse *res)
1616 {
1617     printf("Received SortResponse: status=");
1618     switch (*res->sortStatus)
1619     {
1620     case Z_SortStatus_success:
1621         printf ("success"); break;
1622     case Z_SortStatus_partial_1:
1623         printf ("partial"); break;
1624     case Z_SortStatus_failure:
1625         printf ("failure"); break;
1626     default:
1627         printf ("unknown (%d)", *res->sortStatus);
1628     }
1629     printf ("\n");
1630     print_refid (res->referenceId);
1631 #ifdef ASN_COMPILED
1632     if (res->diagnostics)
1633         display_diagrecs(res->diagnostics,
1634                          res->num_diagnostics);
1635 #else
1636     if (res->diagnostics)
1637         display_diagrecs(res->diagnostics->diagRecs,
1638                          res->diagnostics->num_diagRecs);
1639 #endif
1640 }
1641
1642 int cmd_sort_generic(char *arg, int newset)
1643 {
1644     if (!conn)
1645     {
1646         printf("Session not initialized yet\n");
1647         return 0;
1648     }
1649     if (!ODR_MASK_GET(session->options, Z_Options_sort))
1650     {
1651         printf("Target doesn't support sort\n");
1652         return 0;
1653     }
1654     if (*arg)
1655     {
1656         if (send_sortrequest(arg, newset) < 0)
1657             return 0;
1658         return 2;
1659     }
1660     return 0;
1661 }
1662
1663 int cmd_sort(char *arg)
1664 {
1665     return cmd_sort_generic (arg, 0);
1666 }
1667
1668 int cmd_sort_newset (char *arg)
1669 {
1670     return cmd_sort_generic (arg, 1);
1671 }
1672
1673 int cmd_scan(char *arg)
1674 {
1675     if (!conn)
1676     {
1677         printf("Session not initialized yet\n");
1678         return 0;
1679     }
1680     if (!ODR_MASK_GET(session->options, Z_Options_scan))
1681     {
1682         printf("Target doesn't support scan\n");
1683         return 0;
1684     }
1685     if (*arg)
1686     {
1687         if (send_scanrequest(arg, 1, 20) < 0)
1688             return 0;
1689     }
1690     else
1691         if (send_scanrequest(last_scan, 1, 20) < 0)
1692             return 0;
1693     return 2;
1694 }
1695
1696 int cmd_format(char *arg)
1697 {
1698     if (!arg || !*arg)
1699     {
1700         printf("Usage: format <recordsyntax>\n");
1701         return 0;
1702     }
1703     recordsyntax = oid_getvalbyname (arg);
1704     if (recordsyntax == VAL_NONE)
1705     {
1706         printf ("unknown record syntax\n");
1707         return 0;
1708     }
1709     return 1;
1710 }
1711
1712 int cmd_elements(char *arg)
1713 {
1714     static Z_ElementSetNames esn;
1715     static char what[100];
1716
1717     if (!arg || !*arg)
1718     {
1719         printf("Usage: elements <esn>\n");
1720         return 0;
1721     }
1722     strcpy(what, arg);
1723     esn.which = Z_ElementSetNames_generic;
1724     esn.u.generic = what;
1725     elementSetNames = &esn;
1726     return 1;
1727 }
1728
1729 int cmd_attributeset(char *arg)
1730 {
1731     char what[100];
1732
1733     if (!arg || !*arg)
1734     {
1735         printf("Usage: attributeset <setname>\n");
1736         return 0;
1737     }
1738     sscanf(arg, "%s", what);
1739     if (p_query_attset (what))
1740     {
1741         printf("Unknown attribute set name\n");
1742         return 0;
1743     }
1744     return 1;
1745 }
1746
1747 int cmd_querytype (char *arg)
1748 {
1749     if (!strcmp (arg, "ccl"))
1750         queryType = QueryType_CCL;
1751     else if (!strcmp (arg, "prefix"))
1752         queryType = QueryType_Prefix;
1753 #if CCL2RPN
1754     else if (!strcmp (arg, "ccl2rpn") || !strcmp (arg, "cclrpn"))
1755         queryType = QueryType_CCL2RPN;
1756 #endif
1757     else
1758     {
1759         printf ("Querytype must be one of:\n");
1760         printf (" prefix         - Prefix query\n");
1761         printf (" ccl            - CCL query\n");
1762 #if CCL2RPN
1763         printf (" ccl2rpn        - CCL query converted to RPN\n");
1764 #endif
1765         return 0;
1766     }
1767     return 1;
1768 }
1769
1770 int cmd_refid (char *arg)
1771 {
1772     xfree (refid);
1773     refid = NULL;
1774     if (*arg)
1775     {
1776         refid = (char *) xmalloc (strlen(arg)+1);
1777         strcpy (refid, arg);
1778     }
1779     return 1;
1780 }
1781
1782 int cmd_close(char *arg)
1783 {
1784     Z_APDU *apdu;
1785     Z_Close *req;
1786     if (!conn)
1787         return 0;
1788
1789     apdu = zget_APDU(out, Z_APDU_close);
1790     req = apdu->u.close;
1791     *req->closeReason = Z_Close_finished;
1792     send_apdu(apdu);
1793     printf("Sent close request.\n");
1794     sent_close = 1;
1795     return 2;
1796 }
1797
1798 static void initialize(void)
1799 {
1800 #if CCL2RPN
1801     FILE *inf;
1802 #endif
1803     nmem_init();
1804     if (!(out = odr_createmem(ODR_ENCODE)) ||
1805         !(in = odr_createmem(ODR_DECODE)) ||
1806         !(print = odr_createmem(ODR_PRINT)))
1807     {
1808         fprintf(stderr, "failed to allocate ODR streams\n");
1809         exit(1);
1810     }
1811     setvbuf(stdout, 0, _IONBF, 0);
1812
1813 #if CCL2RPN
1814     bibset = ccl_qual_mk (); 
1815     inf = fopen ("default.bib", "r");
1816     if (inf)
1817     {
1818         ccl_qual_file (bibset, inf);
1819         fclose (inf);
1820     }
1821 #endif
1822     cmd_base("Default");
1823 }
1824
1825 static int client(int wait)
1826 {
1827     static struct {
1828         char *cmd;
1829         int (*fun)(char *arg);
1830         char *ad;
1831     } cmd[] = {
1832         {"open", cmd_open, "('tcp'|'osi')':'[<tsel>'/']<host>[':'<port>]"},
1833         {"quit", cmd_quit, ""},
1834         {"find", cmd_find, "<query>"},
1835         {"delete", cmd_delete, "<setname>"},
1836         {"base", cmd_base, "<base-name>"},
1837         {"show", cmd_show, "<rec#>['+'<#recs>['+'<setname>]]"},
1838         {"scan", cmd_scan, "<term>"},
1839         {"sort", cmd_sort, "<sortkey> <flag> <sortkey> <flag> ..."},
1840         {"sort+", cmd_sort_newset, "<sortkey> <flag> <sortkey> <flag> ..."},
1841         {"authentication", cmd_authentication, "<acctstring>"},
1842         {"lslb", cmd_lslb, "<largeSetLowerBound>"},
1843         {"ssub", cmd_ssub, "<smallSetUpperBound>"},
1844         {"mspn", cmd_mspn, "<mediumSetPresentNumber>"},
1845         {"status", cmd_status, ""},
1846         {"setnames", cmd_setnames, ""},
1847         {"cancel", cmd_cancel, ""},
1848         {"format", cmd_format, "<recordsyntax>"},
1849         {"elements", cmd_elements, "<elementSetName>"},
1850         {"close", cmd_close, ""},
1851         {"attributeset", cmd_attributeset, "<attrset>"},
1852         {"querytype", cmd_querytype, "<type>"},
1853         {"refid", cmd_refid, "<id>"},
1854         {"itemorder", cmd_itemorder, "<item>"},
1855         {0,0}
1856     };
1857     char *netbuffer= 0;
1858     int netbufferlen = 0;
1859     int i;
1860     Z_APDU *apdu;
1861
1862     while (1)
1863     {
1864         int res;
1865 #ifdef USE_SELECT
1866         fd_set input;
1867 #endif
1868         char line[1024], word[1024], arg[1024];
1869         
1870 #ifdef USE_SELECT
1871         FD_ZERO(&input);
1872         FD_SET(0, &input);
1873         if (conn)
1874             FD_SET(cs_fileno(conn), &input);
1875         if ((res = select(20, &input, 0, 0, 0)) < 0)
1876         {
1877             perror("select");
1878             exit(1);
1879         }
1880         if (!res)
1881             continue;
1882         if (!wait && FD_ISSET(0, &input))
1883 #else
1884         if (!wait)
1885 #endif
1886             {
1887             /* quick & dirty way to get a command line. */
1888                 char *end_p;
1889                 if (!fgets(line, 1023, stdin))
1890                     break;
1891                 if ((end_p = strchr (line, '\n')))
1892                     *end_p = '\0';
1893             if ((res = sscanf(line, "%s %[^;]", word, arg)) <= 0)
1894             {
1895                 strcpy(word, last_cmd);
1896                 *arg = '\0';
1897             }
1898             else if (res == 1)
1899                 *arg = 0;
1900             strcpy(last_cmd, word);
1901             for (i = 0; cmd[i].cmd; i++)
1902                 if (!strncmp(cmd[i].cmd, word, strlen(word)))
1903                 {
1904                     res = (*cmd[i].fun)(arg);
1905                     break;
1906                 }
1907             if (!cmd[i].cmd) /* dump our help-screen */
1908             {
1909                 printf("Unknown command: %s.\n", word);
1910                 printf("Currently recognized commands:\n");
1911                 for (i = 0; cmd[i].cmd; i++)
1912                     printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
1913                 res = 1;
1914             }
1915             if (res < 2)
1916             {
1917                 printf(C_PROMPT);
1918                 continue;
1919             }
1920         }
1921         wait = 0;
1922 #ifdef USE_SELECT
1923         if (conn && FD_ISSET(cs_fileno(conn), &input))
1924 #endif
1925         {
1926             do
1927             {
1928                 if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
1929                 {
1930                     perror("cs_get");
1931                     exit(1);
1932                 }
1933                 if (!res)
1934                 {
1935                     printf("Target closed connection.\n");
1936                     exit(1);
1937                 }
1938                 odr_reset(in); /* release APDU from last round */
1939                 odr_setbuf(in, netbuffer, res, 0);
1940                 if (!z_APDU(in, &apdu, 0))
1941                 {
1942                     odr_perror(in, "Decoding incoming APDU");
1943                     fprintf(stderr, "[Near %d]\n", odr_offset(in));
1944                     fprintf(stderr, "Packet dump:\n---------\n");
1945                     odr_dumpBER(stderr, netbuffer, res);
1946                     fprintf(stderr, "---------\n");
1947                     exit(1);
1948                 }
1949 #if 0
1950                 if (!z_APDU(print, &apdu, 0))
1951                 {
1952                     odr_perror(print, "Failed to print incoming APDU");
1953                     odr_reset(print);
1954                     continue;
1955                 }
1956 #endif
1957                 switch(apdu->which)
1958                 {
1959                 case Z_APDU_initResponse:
1960                     process_initResponse(apdu->u.initResponse);
1961                     break;
1962                 case Z_APDU_searchResponse:
1963                     process_searchResponse(apdu->u.searchResponse);
1964                     break;
1965                 case Z_APDU_scanResponse:
1966                     process_scanResponse(apdu->u.scanResponse);
1967                     break;
1968                 case Z_APDU_presentResponse:
1969                     print_refid (apdu->u.presentResponse->referenceId);
1970                     setno +=
1971                         *apdu->u.presentResponse->numberOfRecordsReturned;
1972                     if (apdu->u.presentResponse->records)
1973                         display_records(apdu->u.presentResponse->records);
1974                     else
1975                         printf("No records.\n");
1976                     printf ("nextResultSetPosition = %d\n",
1977                         *apdu->u.presentResponse->nextResultSetPosition);
1978                     break;
1979                 case Z_APDU_sortResponse:
1980                     process_sortResponse(apdu->u.sortResponse);
1981                     break;
1982                 case Z_APDU_extendedServicesResponse:
1983                     printf("Got extended services response\n");
1984                     process_ESResponse(apdu->u.extendedServicesResponse);
1985                     break;
1986                 case Z_APDU_close:
1987                     printf("Target has closed the association.\n");
1988                     process_close(apdu->u.close);
1989                     break;
1990                 case Z_APDU_resourceControlRequest:
1991                     process_resourceControlRequest
1992                         (apdu->u.resourceControlRequest);
1993                     break;
1994                 case Z_APDU_deleteResultSetResponse:
1995                     printf("Got deleteResultSetResponse status=%d\n",
1996                            *apdu->u.deleteResultSetResponse->
1997                            deleteOperationStatus);
1998                     break;
1999                 default:
2000                     printf("Received unknown APDU type (%d).\n", 
2001                            apdu->which);
2002                     exit(1);
2003                 }
2004             }
2005             while (conn && cs_more(conn));
2006             printf(C_PROMPT);
2007             fflush(stdout);
2008         }
2009     }
2010     return 0;
2011 }
2012
2013 int main(int argc, char **argv)
2014 {
2015     char *prog = *argv;
2016     char *arg;
2017     int ret;
2018     int opened = 0;
2019
2020     while ((ret = options("m:v:", argv, argc, &arg)) != -2)
2021     {
2022         switch (ret)
2023         {
2024         case 0:
2025             if (!opened)
2026             {
2027                 initialize ();
2028                 if (cmd_open (arg) == 2)
2029                     opened = 1;
2030             }
2031             break;
2032         case 'm':
2033             if (!(marcdump = fopen (arg, "a")))
2034             {
2035                 perror (arg);
2036                 exit (1);
2037             }
2038             break;
2039         case 'v':
2040             log_init (log_mask_str(arg), "", NULL);
2041             break;
2042         default:
2043             fprintf (stderr, "Usage: %s [-m <marclog>] [<server-addr>]\n",
2044                      prog);
2045             exit (1);
2046         }
2047     }
2048     if (!opened)
2049     {
2050         initialize ();
2051         printf (C_PROMPT);
2052     }
2053     return client (opened);
2054 }
2055
2056