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