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