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