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