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