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