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