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