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