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