Moving to v3.
[yaz-moved-to-github.git] / client / client.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: client.c,v $
7  * Revision 1.12  1995-06-15 07:44:57  quinn
8  * Moving to v3.
9  *
10  * Revision 1.11  1995/06/14  15:26:40  quinn
11  * *** empty log message ***
12  *
13  * Revision 1.10  1995/06/06  14:56:58  quinn
14  * Better diagnostics.
15  *
16  * Revision 1.9  1995/06/06  08:15:19  quinn
17  * Cosmetic.
18  *
19  * Revision 1.8  1995/06/05  10:52:22  quinn
20  * Added SCAN.
21  *
22  * Revision 1.7  1995/06/02  09:50:09  quinn
23  * Smallish.
24  *
25  * Revision 1.6  1995/05/31  08:29:21  quinn
26  * Nothing significant.
27  *
28  * Revision 1.5  1995/05/29  08:10:47  quinn
29  * Moved oid.c to util.
30  *
31  * Revision 1.4  1995/05/22  15:30:13  adam
32  * Client uses prefix query notation.
33  *
34  * Revision 1.3  1995/05/22  15:06:53  quinn
35  * *** empty log message ***
36  *
37  * Revision 1.2  1995/05/22  14:56:40  quinn
38  * *** empty log message ***
39  *
40  * Revision 1.1  1995/05/22  11:30:31  quinn
41  * Added prettier client.
42  *
43  *
44  */
45
46 /*
47  * This is the obligatory little toy client, whose primary purpose is
48  * to illustrate the use of the YAZ service-level API.
49  */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <sys/time.h>
54 #include <assert.h>
55 #ifdef _AIX
56 #include <sys/select.h>
57 #endif
58
59 #include <comstack.h>
60 #include <tcpip.h>
61 #ifdef USE_XTIMOSI
62 #include <xmosi.h>
63 #endif
64
65 #include <proto.h>
66
67 #include <marcdisp.h>
68
69 #ifdef RPN_QUERY
70 #ifdef PREFIX_QUERY
71 #include <pquery.h>
72 #else
73 #include <yaz-ccl.h>
74 #endif
75 #endif
76
77 #define C_PROMPT "Z> "
78
79 static ODR out, in, print;              /* encoding and decoding streams */
80 static COMSTACK conn = 0;               /* our z-association */
81 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
82 static char database[512] = "Default";  /* Database name */
83 static int setnumber = 0;               /* current result set number */
84 static int smallSetUpperBound = 0;
85 static int largeSetLowerBound = 1;
86 static int mediumSetPresentNumber = 0;
87 static int setno = 1;                   /* current set offset */
88 static int protocol = PROTO_Z3950;      /* current app protocol */
89 static ODR_MEM session_mem;                /* memory handle for init-response */
90 static Z_InitResponse *session = 0;        /* session parameters */
91 static char last_scan[512] = "0";
92 static char last_cmd[100] = "?";
93 #ifdef RPN_QUERY
94 #ifndef PREFIX_QUERY
95 static CCL_bibset bibset;               /* CCL bibset handle */
96 #endif
97 #endif
98
99 static void send_apdu(Z_APDU *a)
100 {
101     char *buf;
102     int len;
103
104     if (!z_APDU(out, &a, 0))
105     {
106         odr_perror(out, "Encoding APDU");
107         exit(1);
108     }
109     buf = odr_getbuf(out, &len, 0);
110     odr_reset(out); /* release the APDU */
111     if (cs_put(conn, buf, len) < 0)
112     {
113         fprintf(stderr, "cs_put: %s", cs_errlist[cs_errno(conn)]);
114         exit(1);
115     }
116 }
117
118 /* INIT SERVICE ------------------------------- */
119
120 static void send_initRequest()
121 {
122     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
123     Z_InitRequest *req = apdu->u.initRequest;
124
125     ODR_MASK_SET(req->options, Z_Options_search);
126     ODR_MASK_SET(req->options, Z_Options_present);
127     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
128     ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl);
129     ODR_MASK_SET(req->options, Z_Options_scan);
130
131     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
132     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
133
134     req->idAuthentication = auth;
135
136     send_apdu(apdu);
137     printf("Sent initrequest.\n");
138 }
139
140 static int process_initResponse(Z_InitResponse *res)
141 {
142     /* save session parameters for later use */
143     session_mem = odr_extract_mem(in);
144     session = res;
145
146     if (!*res->result)
147         printf("Connection rejected by target.\n");
148     else
149         printf("Connection accepted by target.\n");
150     if (res->implementationId)
151         printf("ID     : %s\n", res->implementationId);
152     if (res->implementationName)
153         printf("Name   : %s\n", res->implementationName);
154     if (res->implementationVersion)
155         printf("Version: %s\n", res->implementationVersion);
156     if (res->userInformationField)
157     {
158         printf("UserInformationfield:\n");
159         if (!odr_external(print, (Odr_external**)&res-> userInformationField,
160             0))
161         {
162             odr_perror(print, "Printing userinfo\n");
163             odr_reset(print);
164         }
165         if (res->userInformationField->which == ODR_EXTERNAL_octet)
166         {
167             printf("Guessing visiblestring:\n");
168             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
169         }
170     }
171     return 0;
172 }
173
174 int cmd_open(char *arg)
175 {
176     void *add;
177     char type[100], addr[100];
178     CS_TYPE t;
179
180     if (conn)
181     {
182         printf("Already connected.\n");
183         return 0;
184     }
185     if (!*arg || sscanf(arg, "%[^:]:%s", type, addr) < 2)
186     {
187         fprintf(stderr, "Usage: open (osi|tcp) ':' [tsel '/']host[':'port]\n");
188         return 0;
189     }
190 #ifdef USE_XTIMOSI
191     if (!strcmp(type, "osi"))
192     {
193         if (!(add = mosi_strtoaddr(addr)))
194         {
195             perror(arg);
196             return 0;
197         }
198         t = mosi_type;
199         protocol = PROTO_SR;
200     }
201     else
202 #endif
203     if (!strcmp(type, "tcp"))
204     {
205         if (!(add = tcpip_strtoaddr(addr)))
206         {
207             perror(arg);
208             return 0;
209         }
210         t = tcpip_type;
211         protocol = PROTO_Z3950;
212     }
213     else
214     {
215         fprintf(stderr, "Bad type: %s\n", type);
216         return 0;
217     }
218     if (!(conn = cs_create(t, 1, protocol)))
219     {
220         perror("cs_create");
221         return 0;
222     }
223     printf("Connecting...");
224     fflush(stdout);
225     if (cs_connect(conn, add) < 0)
226     {
227         perror("connect");
228         cs_close(conn);
229         conn = 0;
230         return 0;
231     }
232     printf("Ok.\n");
233     send_initRequest();
234     return 2;
235 }
236
237 int cmd_authentication(char *arg)
238 {
239     static Z_IdAuthentication au;
240     static char open[256];
241
242     if (!*arg)
243     {
244         printf("Auth field set to null\n");
245         auth = 0;
246         return 1;
247     }
248     auth = &au;
249     au.which = Z_IdAuthentication_open;
250     au.u.open = open;
251     strcpy(open, arg);
252     return 1;
253 }
254
255 /* SEARCH SERVICE ------------------------------ */
256
257 void display_record(Z_DatabaseRecord *p)
258 {
259     Odr_external *r = (Odr_external*) p;
260
261     if (r->direct_reference)
262     {
263         oident *ent = oid_getentbyoid(r->direct_reference);
264
265         printf("Record type: ");
266         if (ent)
267             printf("%s\n", ent->desc);
268         else if (!odr_oid(print, &r->direct_reference, 0))
269         {
270             odr_perror(print, "print oid");
271             odr_reset(print);
272         }
273     }
274 #if 1
275     if (r->which == ODR_EXTERNAL_octet && p->u.octet_aligned->len)
276     {
277 #if 1
278         marc_display ((char*)p->u.octet_aligned->buf, stdout);
279 #else
280         FILE *ofi = fopen("dump", "a");
281         assert(ofi);
282         fwrite(p->u.octet_aligned->buf, 1, p->u.octet_aligned->len, ofi);
283         fclose(ofi);
284         printf("dumped record\n");
285 #endif
286     }
287     else
288     {
289         printf("Unknown record representation.\n");
290         if (!odr_external(print, &r, 0))
291         {
292             odr_perror(print, "Printing external");
293             odr_reset(print);
294         }
295     }
296 #endif
297 }
298
299 static void display_diagrec(Z_DiagRec *p)
300 {
301     oident *ent;
302 #ifdef Z_95
303     Z_DefaultDiagFormat *r;
304 #else
305     Z_DiagRec *r = p;
306 #endif
307
308     printf("Diagnostic message from database.\n");
309 #ifdef Z_95
310     if (p->which != Z_DiagRec_defaultFormat)
311     {
312         printf("Diagnostic record not in default format.\n");
313         return;
314     }
315     else
316         r = p->u.defaultFormat;
317 #endif
318     if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
319         ent->class != CLASS_DIAGSET || ent->value != VAL_BIB1)
320         printf("Missing or unknown diagset\n");
321     printf("Error condition: %d", *r->condition);
322     printf(" -- %s\n", r->addinfo ? r->addinfo : "");
323 }
324
325 static void display_nameplusrecord(Z_NamePlusRecord *p)
326 {
327     if (p->databaseName)
328         printf("[%s]", p->databaseName);
329     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
330         display_diagrec(p->u.surrogateDiagnostic);
331     else
332         display_record(p->u.databaseRecord);
333 }
334
335 static void display_records(Z_Records *p)
336 {
337     int i;
338
339     if (p->which == Z_Records_NSD)
340         display_diagrec(p->u.nonSurrogateDiagnostic);
341     else
342     {
343         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
344         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
345             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
346     }
347 }
348
349 static int send_searchRequest(char *arg)
350 {
351     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
352     Z_SearchRequest *req = apdu->u.searchRequest;
353     char *databaseNames = database;
354     Z_Query query;
355 #ifdef RPN_QUERY
356 #ifndef PREFIX_QUERY
357     struct ccl_rpn_node *rpn;
358     int error, pos;
359 #endif
360 #endif
361     char setstring[100];
362 #ifdef RPN_QUERY
363     Z_RPNQuery *RPNquery;
364     oident bib1;
365 #else
366     Odr_oct ccl_query;
367 #endif
368
369 #ifdef RPN_QUERY
370 #ifndef PREFIX_QUERY
371     rpn = ccl_find_str(bibset, arg, &error, &pos);
372     if (error)
373     {
374         printf("CCL ERROR: %s\n", ccl_err_msg(error));
375         return 0;
376     }
377 #endif
378 #endif
379
380     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
381     {
382         static unsigned char big[2100];
383         static Odr_oct bigo;
384
385         /* send a very big referenceid to test transport stack etc. */
386         memset(big, 'A', 2100);
387         bigo.len = bigo.size = 2100;
388         bigo.buf = big;
389         req->referenceId = &bigo;
390     }
391
392     if (setnumber >= 0)
393     {
394         sprintf(setstring, "%d", ++setnumber);
395         req->resultSetName = setstring;
396     }
397     *req->smallSetUpperBound = smallSetUpperBound;
398     *req->largeSetLowerBound = largeSetLowerBound;
399     *req->mediumSetPresentNumber = mediumSetPresentNumber;
400     req->num_databaseNames = 1;
401     req->databaseNames = &databaseNames;
402
403     req->query = &query;
404
405 #ifdef RPN_QUERY
406     query.which = Z_Query_type_1;
407
408 #ifndef PREFIX_QUERY
409     assert((RPNquery = ccl_rpn_query(rpn)));
410 #else
411     RPNquery = p_query_rpn (out, arg);
412     if (!RPNquery)
413     {
414         printf("Prefix query error\n");
415         return 0;
416     }
417 #endif
418     bib1.proto = protocol;
419     bib1.class = CLASS_ATTSET;
420     bib1.value = VAL_BIB1;
421     RPNquery->attributeSetId = oid_getoidbyent(&bib1);
422     query.u.type_1 = RPNquery;
423 #else
424     query.which = Z_Query_type_2;
425     query.u.type_2 = &ccl_query;
426     ccl_query.buf = (unsigned char*) arg;
427     ccl_query.len = strlen(arg);
428 #endif
429
430     send_apdu(apdu);
431     setno = 1;
432     printf("Sent searchRequest.\n");
433     return 2;
434 }
435
436 static int process_searchResponse(Z_SearchResponse *res)
437 {
438     if (*res->searchStatus)
439         printf("Search was a success.\n");
440     else
441         printf("Search was a bloomin' failure.\n");
442     printf("Number of hits: %d, setno %d\n",
443         *res->resultCount, setnumber);
444     printf("records returned: %d\n",
445         *res->numberOfRecordsReturned);
446     setno += *res->numberOfRecordsReturned;
447     if (res->records)
448         display_records(res->records);
449     return 0;
450 }
451
452 static int cmd_find(char *arg)
453 {
454     if (!*arg)
455     {
456         printf("Find what?\n");
457         return 0;
458     }
459     if (!conn)
460     {
461         printf("Not connected yet\n");
462         return 0;
463     }
464     if (!send_searchRequest(arg))
465         return 0;
466     return 2;
467 }
468
469 static int cmd_ssub(char *arg)
470 {
471     if (!(smallSetUpperBound = atoi(arg)))
472         return 0;
473     return 1;
474 }
475
476 static int cmd_lslb(char *arg)
477 {
478     if (!(largeSetLowerBound = atoi(arg)))
479         return 0;
480     return 1;
481 }
482
483 static int cmd_mspn(char *arg)
484 {
485     if (!(mediumSetPresentNumber = atoi(arg)))
486         return 0;
487     return 1;
488 }
489
490 static int cmd_status(char *arg)
491 {
492     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
493     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
494     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
495     return 1;
496 }
497
498 static int cmd_base(char *arg)
499 {
500     if (!*arg)
501     {
502         printf("Usage: base <database>\n");
503         return 0;
504     }
505     strcpy(database, arg);
506     return 1;
507 }
508
509 static int cmd_setnames(char *arg)
510 {
511     if (setnumber < 0)
512     {
513         printf("Set numbering enabled.\n");
514         setnumber = 0;
515     }
516     else
517     {
518         printf("Set numbering disabled.\n");
519         setnumber = -1;
520     }
521     return 1;
522 }
523
524 /* PRESENT SERVICE ----------------------------- */
525
526 static int send_presentRequest(char *arg)
527 {
528     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
529     Z_PresentRequest *req = apdu->u.presentRequest;
530     int nos = 1;
531     char *p;
532     char setstring[100];
533
534     if ((p = strchr(arg, '+')))
535     {
536         nos = atoi(p + 1);
537         *p = 0;
538     }
539     if (*arg)
540         setno = atoi(arg);
541
542     if (setnumber >= 0)
543     {
544         sprintf(setstring, "%d", setnumber);
545         req->resultSetId = setstring;
546     }
547     req->resultSetStartPoint = &setno;
548     req->numberOfRecordsRequested = &nos;
549     send_apdu(apdu);
550     printf("Sent presentRequest (%d+%d).\n", setno, nos);
551     return 2;
552 }
553
554 static int cmd_show(char *arg)
555 {
556     if (!send_presentRequest(arg))
557         return 0;
558     return 2;
559 }
560
561 int cmd_quit(char *arg)
562 {
563     printf("See you later, alligator.\n");
564     exit(0);
565 }
566
567 int cmd_cancel(char *arg)
568 {
569     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
570     Z_TriggerResourceControlRequest *req =
571         apdu->u.triggerResourceControlRequest;
572     bool_t false = 0;
573     
574     if (!session)
575     {
576         printf("Session not initialized yet\n");
577         return 0;
578     }
579     if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
580     {
581         printf("Target doesn't support cancel (trigger resource ctrl)\n");
582         return 0;
583     }
584     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
585     req->resultSetWanted = &false;
586
587     send_apdu(apdu);
588     printf("Sent cancel request\n");
589     return 2;
590 }
591
592 int send_scanrequest(char *string, int pp, int num)
593 {
594     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
595     Z_ScanRequest *req = apdu->u.scanRequest;
596     char *db = database;
597     oident attset;
598
599     req->num_databaseNames = 1;
600     req->databaseNames = &db;
601     attset.proto = protocol;
602     attset.class = CLASS_ATTSET;
603     attset.value = VAL_BIB1;
604     req->attributeSet = oid_getoidbyent(&attset);
605     req->termListAndStartPoint = p_query_scan(out, string);
606     req->numberOfTermsRequested = &num;
607     req->preferredPositionInResponse = &pp;
608     send_apdu(apdu);
609     return 2;
610 }
611
612 void display_term(Z_TermInfo *t)
613 {
614     if (t->term->which == Z_Term_general)
615     {
616         printf("%.*s (%d)\n", t->term->u.general->len, t->term->u.general->buf,
617             t->globalOccurrences ? *t->globalOccurrences : -1);
618         sprintf(last_scan, "%.*s", t->term->u.general->len,
619             t->term->u.general->buf);
620     }
621     else
622         printf("Term type not general.\n");
623 }
624
625 void process_scanResponse(Z_ScanResponse *res)
626 {
627     int i;
628
629     printf("SCAN: %d entries, position=%d\n", *res->numberOfEntriesReturned,
630         *res->positionOfTerm);
631     if (*res->scanStatus != Z_Scan_success)
632         printf("Scan returned code %d\n", *res->scanStatus);
633     if (!res->entries)
634         return;
635     if (res->entries->which == Z_ListEntries_entries)
636     {
637         Z_Entries *ent = res->entries->u.entries;
638
639         for (i = 0; i < ent->num_entries; i++)
640             if (ent->entries[i]->which == Z_Entry_termInfo)
641             {
642                 printf("%c ", i + 1 == *res->positionOfTerm ? '*' : ' ');
643                 display_term(ent->entries[i]->u.termInfo);
644             }
645             else
646                 display_diagrec(ent->entries[i]->u.surrogateDiagnostic);
647     }
648     else
649         display_diagrec(res->entries->u.nonSurrogateDiagnostics->diagRecs[0]);
650 }
651
652 int cmd_scan(char *arg)
653 {
654     if (!session)
655     {
656         printf("Session not initialized yet\n");
657         return 0;
658     }
659     if (!ODR_MASK_GET(session->options, Z_Options_scan))
660     {
661         printf("Target doesn't support scan\n");
662         return 0;
663     }
664     if (*arg)
665     {
666         if (send_scanrequest(arg, 5, 20) < 0)
667             return 0;
668     }
669     else
670         if (send_scanrequest(last_scan, 1, 20) < 0)
671             return 0;
672     return 2;
673 }
674
675 static void initialize(void)
676 {
677 #ifdef RPN_QUERY
678 #ifndef PREFIX_QUERY
679     FILE *inf;
680 #endif
681 #endif
682
683     if (!(out = odr_createmem(ODR_ENCODE)) ||
684         !(in = odr_createmem(ODR_DECODE)) ||
685         !(print = odr_createmem(ODR_PRINT)))
686     {
687         fprintf(stderr, "failed to allocate ODR streams\n");
688         exit(1);
689     }
690     setvbuf(stdout, 0, _IONBF, 0);
691
692 #ifdef RPN_QUERY
693 #ifndef PREFIX_QUERY
694     bibset = ccl_qual_mk (); 
695     inf = fopen ("default.bib", "r");
696     if (inf)
697     {
698         ccl_qual_file (bibset, inf);
699         fclose (inf);
700     }
701 #endif
702 #endif
703 }
704
705 static int client(void)
706 {
707     static struct {
708         char *cmd;
709         int (*fun)(char *arg);
710         char *ad;
711     } cmd[] = {
712         {"open", cmd_open, "('tcp'|'osi')':'[<TSEL>'/']<HOST>[':'<PORT>]"},
713         {"quit", cmd_quit, ""},
714         {"find", cmd_find, "<CCL-QUERY>"},
715         {"base", cmd_base, "<BASE-NAME>"},
716         {"show", cmd_show, "<REC#>['+'<#RECS>]"},
717         {"scan", cmd_scan, "<TERM>"},
718         {"authentication", cmd_authentication, "<ACCTSTRING>"},
719         {"lslb", cmd_lslb, "<largeSetLowerBound>"},
720         {"ssub", cmd_ssub, "<smallSetUpperBound>"},
721         {"mspn", cmd_mspn, "<mediumSetPresentNumber>"},
722         {"status", cmd_status, ""},
723         {"setnames", cmd_setnames, ""},
724         {"cancel", cmd_cancel, ""},
725         {0,0}
726     };
727     char *netbuffer= 0;
728     int netbufferlen = 0;
729     int i;
730     Z_APDU *apdu;
731
732     while (1)
733     {
734         int res;
735         fd_set input;
736         char line[1024], word[1024], arg[1024];
737
738         FD_ZERO(&input);
739         FD_SET(0, &input);
740         if (conn)
741             FD_SET(cs_fileno(conn), &input);
742         if ((res = select(20, &input, 0, 0, 0)) < 0)
743         {
744             perror("select");
745             exit(1);
746         }
747         if (!res)
748             continue;
749         if (FD_ISSET(0, &input))
750         {
751             /* quick & dirty way to get a command line. */
752             if (!gets(line))
753                 break;
754             if ((res = sscanf(line, "%s %[^;]", word, arg)) <= 0)
755             {
756                 strcpy(word, last_cmd);
757                 *arg = '\0';
758             }
759             else if (res == 1)
760                 *arg = 0;
761             strcpy(last_cmd, word);
762             for (i = 0; cmd[i].cmd; i++)
763                 if (!strncmp(cmd[i].cmd, word, strlen(word)))
764                 {
765                     res = (*cmd[i].fun)(arg);
766                     break;
767                 }
768             if (!cmd[i].cmd) /* dump our help-screen */
769             {
770                 printf("Unknown command: %s.\n", word);
771                 printf("Currently recognized commands:\n");
772                 for (i = 0; cmd[i].cmd; i++)
773                     printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
774                 res = 1;
775             }
776             if (res < 2)
777                 printf(C_PROMPT);
778         }
779         if (conn && FD_ISSET(cs_fileno(conn), &input))
780         {
781             do
782             {
783                 if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
784                 {
785                     perror("cs_get");
786                     exit(1);
787                 }
788                 if (!res)
789                 {
790                     printf("Target closed connection.\n");
791                     exit(1);
792                 }
793                 odr_reset(in); /* release APDU from last round */
794                 odr_setbuf(in, netbuffer, res, 0);
795                 if (!z_APDU(in, &apdu, 0))
796                 {
797                     odr_perror(in, "Decoding incoming APDU");
798                     exit(1);
799                 }
800 #if 0
801                 if (!z_APDU(print, &apdu, 0))
802                 {
803                     odr_perror(print, "Failed to print incoming APDU");
804                     odr_reset(print);
805                     continue;
806                 }
807 #endif
808                 switch(apdu->which)
809                 {
810                     case Z_APDU_initResponse:
811                         process_initResponse(apdu->u.initResponse);
812                         break;
813                     case Z_APDU_searchResponse:
814                         process_searchResponse(apdu->u.searchResponse);
815                         break;
816                     case Z_APDU_scanResponse:
817                         process_scanResponse(apdu->u.scanResponse);
818                         break;
819                     case Z_APDU_presentResponse:
820                         printf("Received presentResponse.\n");
821                         setno +=
822                             *apdu->u.presentResponse->numberOfRecordsReturned;
823                         if (apdu->u.presentResponse->records)
824                             display_records(apdu->u.presentResponse->records);
825                         else
826                             printf("No records.\n");
827                         break;
828                     default:
829                         printf("Received unknown APDU type (%d).\n", 
830                             apdu->which);
831                         exit(1);
832                 }
833                 printf("Z> ");
834                 fflush(stdout);
835             }
836             while (cs_more(conn));
837         }
838     }
839     return 0;
840 }
841
842 int main(int argc, char **argv)
843 {
844     initialize();
845     if (argc > 1)
846         cmd_open(argv[1]);
847     else
848         printf(C_PROMPT);
849     return client();
850 }