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