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