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